1#![feature(impl_trait_in_assoc_type)]
16#![feature(map_try_insert)]
17#![feature(stmt_expr_attributes)]
18
19mod key_cmp;
20
21use std::borrow::Borrow;
22use std::cmp::Ordering;
23use std::collections::HashMap;
24
25pub use key_cmp::*;
26use risingwave_common::util::epoch::EPOCH_SPILL_TIME_MASK;
27use risingwave_pb::common::{BatchQueryEpoch, batch_query_epoch};
28use sstable_info::SstableInfo;
29
30use crate::key_range::KeyRangeCommon;
31use crate::table_stats::TableStatsMap;
32
33pub mod change_log;
34pub mod compact;
35pub mod compact_task;
36pub mod compaction_group;
37pub mod filter_utils;
38pub mod key;
39pub mod key_range;
40pub mod level;
41pub mod prost_key_range;
42pub mod sstable_info;
43pub mod state_table_info;
44pub mod table_stats;
45pub mod table_watermark;
46pub mod time_travel;
47pub mod version;
48pub use frontend_version::{FrontendHummockVersion, FrontendHummockVersionDelta};
49mod frontend_version;
50pub mod vector_index;
51
52pub use compact::*;
53use risingwave_common::catalog::TableId;
54use risingwave_pb::hummock::hummock_version_checkpoint::PbStaleObjects;
55use risingwave_pb::hummock::{PbVectorIndexObjectType, VectorIndexObjectType};
56pub use risingwave_pb::id::{
57 CompactionGroupId, HummockHnswGraphFileId, HummockRawObjectId, HummockSstableId,
58 HummockSstableObjectId, HummockVectorFileId, HummockVersionId,
59};
60
61use crate::table_watermark::TableWatermarks;
62use crate::vector_index::VectorIndexAdd;
63
64pub type HummockRefCount = u64;
65pub type HummockContextId = risingwave_common::id::WorkerId;
66pub type HummockEpoch = u64;
67pub type HummockCompactionTaskId = u64;
68
69pub const INVALID_VERSION_ID: HummockVersionId = HummockVersionId::new(0);
70pub const FIRST_VERSION_ID: HummockVersionId = HummockVersionId::new(1);
71pub const SPLIT_TABLE_COMPACTION_GROUP_ID_HEAD: u64 = 1u64 << 56;
72pub const SINGLE_TABLE_COMPACTION_GROUP_ID_HEAD: u64 = 2u64 << 56;
73pub const SST_OBJECT_SUFFIX: &str = "data";
74pub const VECTOR_FILE_OBJECT_SUFFIX: &str = "vector";
75pub const HUMMOCK_SSTABLE_OBJECT_ID_MAX_DECIMAL_LENGTH: usize = 20;
76
77macro_rules! for_all_object_suffix {
78 ($({$name:ident, $type_name:ty, $suffix:expr},)+) => {
79 #[derive(Eq, PartialEq, Debug, Hash, Clone, Copy)]
80 pub enum HummockObjectId {
81 $(
82 $name($type_name),
83 )+
84 }
85
86 pub const VALID_OBJECT_ID_SUFFIXES: [&str; 3] = [$(
87 $suffix
88 ),+];
89
90 impl HummockObjectId {
91 fn new(id: u64, suffix: &str) -> Option<Self> {
92 match suffix {
93 $(
94 suffix if suffix == $suffix => Some(HummockObjectId::$name(<$type_name>::new(id))),
95 )+
96 _ => None,
97 }
98 }
99
100 pub fn suffix(&self) -> &str {
101 match self {
102 $(
103 HummockObjectId::$name(_) => $suffix,
104 )+
105 }
106 }
107
108 pub fn as_raw(&self) -> HummockRawObjectId {
109 let raw = match self {
110 $(
111 HummockObjectId::$name(id) => id.as_raw_id(),
112 )+
113 };
114 HummockRawObjectId::new(raw)
115 }
116 }
117
118 pub fn try_get_object_id_from_path(path: &str) -> Option<HummockObjectId> {
119 let split: Vec<_> = path.split(&['/', '.']).collect();
120 if split.len() <= 2 {
121 return None;
122 }
123 let suffix = split[split.len() - 1];
124 let id_str = split[split.len() - 2];
125 match suffix {
126 $(
127 suffix if suffix == $suffix => {
128 let id = id_str
129 .parse::<u64>()
130 .unwrap_or_else(|_| panic!("expect valid object id, got {}", id_str));
131 Some(HummockObjectId::$name(<$type_name>::new(id)))
132 },
133 )+
134 _ => None,
135 }
136 }
137 };
138 () => {
139 for_all_object_suffix! {
140 {Sstable, HummockSstableObjectId, SST_OBJECT_SUFFIX},
141 {VectorFile, HummockVectorFileId, VECTOR_FILE_OBJECT_SUFFIX},
142 {HnswGraphFile, HummockHnswGraphFileId, "hnsw_graph"},
143 }
144 };
145}
146
147for_all_object_suffix!();
148
149pub fn get_stale_object_ids(
150 stale_objects: &PbStaleObjects,
151) -> impl Iterator<Item = HummockObjectId> + '_ {
152 match HummockObjectId::Sstable(0.into()) {
156 HummockObjectId::Sstable(_) => {}
157 HummockObjectId::VectorFile(_) => {}
158 HummockObjectId::HnswGraphFile(_) => {}
159 };
160 stale_objects
161 .id
162 .iter()
163 .map(|sst_id| HummockObjectId::Sstable(*sst_id))
164 .chain(stale_objects.vector_files.iter().map(
165 |file| match file.get_object_type().unwrap() {
166 PbVectorIndexObjectType::VectorIndexObjectUnspecified => {
167 unreachable!()
168 }
169 VectorIndexObjectType::VectorIndexObjectVector => {
170 HummockObjectId::VectorFile(file.id.into())
171 }
172 VectorIndexObjectType::VectorIndexObjectHnswGraph => {
173 HummockObjectId::HnswGraphFile(file.id.into())
174 }
175 },
176 ))
177}
178
179#[macro_export]
180macro_rules! info_in_release {
189 ($($arg:tt)*) => {
190 {
191 #[cfg(debug_assertions)]
192 {
193 use tracing::debug;
194 debug!($($arg)*);
195 }
196 #[cfg(not(debug_assertions))]
197 {
198 use tracing::info;
199 info!($($arg)*);
200 }
201 }
202 }
203}
204
205#[derive(Default, Debug)]
206pub struct SyncResult {
207 pub sync_size: usize,
209 pub uncommitted_ssts: Vec<LocalSstableInfo>,
211 pub table_watermarks: HashMap<TableId, TableWatermarks>,
213 pub old_value_ssts: Vec<LocalSstableInfo>,
215 pub vector_index_adds: HashMap<TableId, Vec<VectorIndexAdd>>,
216}
217
218#[derive(Debug, Clone)]
219pub struct LocalSstableInfo {
220 pub sst_info: SstableInfo,
221 pub table_stats: TableStatsMap,
222 pub created_at: u64,
223}
224
225impl LocalSstableInfo {
226 pub fn new(sst_info: SstableInfo, table_stats: TableStatsMap, created_at: u64) -> Self {
227 Self {
228 sst_info,
229 table_stats,
230 created_at,
231 }
232 }
233
234 pub fn for_test(sst_info: SstableInfo) -> Self {
235 Self {
236 sst_info,
237 table_stats: Default::default(),
238 created_at: u64::MAX,
239 }
240 }
241
242 pub fn file_size(&self) -> u64 {
243 assert_eq!(self.sst_info.file_size, self.sst_info.sst_size);
244 self.sst_info.file_size
245 }
246}
247
248impl PartialEq for LocalSstableInfo {
249 fn eq(&self, other: &Self) -> bool {
250 self.sst_info == other.sst_info
251 }
252}
253
254#[derive(Debug, Clone, Copy)]
256pub enum HummockReadEpoch {
257 Committed(HummockEpoch),
259 BatchQueryCommitted(HummockEpoch, HummockVersionId),
261 NoWait(HummockEpoch),
263 Backup(HummockEpoch),
265 TimeTravel(HummockEpoch),
266}
267
268impl From<BatchQueryEpoch> for HummockReadEpoch {
269 fn from(e: BatchQueryEpoch) -> Self {
270 match e.epoch.unwrap() {
271 batch_query_epoch::Epoch::Committed(epoch) => {
272 HummockReadEpoch::BatchQueryCommitted(epoch.epoch, epoch.hummock_version_id)
273 }
274 batch_query_epoch::Epoch::Current(epoch) => HummockReadEpoch::NoWait(epoch),
275 batch_query_epoch::Epoch::Backup(epoch) => HummockReadEpoch::Backup(epoch),
276 batch_query_epoch::Epoch::TimeTravel(epoch) => HummockReadEpoch::TimeTravel(epoch),
277 }
278 }
279}
280
281pub fn test_batch_query_epoch() -> BatchQueryEpoch {
282 BatchQueryEpoch {
283 epoch: Some(batch_query_epoch::Epoch::Current(u64::MAX)),
284 }
285}
286
287impl HummockReadEpoch {
288 pub fn get_epoch(&self) -> HummockEpoch {
289 *match self {
290 HummockReadEpoch::Committed(epoch)
291 | HummockReadEpoch::BatchQueryCommitted(epoch, _)
292 | HummockReadEpoch::NoWait(epoch)
293 | HummockReadEpoch::Backup(epoch)
294 | HummockReadEpoch::TimeTravel(epoch) => epoch,
295 }
296 }
297
298 pub fn is_read_committed(&self) -> bool {
299 match self {
300 HummockReadEpoch::Committed(_)
301 | HummockReadEpoch::TimeTravel(_)
302 | HummockReadEpoch::BatchQueryCommitted(_, _) => true,
303 HummockReadEpoch::NoWait(_) | HummockReadEpoch::Backup(_) => false,
304 }
305 }
306}
307pub struct ObjectIdRange {
308 pub start_id: HummockRawObjectId,
310 pub end_id: HummockRawObjectId,
312}
313
314impl ObjectIdRange {
315 pub fn new(
316 start_id: impl Into<HummockRawObjectId>,
317 end_id: impl Into<HummockRawObjectId>,
318 ) -> Self {
319 Self {
320 start_id: start_id.into(),
321 end_id: end_id.into(),
322 }
323 }
324
325 fn peek_next_object_id(&self) -> Option<HummockRawObjectId> {
326 if self.start_id < self.end_id {
327 return Some(self.start_id);
328 }
329 None
330 }
331
332 pub fn get_next_object_id(&mut self) -> Option<HummockRawObjectId> {
334 let next_id = self.peek_next_object_id();
335 self.start_id += 1;
336 next_id
337 }
338}
339
340pub fn can_concat(ssts: &[impl Borrow<SstableInfo>]) -> bool {
341 let len = ssts.len();
342 for i in 1..len {
343 if ssts[i - 1]
344 .borrow()
345 .key_range
346 .compare_right_with(&ssts[i].borrow().key_range.left)
347 != Ordering::Less
348 {
349 return false;
350 }
351 }
352 true
353}
354
355pub fn full_key_can_concat(ssts: &[SstableInfo]) -> bool {
356 let len = ssts.len();
357 for i in 1..len {
358 let sst_1 = &ssts[i - 1];
359 let sst_2 = &ssts[i];
360
361 if sst_1.key_range.right_exclusive {
362 if KeyComparator::compare_encoded_full_key(
363 &sst_1.key_range.right,
364 &sst_2.key_range.left,
365 )
366 .is_gt()
367 {
368 return false;
369 }
370 } else if KeyComparator::compare_encoded_full_key(
371 &sst_1.key_range.right,
372 &sst_2.key_range.left,
373 )
374 .is_ge()
375 {
376 return false;
377 }
378 }
379 true
380}
381
382const CHECKPOINT_DIR: &str = "checkpoint";
383const CHECKPOINT_NAME: &str = "0";
384const ARCHIVE_DIR: &str = "archive";
385
386pub fn version_checkpoint_path(root_dir: &str) -> String {
387 format!("{}/{}/{}", root_dir, CHECKPOINT_DIR, CHECKPOINT_NAME)
388}
389
390pub fn version_archive_dir(root_dir: &str) -> String {
391 format!("{}/{}", root_dir, ARCHIVE_DIR)
392}
393
394pub fn version_checkpoint_dir(checkpoint_path: &str) -> String {
395 checkpoint_path.trim_end_matches(|c| c != '/').to_owned()
396}
397
398#[derive(Clone, Copy, PartialEq, Eq, Hash, Default, Debug, PartialOrd, Ord)]
404pub struct EpochWithGap(u64);
405
406impl EpochWithGap {
407 pub fn new(epoch: u64, spill_offset: u16) -> Self {
408 if risingwave_common::util::epoch::is_max_epoch(epoch) {
412 EpochWithGap::new_max_epoch()
413 } else {
414 debug_assert!((epoch & EPOCH_SPILL_TIME_MASK) == 0);
415 EpochWithGap(epoch + spill_offset as u64)
416 }
417 }
418
419 pub fn new_from_epoch(epoch: u64) -> Self {
420 EpochWithGap::new(epoch, 0)
421 }
422
423 pub fn new_min_epoch() -> Self {
424 EpochWithGap(0)
425 }
426
427 pub fn new_max_epoch() -> Self {
428 EpochWithGap(HummockEpoch::MAX)
429 }
430
431 pub(crate) fn as_u64(&self) -> HummockEpoch {
433 self.0
434 }
435
436 pub fn from_u64(epoch_with_gap: u64) -> Self {
438 EpochWithGap(epoch_with_gap)
439 }
440
441 pub fn pure_epoch(&self) -> HummockEpoch {
443 self.0 & !EPOCH_SPILL_TIME_MASK
444 }
445
446 pub fn offset(&self) -> u64 {
447 self.0 & EPOCH_SPILL_TIME_MASK
448 }
449}
450
451pub fn get_object_data_path(
452 obj_prefix: &str,
453 path_prefix: &str,
454 object_id: HummockObjectId,
455) -> String {
456 let suffix = object_id.suffix();
457 let object_id = object_id.as_raw();
458
459 let mut path = String::with_capacity(
460 path_prefix.len()
461 + "/".len()
462 + obj_prefix.len()
463 + HUMMOCK_SSTABLE_OBJECT_ID_MAX_DECIMAL_LENGTH
464 + ".".len()
465 + suffix.len(),
466 );
467 path.push_str(path_prefix);
468 path.push('/');
469 path.push_str(obj_prefix);
470 path.push_str(&object_id.to_string());
471 path.push('.');
472 path.push_str(suffix);
473 path
474}
475
476pub fn get_object_id_from_path(path: &str) -> HummockObjectId {
477 use itertools::Itertools;
478 let split = path.split(&['/', '.']).collect_vec();
479 assert!(split.len() > 2);
480 let suffix = split[split.len() - 1];
481 let id = split[split.len() - 2]
482 .parse::<u64>()
483 .expect("valid object id");
484 HummockObjectId::new(id, suffix)
485 .unwrap_or_else(|| panic!("unknown object id suffix {}", suffix))
486}
487
488#[cfg(test)]
489mod tests {
490 use bytes::Bytes;
491 use sstable_info::SstableInfoInner;
492
493 use super::*;
494
495 #[test]
496 fn test_object_id_decimal_max_length() {
497 let len = u64::MAX.to_string().len();
498 assert_eq!(len, HUMMOCK_SSTABLE_OBJECT_ID_MAX_DECIMAL_LENGTH)
499 }
500
501 #[test]
502 fn test_full_key_concat() {
503 let key1 = b"\0\0\0\x08\0\0\0\x0112-3\0\0\0\0\x04\0\x1c\x16l'\xe2\0\0";
504 let key2 = b"\0\0\0\x08\0\0\0\x0112-3\0\0\0\0\x04\0\x1c\x16l \x12\0\0";
505
506 let sst_1 = SstableInfoInner {
507 key_range: key_range::KeyRange {
508 left: Bytes::from(key1.to_vec()),
509 right: Bytes::from(key1.to_vec()),
510 right_exclusive: false,
511 },
512 ..Default::default()
513 };
514
515 let sst_2 = SstableInfoInner {
516 key_range: key_range::KeyRange {
517 left: Bytes::from(key2.to_vec()),
518 right: Bytes::from(key2.to_vec()),
519 right_exclusive: false,
520 },
521 ..Default::default()
522 };
523
524 let sst_3 = SstableInfoInner {
525 key_range: key_range::KeyRange {
526 left: Bytes::from(key1.to_vec()),
527 right: Bytes::from(key2.to_vec()),
528 right_exclusive: false,
529 },
530 ..Default::default()
531 };
532
533 assert!(full_key_can_concat(&[sst_1.clone().into(), sst_2.into()]));
534
535 assert!(!full_key_can_concat(&[sst_1.into(), sst_3.into()]));
536 }
537}