1use std::borrow::Cow;
5
6use reifydb_codec::key::{deserializer::KeyDeserializer, encoded::EncodedKey, serializer::KeySerializer};
7use smallvec::{SmallVec, smallvec};
8
9use super::KeyTag;
10use crate::{
11 interface::{catalog::flow::FlowId, cdc::CdcConsumerId},
12 key::{
13 any::{ByteEncoding, Field, KeyFields},
14 bound::TaggedKeyBoundRange,
15 },
16};
17
18pub trait ToConsumerKey {
19 fn to_consumer_key(&self) -> CdcConsumerKey;
20}
21
22impl ToConsumerKey for CdcConsumerKey {
23 fn to_consumer_key(&self) -> CdcConsumerKey {
24 self.clone()
25 }
26}
27
28impl ToConsumerKey for CdcConsumerId {
29 fn to_consumer_key(&self) -> CdcConsumerKey {
30 CdcConsumerKey {
31 consumer: self.clone(),
32 }
33 }
34}
35
36impl ToConsumerKey for FlowId {
37 fn to_consumer_key(&self) -> CdcConsumerKey {
38 CdcConsumerKey::new(CdcConsumerId::new(format!("flow:{}", self.0)))
39 }
40}
41
42#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
43pub struct CdcConsumerKey {
44 pub consumer: CdcConsumerId,
45}
46
47impl CdcConsumerKey {
48 pub fn new(consumer: impl Into<CdcConsumerId>) -> Self {
49 Self {
50 consumer: consumer.into(),
51 }
52 }
53
54 pub fn encoded(consumer: impl Into<CdcConsumerId>) -> EncodedKey {
55 Self {
56 consumer: consumer.into(),
57 }
58 .encode()
59 }
60
61 pub fn full_scan() -> TaggedKeyBoundRange {
62 TaggedKeyBoundRange::kind(Self::TAG)
63 }
64}
65
66impl CdcConsumerKey {
67 pub const TAG: KeyTag = KeyTag::CdcConsumer;
68
69 pub fn encode(&self) -> EncodedKey {
70 let mut serializer = KeySerializer::new();
71 serializer.extend_u8(Self::TAG as u8).extend_str(&self.consumer);
72 serializer.to_encoded_key()
73 }
74
75 pub fn decode(key: &EncodedKey) -> Option<Self>
76 where
77 Self: Sized,
78 {
79 let mut de = KeyDeserializer::from_bytes(key.as_slice());
80
81 let kind: KeyTag = de.read_u8().ok()?.try_into().ok()?;
82 if kind != Self::TAG {
83 return None;
84 }
85
86 let consumer_id = de.read_str().ok()?;
87
88 Some(Self {
89 consumer: CdcConsumerId(consumer_id),
90 })
91 }
92}
93
94#[cfg(test)]
95pub mod cdc_consumer_key_tests {
96 use std::ops::RangeBounds;
97
98 use super::{CdcConsumerKey, ToConsumerKey};
99 use crate::interface::{catalog::flow::FlowId, cdc::CdcConsumerId};
100
101 #[test]
102 fn test_encode_decode_cdc_consumer() {
103 let key = CdcConsumerKey {
104 consumer: CdcConsumerId::new("test-consumer"),
105 };
106
107 let encoded = key.encode();
108 let decoded = CdcConsumerKey::decode(&encoded).expect("Failed to decode key");
109
110 assert_eq!(decoded.consumer, CdcConsumerId::new("test-consumer"));
111 }
112
113 #[test]
114 fn test_cdc_consumer_keys_within_range() {
115 let key1 = CdcConsumerKey {
116 consumer: CdcConsumerId::new("consumer-a"),
117 }
118 .encode();
119
120 let key2 = CdcConsumerKey {
121 consumer: CdcConsumerId::new("consumer-b"),
122 }
123 .encode();
124
125 let key3 = CdcConsumerKey {
126 consumer: CdcConsumerId::new("consumer-z"),
127 }
128 .encode();
129
130 let range = CdcConsumerKey::full_scan().encode();
131
132 assert!(range.contains(&key1), "consumer-a key should be in range");
133 assert!(range.contains(&key2), "consumer-b key should be in range");
134 assert!(range.contains(&key3), "consumer-z key should be in range");
135 }
136
137 #[test]
138 fn test_flow_id_to_consumer_key() {
139 let flow_id = FlowId(42);
140 let encoded = flow_id.to_consumer_key().encode();
141
142 let decoded = CdcConsumerKey::decode(&encoded).expect("Failed to decode key");
143 assert_eq!(decoded.consumer, CdcConsumerId::new("flow:42"));
144 }
145
146 #[test]
147 fn test_flow_id_keys_within_range() {
148 let flow1 = FlowId(1).to_consumer_key().encode();
149 let flow2 = FlowId(100).to_consumer_key().encode();
150 let flow3 = FlowId(999).to_consumer_key().encode();
151
152 let range = CdcConsumerKey::full_scan().encode();
153
154 assert!(range.contains(&flow1), "flow:1 key should be in range");
155 assert!(range.contains(&flow2), "flow:100 key should be in range");
156 assert!(range.contains(&flow3), "flow:999 key should be in range");
157 }
158}
159
160pub fn should_exclude_from_cdc(kind: KeyTag) -> bool {
161 matches!(
162 kind,
163 KeyTag::OperatorState
164 | KeyTag::CdcConsumer | KeyTag::Metric
165 | KeyTag::SystemSequence
166 | KeyTag::RowSequence | KeyTag::ColumnSequence
167 | KeyTag::SystemVersion
168 | KeyTag::TransactionVersion
169 | KeyTag::FlowVersion | KeyTag::RingBufferMetadata
170 | KeyTag::Index | KeyTag::ConfigStorage
171 | KeyTag::Token | KeyTag::VersionEpoch
172 | KeyTag::QueuePartition
173 | KeyTag::QueueItemState
174 | KeyTag::QueueDue | KeyTag::QueueKeyActive
175 )
176}
177
178#[cfg(test)]
179pub mod primary_key_tests {
180 use super::*;
181
182 #[test]
183 fn test_all_key_kinds_have_explicit_cdc_decision() {
184 let test_variant = KeyTag::Row;
188
189 match test_variant {
190 KeyTag::Namespace => {}
191 KeyTag::Table => {}
192 KeyTag::Row => {}
193 KeyTag::NamespaceTable => {}
194 KeyTag::SystemSequence => {}
195 KeyTag::Columns => {}
196 KeyTag::Column => {}
197 KeyTag::RowSequence => {}
198 KeyTag::ColumnProperty => {}
199 KeyTag::SystemVersion => {}
200 KeyTag::TransactionVersion => {}
201 KeyTag::Index => {}
202 KeyTag::IndexEntry => {}
203 KeyTag::ColumnSequence => {}
204 KeyTag::CdcConsumer => {}
205 KeyTag::View => {}
206 KeyTag::NamespaceView => {}
207 KeyTag::PrimaryKey => {}
208 KeyTag::OperatorState => {}
209 KeyTag::RingBuffer => {}
210 KeyTag::NamespaceRingBuffer => {}
211 KeyTag::RingBufferMetadata => {}
212 KeyTag::Flow => {}
213 KeyTag::NamespaceFlow => {}
214 KeyTag::Operator => {}
215 KeyTag::OperatorByFlow => {}
216 KeyTag::FlowEdge => {}
217 KeyTag::FlowEdgeByFlow => {}
218 KeyTag::OutputFrontier => {}
219 KeyTag::Dictionary => {}
220 KeyTag::DictionaryEntry => {}
221 KeyTag::DictionaryEntryIndex => {}
222 KeyTag::NamespaceDictionary => {}
223 KeyTag::Metric => {}
224 KeyTag::FlowVersion => {}
225 KeyTag::RowShape => {}
226 KeyTag::SumType => {}
227 KeyTag::NamespaceSumType => {}
228 KeyTag::RowShapeField => {}
229 KeyTag::Handler => {}
230 KeyTag::NamespaceHandler => {}
231 KeyTag::VariantHandler => {}
232 KeyTag::Series => {}
233 KeyTag::NamespaceSeries => {}
234 KeyTag::SeriesMetadata => {}
235 KeyTag::Identity => {}
236 KeyTag::Role => {}
237 KeyTag::GrantedRole => {}
238 KeyTag::Policy => {}
239 KeyTag::PolicyOp => {}
240 KeyTag::Migration => {}
241 KeyTag::Authentication => {}
242 KeyTag::MigrationEvent => {}
243 KeyTag::ConfigStorage => {}
244 KeyTag::Token => {}
245 KeyTag::Source => {}
246 KeyTag::NamespaceSource => {}
247 KeyTag::Sink => {}
248 KeyTag::NamespaceSink => {}
249 KeyTag::RowSettings => {}
250 KeyTag::Procedure => {}
251 KeyTag::NamespaceProcedure => {}
252 KeyTag::ProcedureParam => {}
253 KeyTag::Binding => {}
254 KeyTag::OperatorSettings => {}
255 KeyTag::NamespaceBinding => {}
256 KeyTag::ColumnSnapshot => {}
257 KeyTag::SeriesColumnSnapshot => {}
258 KeyTag::TableColumnSnapshot => {}
259 KeyTag::IdentityAttribute => {}
260 KeyTag::IdentityAttributeValue => {}
261 KeyTag::PartitionedRow => {}
262 KeyTag::PartitionedSeriesRow => {}
263 KeyTag::Partition => {}
264 KeyTag::Queue => {}
265 KeyTag::NamespaceQueue => {}
266 KeyTag::QueueDeduplication => {}
267 KeyTag::QueuePartition => {}
268 KeyTag::QueueItemState => {}
269 KeyTag::QueueDue => {}
270 KeyTag::QueueAttempt => {}
271 KeyTag::QueueKeyActive => {}
272 KeyTag::VersionEpoch => {}
273 KeyTag::SeriesRow => {}
274 KeyTag::SortedViewRow => {}
275 KeyTag::PartitionedSortedViewRow => {}
276 KeyTag::Relationship => {} }
281 }
282
283 #[test]
284 fn test_sorted_view_rows_reach_the_cdc_log() {
285 assert!(!should_exclude_from_cdc(KeyTag::SortedViewRow));
288 assert!(!should_exclude_from_cdc(KeyTag::PartitionedSortedViewRow));
289 }
290
291 #[test]
292 fn test_exclude_operator_state() {
293 assert!(should_exclude_from_cdc(KeyTag::OperatorState));
294 }
295
296 #[test]
297 fn test_exclude_cdc_consumer() {
298 assert!(should_exclude_from_cdc(KeyTag::CdcConsumer));
299 }
300
301 #[test]
302 fn test_exclude_storage_tracker() {
303 assert!(should_exclude_from_cdc(KeyTag::Metric));
304 }
305
306 #[test]
307 fn test_exclude_system_sequence() {
308 assert!(should_exclude_from_cdc(KeyTag::SystemSequence));
309 }
310
311 #[test]
312 fn test_exclude_row_sequence() {
313 assert!(should_exclude_from_cdc(KeyTag::RowSequence));
314 }
315
316 #[test]
317 fn test_exclude_column_sequence() {
318 assert!(should_exclude_from_cdc(KeyTag::ColumnSequence));
319 }
320
321 #[test]
322 fn test_exclude_system_version() {
323 assert!(should_exclude_from_cdc(KeyTag::SystemVersion));
324 }
325
326 #[test]
327 fn test_exclude_transaction_version() {
328 assert!(should_exclude_from_cdc(KeyTag::TransactionVersion));
329 }
330
331 #[test]
332 fn test_exclude_ring_buffer_metadata() {
333 assert!(should_exclude_from_cdc(KeyTag::RingBufferMetadata));
334 }
335
336 #[test]
337 fn test_exclude_index() {
338 assert!(should_exclude_from_cdc(KeyTag::Index));
339 }
340
341 #[test]
342 fn test_include_namespace() {
343 assert!(!should_exclude_from_cdc(KeyTag::Namespace));
344 }
345
346 #[test]
347 fn test_include_table() {
348 assert!(!should_exclude_from_cdc(KeyTag::Table));
349 }
350
351 #[test]
352 fn test_include_row() {
353 assert!(!should_exclude_from_cdc(KeyTag::Row));
354 }
355
356 #[test]
357 fn test_include_series_row() {
358 assert!(!should_exclude_from_cdc(KeyTag::SeriesRow));
360 }
361
362 #[test]
363 fn test_include_partitioned_row() {
364 assert!(!should_exclude_from_cdc(KeyTag::PartitionedRow));
365 }
366
367 #[test]
368 fn test_include_partition() {
369 assert!(!should_exclude_from_cdc(KeyTag::Partition));
370 }
371
372 #[test]
373 fn test_include_namespace_table() {
374 assert!(!should_exclude_from_cdc(KeyTag::NamespaceTable));
375 }
376
377 #[test]
378 fn test_include_columns() {
379 assert!(!should_exclude_from_cdc(KeyTag::Columns));
380 }
381
382 #[test]
383 fn test_include_column() {
384 assert!(!should_exclude_from_cdc(KeyTag::Column));
385 }
386
387 #[test]
388 fn test_include_column_property() {
389 assert!(!should_exclude_from_cdc(KeyTag::ColumnProperty));
390 }
391
392 #[test]
393 fn test_include_index_entry() {
394 assert!(!should_exclude_from_cdc(KeyTag::IndexEntry));
395 }
396
397 #[test]
398 fn test_include_view() {
399 assert!(!should_exclude_from_cdc(KeyTag::View));
400 }
401
402 #[test]
403 fn test_include_namespace_view() {
404 assert!(!should_exclude_from_cdc(KeyTag::NamespaceView));
405 }
406
407 #[test]
408 fn test_include_primary_key() {
409 assert!(!should_exclude_from_cdc(KeyTag::PrimaryKey));
410 }
411
412 #[test]
413 fn test_include_ring_buffer() {
414 assert!(!should_exclude_from_cdc(KeyTag::RingBuffer));
415 }
416
417 #[test]
418 fn test_include_namespace_ring_buffer() {
419 assert!(!should_exclude_from_cdc(KeyTag::NamespaceRingBuffer));
420 }
421
422 #[test]
423 fn test_include_queue() {
424 assert!(!should_exclude_from_cdc(KeyTag::Queue));
425 }
426
427 #[test]
428 fn test_include_namespace_queue() {
429 assert!(!should_exclude_from_cdc(KeyTag::NamespaceQueue));
430 }
431
432 #[test]
433 fn test_include_queue_deduplication() {
434 assert!(!should_exclude_from_cdc(KeyTag::QueueDeduplication));
435 }
436
437 #[test]
438 fn test_exclude_queue_partition() {
439 assert!(should_exclude_from_cdc(KeyTag::QueuePartition));
440 }
441
442 #[test]
443 fn test_exclude_queue_item_state() {
444 assert!(should_exclude_from_cdc(KeyTag::QueueItemState));
445 }
446
447 #[test]
448 fn test_exclude_queue_due() {
449 assert!(should_exclude_from_cdc(KeyTag::QueueDue));
450 }
451
452 #[test]
453 fn test_exclude_queue_key_active() {
454 assert!(should_exclude_from_cdc(KeyTag::QueueKeyActive));
455 }
456
457 #[test]
458 fn test_include_queue_attempt() {
459 assert!(!should_exclude_from_cdc(KeyTag::QueueAttempt));
463 }
464
465 #[test]
466 fn test_include_operator_settings() {
467 assert!(!should_exclude_from_cdc(KeyTag::OperatorSettings));
468 }
469
470 #[test]
471 fn test_include_flow() {
472 assert!(!should_exclude_from_cdc(KeyTag::Flow));
473 }
474
475 #[test]
476 fn test_include_namespace_flow() {
477 assert!(!should_exclude_from_cdc(KeyTag::NamespaceFlow));
478 }
479
480 #[test]
481 fn test_include_operator() {
482 assert!(!should_exclude_from_cdc(KeyTag::Operator));
483 }
484
485 #[test]
486 fn test_include_operator_by_flow() {
487 assert!(!should_exclude_from_cdc(KeyTag::OperatorByFlow));
488 }
489
490 #[test]
491 fn test_include_flow_edge() {
492 assert!(!should_exclude_from_cdc(KeyTag::FlowEdge));
493 }
494
495 #[test]
496 fn test_include_flow_edge_by_flow() {
497 assert!(!should_exclude_from_cdc(KeyTag::FlowEdgeByFlow));
498 }
499
500 #[test]
501 fn test_include_dictionary() {
502 assert!(!should_exclude_from_cdc(KeyTag::Dictionary));
503 }
504
505 #[test]
506 fn test_include_dictionary_entry() {
507 assert!(!should_exclude_from_cdc(KeyTag::DictionaryEntry));
508 }
509
510 #[test]
511 fn test_include_dictionary_entry_index() {
512 assert!(!should_exclude_from_cdc(KeyTag::DictionaryEntryIndex));
513 }
514
515 #[test]
516 fn test_include_namespace_dictionary() {
517 assert!(!should_exclude_from_cdc(KeyTag::NamespaceDictionary));
518 }
519
520 #[test]
521 fn test_include_handler() {
522 assert!(!should_exclude_from_cdc(KeyTag::Handler));
523 }
524
525 #[test]
526 fn test_include_namespace_handler() {
527 assert!(!should_exclude_from_cdc(KeyTag::NamespaceHandler));
528 }
529
530 #[test]
531 fn test_include_variant_handler() {
532 assert!(!should_exclude_from_cdc(KeyTag::VariantHandler));
533 }
534
535 #[test]
536 fn test_include_shape() {
537 assert!(!should_exclude_from_cdc(KeyTag::RowShape));
538 }
539
540 #[test]
541 fn test_include_sum_type() {
542 assert!(!should_exclude_from_cdc(KeyTag::SumType));
543 }
544
545 #[test]
546 fn test_include_namespace_sum_type() {
547 assert!(!should_exclude_from_cdc(KeyTag::NamespaceSumType));
548 }
549
550 #[test]
551 fn test_include_shape_field() {
552 assert!(!should_exclude_from_cdc(KeyTag::RowShapeField));
553 }
554
555 #[test]
556 fn test_include_series() {
557 assert!(!should_exclude_from_cdc(KeyTag::Series));
558 }
559
560 #[test]
561 fn test_include_namespace_series() {
562 assert!(!should_exclude_from_cdc(KeyTag::NamespaceSeries));
563 }
564
565 #[test]
566 fn test_include_series_metadata() {
567 assert!(!should_exclude_from_cdc(KeyTag::SeriesMetadata));
568 }
569
570 #[test]
571 fn test_include_identity() {
572 assert!(!should_exclude_from_cdc(KeyTag::Identity));
573 }
574
575 #[test]
576 fn test_include_role() {
577 assert!(!should_exclude_from_cdc(KeyTag::Role));
578 }
579
580 #[test]
581 fn test_include_granted_role() {
582 assert!(!should_exclude_from_cdc(KeyTag::GrantedRole));
583 }
584
585 #[test]
586 fn test_include_identity_attribute() {
587 assert!(!should_exclude_from_cdc(KeyTag::IdentityAttribute));
588 }
589
590 #[test]
591 fn test_include_identity_attribute_value() {
592 assert!(!should_exclude_from_cdc(KeyTag::IdentityAttributeValue));
593 }
594
595 #[test]
596 fn test_include_authentication() {
597 assert!(!should_exclude_from_cdc(KeyTag::Authentication));
598 }
599
600 #[test]
601 fn test_include_policy() {
602 assert!(!should_exclude_from_cdc(KeyTag::Policy));
603 }
604
605 #[test]
606 fn test_include_policy_op() {
607 assert!(!should_exclude_from_cdc(KeyTag::PolicyOp));
608 }
609
610 #[test]
611 fn test_include_migration() {
612 assert!(!should_exclude_from_cdc(KeyTag::Migration));
613 }
614
615 #[test]
616 fn test_include_migration_event() {
617 assert!(!should_exclude_from_cdc(KeyTag::MigrationEvent));
618 }
619
620 #[test]
621 fn test_exclude_flow_version() {
622 assert!(should_exclude_from_cdc(KeyTag::FlowVersion));
623 }
624
625 #[test]
626 fn test_exclude_config() {
627 assert!(should_exclude_from_cdc(KeyTag::ConfigStorage));
628 }
629
630 #[test]
631 fn test_exclude_version_epoch() {
632 assert!(should_exclude_from_cdc(KeyTag::VersionEpoch));
633 }
634}
635
636impl KeyFields for CdcConsumerKey {
637 fn fields(&self) -> SmallVec<[Field<'_>; 6]> {
638 smallvec![Field::BytesDesc(ByteEncoding::Escaped, Cow::Borrowed(self.consumer.as_ref().as_bytes()))]
639 }
640}