Skip to main content

reifydb_core/key/
ringbuffer.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2026 ReifyDB
3
4use std::borrow::Cow;
5
6use reifydb_codec::key::{deserializer::KeyDeserializer, encoded::EncodedKey, serializer::KeySerializer};
7use reifydb_macro::KeyCodec;
8use reifydb_value::value::Value;
9use smallvec::{SmallVec, smallvec};
10
11use super::KeyTag;
12use crate::{
13	interface::catalog::{id::RingBufferId, object::ObjectId, storage::StorageId},
14	key::{
15		any::{Field, KeyFields, RawEncoding, Width, encode_values},
16		bound::{TaggedKeyBoundRange, object_fields},
17		catalog::{KeyDeserializerCatalogExt, KeySerializerCatalogExt},
18	},
19};
20
21#[derive(Debug, Clone, PartialEq, KeyCodec, Hash)]
22#[key(tag = RingBuffer)]
23pub struct RingBufferKey {
24	pub ringbuffer: RingBufferId,
25}
26
27impl RingBufferKey {
28	pub fn new(ringbuffer: RingBufferId) -> Self {
29		Self {
30			ringbuffer,
31		}
32	}
33
34	pub fn encoded(ringbuffer: impl Into<RingBufferId>) -> EncodedKey {
35		Self::new(ringbuffer.into()).encode()
36	}
37
38	pub fn full_scan() -> TaggedKeyBoundRange {
39		TaggedKeyBoundRange::kind(Self::TAG)
40	}
41}
42
43#[derive(Debug, Clone, PartialEq, Hash)]
44pub struct RingBufferMetadataKey {
45	pub storage: StorageId,
46	pub partition_values: Vec<Value>,
47}
48
49impl RingBufferMetadataKey {
50	pub fn new(storage: impl Into<StorageId>) -> Self {
51		Self {
52			storage: storage.into(),
53			partition_values: vec![],
54		}
55	}
56
57	pub fn encoded(storage: impl Into<StorageId>) -> EncodedKey {
58		Self::new(storage).encode()
59	}
60
61	pub fn partition(storage: impl Into<StorageId>, partition_values: Vec<Value>) -> Self {
62		Self {
63			storage: storage.into(),
64			partition_values,
65		}
66	}
67
68	pub fn encoded_partition(storage: impl Into<StorageId>, partition_values: Vec<Value>) -> EncodedKey {
69		Self::partition(storage, partition_values).encode()
70	}
71
72	pub fn full_scan_for_storage(storage: impl Into<StorageId>) -> TaggedKeyBoundRange {
73		TaggedKeyBoundRange::prefix(Self::TAG, object_fields(ObjectId::from(storage.into())))
74	}
75}
76
77impl RingBufferMetadataKey {
78	pub const TAG: KeyTag = KeyTag::RingBufferMetadata;
79
80	pub fn encode(&self) -> EncodedKey {
81		let mut serializer = KeySerializer::with_capacity(32);
82		serializer.extend_u8(Self::TAG as u8).extend_object_id(self.storage);
83		for value in &self.partition_values {
84			serializer.extend_value(value);
85		}
86		serializer.to_encoded_key()
87	}
88
89	pub fn decode(key: &EncodedKey) -> Option<Self> {
90		let mut de = KeyDeserializer::from_bytes(key.as_slice());
91
92		let kind: KeyTag = de.read_u8().ok()?.try_into().ok()?;
93		if kind != Self::TAG {
94			return None;
95		}
96
97		let storage = StorageId::from_object(de.read_object_id().ok()?)?;
98
99		let mut partition_values = Vec::new();
100		while !de.is_empty() {
101			partition_values.push(de.read_value().ok()?);
102		}
103
104		Some(Self {
105			storage,
106			partition_values,
107		})
108	}
109}
110
111#[cfg(test)]
112mod tests {
113	use std::ops::RangeBounds;
114
115	use super::*;
116	use crate::interface::catalog::id::ViewId;
117
118	#[test]
119	fn test_metadata_key_encode_decode_roundtrip() {
120		let key = RingBufferMetadataKey::encoded_partition(
121			RingBufferId(42),
122			vec![Value::Utf8("east".to_string())],
123		);
124		let mut de = KeyDeserializer::from_bytes(key.as_slice());
125		let _ = (de.read_u8(), de.read_object_id());
126		let value = de.read_value().unwrap();
127		assert_eq!(value, Value::Utf8("east".to_string()));
128	}
129
130	#[test]
131	fn test_metadata_key_encode_decode_multiple() {
132		let key = RingBufferMetadataKey::encoded_partition(
133			RingBufferId(7),
134			vec![Value::Utf8("us".to_string()), Value::Uint8(42)],
135		);
136		let mut de = KeyDeserializer::from_bytes(key.as_slice());
137		let _ = (de.read_u8(), de.read_object_id());
138		assert_eq!(de.read_value().unwrap(), Value::Utf8("us".to_string()));
139		assert_eq!(de.read_value().unwrap(), Value::Uint8(42));
140	}
141
142	#[test]
143	fn test_metadata_key_roundtrip_ringbuffer() {
144		// The tag byte is what keeps a ring buffer's metadata out of a view's; a bare id would collide.
145		let key = RingBufferMetadataKey {
146			storage: StorageId::RingBuffer(RingBufferId(42)),
147			partition_values: vec![Value::Utf8("east".to_string())],
148		};
149		assert_eq!(RingBufferMetadataKey::decode(&key.encode()).unwrap(), key);
150	}
151
152	#[test]
153	fn test_metadata_key_roundtrip_view() {
154		// A ring-buffer-backed view keeps its own metadata under its own id, not a backing object's.
155		let key = RingBufferMetadataKey {
156			storage: StorageId::View(ViewId(42)),
157			partition_values: vec![Value::Utf8("east".to_string())],
158		};
159		assert_eq!(RingBufferMetadataKey::decode(&key.encode()).unwrap(), key);
160	}
161
162	#[test]
163	fn test_full_scan_for_storage_excludes_a_view_with_the_same_id() {
164		// Ring buffer 42 and view 42 share a numeric id, so only the tag byte separates their scans.
165		let range = RingBufferMetadataKey::full_scan_for_storage(RingBufferId(42)).encode();
166		let ringbuffer = RingBufferMetadataKey::encoded(RingBufferId(42));
167		let view = RingBufferMetadataKey::encoded(ViewId(42));
168		assert!(range.contains(&ringbuffer));
169		assert!(!range.contains(&view));
170	}
171
172	#[test]
173	fn test_ring_buffer_key_matches_legacy_byte_layout() {
174		for id in [RingBufferId(0), RingBufferId(1), RingBufferId(u64::MAX)] {
175			let mut legacy = KeySerializer::with_capacity(9);
176			legacy.extend_u8(KeyTag::RingBuffer as u8).extend_u64(id);
177			assert_eq!(legacy.to_encoded_key().as_slice(), RingBufferKey::encoded(id).as_slice());
178		}
179	}
180}
181
182impl KeyFields for RingBufferMetadataKey {
183	fn fields(&self) -> SmallVec<[Field<'_>; 6]> {
184		smallvec![
185			Field::UAsc(Width::U8, ObjectId::from(self.storage).type_tag() as u128),
186			Field::UDesc(Width::U64, ObjectId::from(self.storage).as_u64() as u128),
187			Field::RawAsc(
188				RawEncoding::Verbatim,
189				Cow::Owned(encode_values(&self.partition_values).as_slice().to_vec())
190			),
191		]
192	}
193}