Skip to main content

chroma_types/
flush.rs

1use super::{AttachedFunctionUuid, CollectionUuid, ConversionError, DatabaseName, Schema};
2use crate::{
3    chroma_proto::{self, FilePaths, FlushSegmentCompactionInfo},
4    SegmentUuid,
5};
6use chroma_error::{ChromaError, ErrorCodes};
7use std::{collections::HashMap, sync::Arc};
8use thiserror::Error;
9use uuid::Uuid;
10
11#[derive(Debug, Clone)]
12pub struct SegmentFlushInfo {
13    pub segment_id: SegmentUuid,
14    pub file_paths: HashMap<String, Vec<String>>,
15}
16
17#[derive(Debug, Clone)]
18pub struct CollectionFlushInfo {
19    pub tenant_id: String,
20    pub database_name: DatabaseName,
21    pub collection_id: CollectionUuid,
22    pub log_position: i64,
23    pub collection_version: i32,
24    pub segment_flush_info: Arc<[SegmentFlushInfo]>,
25    pub total_records_post_compaction: u64,
26    pub size_bytes_post_compaction: u64,
27    pub schema: Option<Schema>,
28}
29
30#[derive(Debug, Clone)]
31pub struct AttachedFunctionUpdateInfo {
32    pub attached_function_id: AttachedFunctionUuid,
33    pub completion_offset: u64,
34}
35
36#[derive(Error, Debug)]
37pub enum FinishAttachedFunctionError {
38    #[error("Failed to finish attached function: {0}")]
39    FailedToFinishAttachedFunction(#[from] tonic::Status),
40    #[error("Attached function not found")]
41    AttachedFunctionNotFound,
42}
43
44impl ChromaError for FinishAttachedFunctionError {
45    fn code(&self) -> ErrorCodes {
46        match self {
47            FinishAttachedFunctionError::FailedToFinishAttachedFunction(_) => ErrorCodes::Internal,
48            FinishAttachedFunctionError::AttachedFunctionNotFound => ErrorCodes::NotFound,
49        }
50    }
51}
52
53#[derive(Error, Debug)]
54pub enum FinishCreateAttachedFunctionError {
55    #[error("Failed to finish creating attached function: {0}")]
56    FailedToFinishCreateAttachedFunction(#[from] tonic::Status),
57    #[error("Attached function not found")]
58    AttachedFunctionNotFound,
59}
60
61impl ChromaError for FinishCreateAttachedFunctionError {
62    fn code(&self) -> ErrorCodes {
63        match self {
64            FinishCreateAttachedFunctionError::FailedToFinishCreateAttachedFunction(_) => {
65                ErrorCodes::Internal
66            }
67            FinishCreateAttachedFunctionError::AttachedFunctionNotFound => ErrorCodes::NotFound,
68        }
69    }
70}
71
72#[derive(Error, Debug)]
73pub enum GetMinCompletionOffsetError {
74    #[error("Failed to get min completion offset: {0}")]
75    FailedToGetMinCompletionOffset(#[from] tonic::Status),
76}
77
78impl ChromaError for GetMinCompletionOffsetError {
79    fn code(&self) -> ErrorCodes {
80        ErrorCodes::Internal
81    }
82}
83
84#[derive(Error, Debug)]
85pub enum AdvanceAttachedFunctionError {
86    #[error("Failed to advance attached function: {0}")]
87    FailedToAdvanceAttachedFunction(#[from] tonic::Status),
88    #[error("Attached function not found - nonce mismatch or attached function doesn't exist")]
89    AttachedFunctionNotFound,
90}
91
92impl ChromaError for AdvanceAttachedFunctionError {
93    fn code(&self) -> ErrorCodes {
94        match self {
95            AdvanceAttachedFunctionError::FailedToAdvanceAttachedFunction(_) => {
96                ErrorCodes::Internal
97            }
98            AdvanceAttachedFunctionError::AttachedFunctionNotFound => ErrorCodes::NotFound,
99        }
100    }
101}
102
103#[derive(Debug, Clone)]
104pub struct AdvanceAttachedFunctionResponse {
105    pub completion_offset: u64,
106}
107
108impl TryInto<FlushSegmentCompactionInfo> for &SegmentFlushInfo {
109    type Error = SegmentFlushInfoConversionError;
110
111    fn try_into(self) -> Result<FlushSegmentCompactionInfo, Self::Error> {
112        let mut file_paths = HashMap::new();
113        for (key, value) in self.file_paths.clone() {
114            file_paths.insert(key, FilePaths { paths: value });
115        }
116
117        Ok(FlushSegmentCompactionInfo {
118            segment_id: self.segment_id.to_string(),
119            file_paths,
120        })
121    }
122}
123
124#[derive(Error, Debug)]
125pub enum SegmentFlushInfoConversionError {
126    #[error("Invalid segment id, valid UUID required")]
127    InvalidSegmentId,
128    #[error(transparent)]
129    DecodeError(#[from] ConversionError),
130}
131
132#[derive(Error, Debug)]
133pub enum CollectionFlushInfoConversionError {
134    #[error("Failed to convert segment flush info: {0}")]
135    SegmentConversionError(#[from] SegmentFlushInfoConversionError),
136    #[error("Failed to serialize schema")]
137    SchemaSerializationError,
138}
139
140impl TryFrom<CollectionFlushInfo> for chroma_proto::FlushCollectionCompactionRequest {
141    type Error = CollectionFlushInfoConversionError;
142
143    fn try_from(collection: CollectionFlushInfo) -> Result<Self, Self::Error> {
144        let segment_compaction_info = collection
145            .segment_flush_info
146            .iter()
147            .map(|segment_flush_info| segment_flush_info.try_into())
148            .collect::<Result<Vec<_>, _>>()?;
149
150        let schema_str = collection
151            .schema
152            .map(|s| {
153                serde_json::to_string(&s)
154                    .map_err(|_| CollectionFlushInfoConversionError::SchemaSerializationError)
155            })
156            .transpose()?;
157
158        Ok(crate::chroma_proto::FlushCollectionCompactionRequest {
159            tenant_id: collection.tenant_id,
160            collection_id: collection.collection_id.0.to_string(),
161            log_position: collection.log_position,
162            collection_version: collection.collection_version,
163            segment_compaction_info,
164            total_records_post_compaction: collection.total_records_post_compaction,
165            size_bytes_post_compaction: collection.size_bytes_post_compaction,
166            schema_str,
167            database_name: Some(collection.database_name.as_ref().to_string()),
168        })
169    }
170}
171
172#[derive(Debug)]
173pub struct FlushCompactionResponse {
174    pub collection_id: CollectionUuid,
175    pub collection_version: i32,
176    pub last_compaction_time: i64,
177}
178
179#[derive(Debug)]
180pub struct FlushCompactionAndAttachedFunctionResponse {
181    pub collections: Vec<FlushCompactionResponse>,
182    // Completion offset updated during register
183    pub completion_offset: u64,
184}
185
186impl FlushCompactionResponse {
187    pub fn new(
188        collection_id: CollectionUuid,
189        collection_version: i32,
190        last_compaction_time: i64,
191    ) -> Self {
192        FlushCompactionResponse {
193            collection_id,
194            collection_version,
195            last_compaction_time,
196        }
197    }
198}
199
200impl TryFrom<chroma_proto::FlushCollectionCompactionAndAttachedFunctionResponse>
201    for FlushCompactionAndAttachedFunctionResponse
202{
203    type Error = FlushCompactionResponseConversionError;
204
205    fn try_from(
206        value: chroma_proto::FlushCollectionCompactionAndAttachedFunctionResponse,
207    ) -> Result<Self, Self::Error> {
208        // Parse all collections from the repeated field
209        let mut collections = Vec::with_capacity(value.collections.len());
210        for collection in value.collections {
211            let id = Uuid::parse_str(&collection.collection_id)
212                .map_err(|_| FlushCompactionResponseConversionError::InvalidUuid)?;
213            collections.push(FlushCompactionResponse {
214                collection_id: CollectionUuid(id),
215                collection_version: collection.collection_version,
216                last_compaction_time: collection.last_compaction_time,
217            });
218        }
219
220        // Extract completion_offset from attached_function_state
221        // Note: next_nonce and next_run are no longer used by the client
222        // They were already set by PrepareAttachedFunction via advance_attached_function()
223        let completion_offset = value
224            .attached_function_state
225            .as_ref()
226            .map(|state| state.completion_offset)
227            .unwrap_or(0);
228
229        Ok(FlushCompactionAndAttachedFunctionResponse {
230            collections,
231            completion_offset,
232        })
233    }
234}
235
236#[derive(Error, Debug)]
237pub enum FlushCompactionResponseConversionError {
238    #[error(transparent)]
239    DecodeError(#[from] ConversionError),
240    #[error("Invalid collection id, valid UUID required")]
241    InvalidUuid,
242    #[error("Invalid attached function nonce, valid UUID required")]
243    InvalidAttachedFunctionNonce,
244    #[error("Invalid timestamp format")]
245    InvalidTimestamp,
246    #[error("Missing collections in response")]
247    MissingCollections,
248}
249
250impl ChromaError for FlushCompactionResponseConversionError {
251    fn code(&self) -> ErrorCodes {
252        match self {
253            FlushCompactionResponseConversionError::InvalidUuid => ErrorCodes::InvalidArgument,
254            FlushCompactionResponseConversionError::InvalidAttachedFunctionNonce => {
255                ErrorCodes::InvalidArgument
256            }
257            FlushCompactionResponseConversionError::InvalidTimestamp => ErrorCodes::InvalidArgument,
258            FlushCompactionResponseConversionError::MissingCollections => {
259                ErrorCodes::InvalidArgument
260            }
261            FlushCompactionResponseConversionError::DecodeError(e) => e.code(),
262        }
263    }
264}