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 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 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 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}