From 233a63d30e86c62bb5bec150e6fa86f03561a978 Mon Sep 17 00:00:00 2001 From: hammadb Date: Fri, 26 Apr 2024 16:37:48 -0700 Subject: [PATCH] cleanup --- .pre-commit-config.yaml | 2 +- chromadb/db/impl/grpc/client.py | 3 - k8s/distributed-chroma/values.yaml | 2 +- rust/worker/chroma_config.yaml | 2 +- rust/worker/src/blockstore/mod.rs | 1 - rust/worker/src/blockstore/prototyping.rs | 368 ------------------ .../src/compactor/compaction_manager.rs | 2 - rust/worker/src/compactor/scheduler.rs | 2 +- rust/worker/src/compactor/scheduler_policy.rs | 2 - rust/worker/test.arrow | Bin 67422 -> 0 bytes 10 files changed, 4 insertions(+), 380 deletions(-) delete mode 100644 rust/worker/src/blockstore/prototyping.rs delete mode 100644 rust/worker/test.arrow diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index c872489b2fe..e2bf17ad68a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,4 @@ -exclude: 'chromadb/proto/(chroma_pb2|coordinator_pb2|logservice_pb2)\.(py|pyi|_grpc\.py)' # Generated files +exclude: 'chromadb/proto/(chroma_pb2|coordinator_pb2|logservice_pb2|chroma_pb2_grpc|coordinator_pb2_grpc|logservice_pb2_grpc)\.(py|pyi)' # Generated files repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.5.0 diff --git a/chromadb/db/impl/grpc/client.py b/chromadb/db/impl/grpc/client.py index 5bf98d7c619..88693921346 100644 --- a/chromadb/db/impl/grpc/client.py +++ b/chromadb/db/impl/grpc/client.py @@ -153,10 +153,7 @@ def get_segments( collection=collection.hex if collection else None, ) response = self._sys_db_stub.GetSegments(request) - print("GRPC RESPONSE: ", response) - print("SEGMENTS LENGTH: ", len(response.segments)) results: List[Segment] = [] - print("GRPC SEGMENTS: ", response.segments) for proto_segment in response.segments: segment = from_proto_segment(proto_segment) results.append(segment) diff --git a/k8s/distributed-chroma/values.yaml b/k8s/distributed-chroma/values.yaml index 8c4002c610a..c5f1f0d2023 100644 --- a/k8s/distributed-chroma/values.yaml +++ b/k8s/distributed-chroma/values.yaml @@ -24,7 +24,7 @@ frontendService: authCredentialsProvider: 'value: ""' authzProvider: 'value: ""' authzConfigProvider: 'value: ""' - memberlistProviderImpl: 'value: "chromadb.segment.impl.distributed.segment_directory.CustomResourceMemberlistProvider"' + memberlistProviderImpl: 'value: "chromadb.segment.impl.distributed.segment_directory.MockMemberlistProvider"' logServiceHost: 'value: "logservice.chroma"' logServicePort: 'value: "50051"' otherEnvConfig: '' diff --git a/rust/worker/chroma_config.yaml b/rust/worker/chroma_config.yaml index 91c5291d640..95eb1924932 100644 --- a/rust/worker/chroma_config.yaml +++ b/rust/worker/chroma_config.yaml @@ -61,4 +61,4 @@ compaction_service: compactor: compaction_manager_queue_size: 1000 max_concurrent_jobs: 100 - compaction_interval_sec: 5 + compaction_interval_sec: 60 diff --git a/rust/worker/src/blockstore/mod.rs b/rust/worker/src/blockstore/mod.rs index 4439ed08148..6a9dacd4565 100644 --- a/rust/worker/src/blockstore/mod.rs +++ b/rust/worker/src/blockstore/mod.rs @@ -2,7 +2,6 @@ mod arrow; mod key; mod memory; mod positional_posting_list_value; -mod prototyping; mod types; pub(crate) mod provider; diff --git a/rust/worker/src/blockstore/prototyping.rs b/rust/worker/src/blockstore/prototyping.rs deleted file mode 100644 index 89521c4d021..00000000000 --- a/rust/worker/src/blockstore/prototyping.rs +++ /dev/null @@ -1,368 +0,0 @@ -use parking_lot::RwLock; -use std::collections::HashMap; -use std::sync::Arc; - -// ===== Value Types ===== -// Example of a value type that references other data. (This would be data in the block) -struct NestedReferences<'value> { - id: &'value [i32], - value: &'value [i32], -} - -trait Value { - type Writeable<'writeable>: WriteableValue; - type Readable<'referred_data>: ReadableValue<'referred_data>; -} - -// Used for dynamic dispatch to get the type of the value. -// referred_data is the lifetime of the data that the value references (if any). -trait ReadableValue<'referred_data> { - fn read_from_block(block: &'referred_data Block) -> Self; -} - -trait WriteableValue { - fn write_to_block(key: &str, value: &Self, block: &BlockBuilder); -} - -// ===== Value Implementations ===== -impl<'referred_data> ReadableValue<'referred_data> for NestedReferences<'referred_data> { - fn read_from_block(block: &'referred_data Block) -> Self { - let id = block.id_storage.get("key").unwrap(); - let value = block.value_storage.get("key").unwrap(); - NestedReferences { id, value } - } -} - -impl<'referred_data> ReadableValue<'referred_data> for &'referred_data String { - fn read_from_block(block: &'referred_data Block) -> Self { - block.string_storage.get("key").unwrap() - } -} - -impl WriteableValue for NestedReferences<'_> { - fn write_to_block(key: &str, value: &Self, block: &BlockBuilder) { - block - .id_storage - .write() - .as_mut() - .unwrap() - .insert(key.to_string(), value.id.to_vec()); - block - .value_storage - .write() - .as_mut() - .unwrap() - .insert(key.to_string(), value.value.to_vec()); - } -} - -impl WriteableValue for String { - fn write_to_block(key: &str, value: &Self, block: &BlockBuilder) { - block - .string_storage - .write() - .as_mut() - .unwrap() - .insert(key.to_string(), value.clone()); - } -} - -// ===== Block Provider & Cache ===== -// Thread-safe block cache and manager. -// This just loads blocks from the cache, but you could imagine it faults to s3 -#[derive(Clone)] -struct BlockManager { - read_block_cache: Arc>>, - write_block_builder_cache: Arc>>, -} - -impl BlockManager { - fn new() -> Self { - Self { - read_block_cache: Arc::new(RwLock::new(HashMap::new())), - write_block_builder_cache: Arc::new(RwLock::new(HashMap::new())), - } - } - - fn get(&self, id: &uuid::Uuid) -> Option { - let cache_guard = self.read_block_cache.read(); - let block = cache_guard.get(id)?.clone(); - Some(block) - } - - fn create(&mut self) -> BlockBuilder { - let builder = BlockBuilder::new(uuid::Uuid::new_v4()); - self.write_block_builder_cache - .write() - .insert(builder.id, builder.clone()); - builder - } - - fn build(&mut self, id: uuid::Uuid) -> Block { - let builder = self.write_block_builder_cache.write().remove(&id).unwrap(); - let block = builder.build(); - self.read_block_cache - .write() - .insert(block.id, block.clone()); - block - } -} - -// ===== Sparse Index ===== -#[derive(Clone)] -struct SparseIndex { - storage: Vec, -} - -impl SparseIndex { - fn new() -> Self { - Self { - storage: Vec::new(), - } - } -} - -// ===== Reader & Writer ===== -// Reader is a non-thread-safe reader that reads from the block store. -// Each thread can create its own reader, the blockmanager will handle the thread-safety -// of the block cache. -struct Reader { - sparse_index: SparseIndex, - manager: BlockManager, - // Used to keep a reference to the block, since we need to keep the block alive while we return values that reference it. - loaded_blocks: HashMap, - marker: std::marker::PhantomData, -} - -trait ReaderTrait<'me, V> { - fn get(&'me mut self, key: &str) -> Option; -} - -impl<'me, V: ReadableValue<'me>> ReaderTrait<'me, V> for Reader { - fn get(&'me mut self, key: &str) -> Option { - self.get(key) - } -} - -enum ReaderEnum { - Reader(Reader), -} - -impl<'me, V: ReadableValue<'me>> ReaderEnum { - fn get(&'me mut self, key: &str) -> Option { - match self { - ReaderEnum::Reader(reader) => reader.get(key), - } - } -} - -impl<'me, V> Reader -where - V: ReadableValue<'me>, -{ - fn from_sparse_index(sparse_index: SparseIndex, manager: BlockManager) -> Self { - Self { - sparse_index: sparse_index, - manager, - loaded_blocks: HashMap::new(), - marker: std::marker::PhantomData, - } - } - - fn get(&'me mut self, key: &str) -> Option { - // Iterate over the blocks in reverse order to get the most recent value. - // Since this implementation does not delete values from old blocks, the first value found is the most recent. - for block_id in self.sparse_index.storage.iter().rev() { - if !self.loaded_blocks.contains_key(block_id) { - let block = self.manager.get(block_id)?; - self.loaded_blocks.insert(*block_id, block); - } - } - // This double for loop is to make the borrow-checker happy by scoping the mutable borrow of self. - // It's not pretty, but it works. - for block_id in self.sparse_index.storage.iter().rev() { - let block = self.loaded_blocks.get(block_id).unwrap(); - let value = V::read_from_block(block); - return Some(value); - } - None - } -} - -struct Writer { - sparse_index: SparseIndex, - active_block: Option, - block_manager: BlockManager, -} - -impl Writer { - fn new(mut block_manager: BlockManager) -> Self { - let sparse_index = SparseIndex::new(); - let new_block = block_manager.create(); - Self { - sparse_index: sparse_index, - active_block: Some(new_block), - block_manager: block_manager, - } - } - - fn from_sparse_index(sparse_index: SparseIndex, mut block_manager: BlockManager) -> Self { - let new_block = block_manager.create(); - Self { - sparse_index: sparse_index, - active_block: Some(new_block), - block_manager: block_manager, - } - } - - fn store(&self, key: String, value: &V) { - V::write_to_block(&key, value, self.active_block.as_ref().unwrap()); - } - - fn commit(mut self) -> SparseIndex { - // Concretize the active block - let read_only_block = self - .block_manager - .build(self.active_block.as_ref().unwrap().id); - self.sparse_index.storage.push(read_only_block.id); - self.sparse_index - - // TODO: remove the write block when the writer is dropped. This will prevent leaking builders. - } -} - -struct HoldsWriter { - writer: Writer, -} -// ===== Block Implementations ===== -#[derive(Clone)] -struct Block { - // This pretends to be a RecordBatch in Arrow. - // Storage for NestedReferences - id_storage: Arc>>, - value_storage: Arc>>, - // Storage for String - string_storage: Arc>, - // Storage for the block id. - id: uuid::Uuid, -} - -// Mock BlockBuilder - it pretends like its arrow by allowing it to store ANY value type. -#[derive(Clone)] -struct BlockBuilder { - // Storage for NestedReferences - id_storage: Arc>>>>, - value_storage: Arc>>>>, - // Storage for String - string_storage: Arc>>>, - // Storage for the block id. - id: uuid::Uuid, -} - -impl BlockBuilder { - fn new(id: uuid::Uuid) -> Self { - Self { - id_storage: Arc::new(RwLock::new(Some(HashMap::new()))), - value_storage: Arc::new(RwLock::new(Some(HashMap::new()))), - string_storage: Arc::new(RwLock::new(Some(HashMap::new()))), - id, - } - } - - fn build(self) -> Block { - Block { - id_storage: self.id_storage.write().take().unwrap().into(), - value_storage: self.value_storage.write().take().unwrap().into(), - string_storage: self.string_storage.write().take().unwrap().into(), - id: self.id, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_self_reference_map() { - let block_manager = BlockManager::new(); - let writer = Writer::new(block_manager.clone()); - let nested_references = NestedReferences { - id: &[1, 2, 3], - value: &[4, 5, 6], - }; - writer.store("key".to_string(), &nested_references); - let writer_sparse_index = writer.commit(); - - let mut reader = - Reader::::from_sparse_index(writer_sparse_index, block_manager); - - let value = reader.get("key").unwrap(); - assert_eq!(value.id, &[1, 2, 3]); - assert_eq!(value.value, &[4, 5, 6]); - } - - #[test] - fn test_string_map() { - let block_manager = BlockManager::new(); - let writer = Writer::new(block_manager.clone()); - let string = "value".to_string(); - writer.store("key".to_string(), &string); - let writer_sparse_index = writer.commit(); - - let mut reader = Reader::<&String>::from_sparse_index(writer_sparse_index, block_manager); - - let value = reader.get("key").unwrap(); - assert_eq!(value, "value"); - } - - #[test] - fn test_non_static_self_reference_map() { - let mut id_data_src = Vec::new(); - let mut value_data_src = Vec::new(); - - for i in 0..10 { - id_data_src.push(i); - value_data_src.push(i + 10); - } - let id_data_src = id_data_src.as_slice(); - let value_data_src = value_data_src.as_slice(); - - fn combine<'value>(id: &'value [i32], value: &'value [i32]) -> NestedReferences<'value> { - NestedReferences { id, value } - } - let combined = combine(id_data_src, value_data_src); - - let block_manager = BlockManager::new(); - let writer = Writer::new(block_manager.clone()); - writer.store("key".to_string(), &combined); - let writer_sparse_index = writer.commit(); - - let mut reader = - Reader::::from_sparse_index(writer_sparse_index, block_manager); - - let value = reader.get("key").unwrap(); - assert_eq!(value.id, id_data_src); - assert_eq!(value.value, value_data_src); - } - - #[test] - fn test_reader_trait() { - let block_manager = BlockManager::new(); - let writer = Writer::new(block_manager.clone()); - let string = "value".to_string(); - writer.store("key".to_string(), &string); - let writer_sparse_index = writer.commit(); - - // let mut reader = Reader::<&String>::from_sparse_index(writer_sparse_index, block_manager); - // let mut reader: Box> = Box::new(reader); - let mut reader = ReaderEnum::Reader(Reader::<&String>::from_sparse_index( - writer_sparse_index, - block_manager, - )); - - // RESUME: This is where the issue is. The compiler can't figure out the lifetime of the reader. - let value = reader.get("key").unwrap(); - assert_eq!(value, "value"); - } -} diff --git a/rust/worker/src/compactor/compaction_manager.rs b/rust/worker/src/compactor/compaction_manager.rs index 63681c162b9..cd2b842c7ab 100644 --- a/rust/worker/src/compactor/compaction_manager.rs +++ b/rust/worker/src/compactor/compaction_manager.rs @@ -305,7 +305,6 @@ mod tests { #[tokio::test] async fn test_compaction_manager() { - println!("Running test_compaction_manager"); let mut log = Box::new(InMemoryLog::new()); let tmpdir = tempfile::tempdir().unwrap(); let storage = Box::new(Storage::Local(LocalStorage::new( @@ -463,7 +462,6 @@ mod tests { let dispatcher_handle = system.start_component(dispatcher); manager.set_dispatcher(dispatcher_handle.receiver()); manager.set_system(system); - println!("Starting compaction manager"); let (num_completed, number_failed) = manager.compact_batch().await; assert_eq!(num_completed, 2); assert_eq!(number_failed, 0); diff --git a/rust/worker/src/compactor/scheduler.rs b/rust/worker/src/compactor/scheduler.rs index ed0a7350a86..6fa1bd95308 100644 --- a/rust/worker/src/compactor/scheduler.rs +++ b/rust/worker/src/compactor/scheduler.rs @@ -147,7 +147,7 @@ impl Scheduler { } pub(crate) async fn schedule(&mut self) { - // TODO: I ADDED THIS CLEAR, ASK LIQUAN IF IT'S OK + // For now, we clear the job queue every time, assuming we will not have any pending jobs running self.job_queue.clear(); if self.memberlist.is_none() || self.memberlist.as_ref().unwrap().is_empty() { // TODO: Log error diff --git a/rust/worker/src/compactor/scheduler_policy.rs b/rust/worker/src/compactor/scheduler_policy.rs index 09b5e157a6d..8553a39cea2 100644 --- a/rust/worker/src/compactor/scheduler_policy.rs +++ b/rust/worker/src/compactor/scheduler_policy.rs @@ -34,7 +34,6 @@ impl SchedulerPolicy for LasCompactionTimeSchedulerPolicy { collections: Vec, number_jobs: i32, ) -> Vec { - println!("POLICY SEES N COLLECTIONS: {:?}", collections.len()); let mut collections = collections; collections.sort_by(|a, b| a.last_compaction_time.cmp(&b.last_compaction_time)); let number_tasks = if number_jobs > collections.len() as i32 { @@ -42,7 +41,6 @@ impl SchedulerPolicy for LasCompactionTimeSchedulerPolicy { } else { number_jobs }; - println!("POLICY CREATING N TASKS: {:?}", number_tasks); let mut tasks = Vec::new(); for collection in &collections[0..number_tasks as usize] { tasks.push(CompactionJob { diff --git a/rust/worker/test.arrow b/rust/worker/test.arrow deleted file mode 100644 index 3f01356b51521af8cb6b6e09c27b0d392c7e59d2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 67422 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 X_3wkn_xFA%_qDGdKYsk_=O4ZS8d%$r