1mod block;
19
20use std::collections::HashSet;
21use std::fmt::{Debug, Formatter};
22use std::ops::{BitXor, Bound, Range};
23
24pub use block::*;
25mod block_iterator;
26pub use block_iterator::*;
27mod xor_filter;
28use serde::{Deserialize, Serialize};
29pub use xor_filter::{
30 BlockedXor8FilterBuilder, BlockedXor16FilterBuilder, Xor8FilterBuilder, Xor16FilterBuilder,
31 XorFilterReader,
32};
33pub mod builder;
34pub use builder::*;
35pub mod writer;
36use risingwave_common::catalog::TableId;
37pub use writer::*;
38mod forward_sstable_iterator;
39pub mod multi_builder;
40use bytes::{Buf, BufMut};
41pub use forward_sstable_iterator::*;
42use tracing::warn;
43mod backward_sstable_iterator;
44pub use backward_sstable_iterator::*;
45use risingwave_hummock_sdk::key::{FullKey, KeyPayloadType, UserKey, UserKeyRangeRef};
46use risingwave_hummock_sdk::{HummockEpoch, HummockSstableObjectId};
47
48mod filter;
49mod utils;
50
51pub use filter::{
52 DEFAULT_FILTER_HASH_PREALLOC_KEY_COUNT_CAP, FilterBuilder, FilterBuilderOptions,
53 NoneFilterBuilder,
54};
55pub use utils::{CompressionAlgorithm, xxhash64_checksum, xxhash64_verify};
56use utils::{get_length_prefixed_slice, put_length_prefixed_slice};
57use xxhash_rust::xxh64;
58
59use super::{HummockError, HummockResult};
60use crate::hummock::CachePolicy;
61use crate::store::ReadOptions;
62
63const MAGIC: u32 = 0x5785ab73;
64const OLD_VERSION: u32 = 1;
65const VERSION: u32 = 2;
66
67#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
81pub struct MonotonicDeleteEvent {
82 pub event_key:
83 risingwave_hummock_sdk::key::range_delete_backward_compatibility_serde_struct::PointRange,
84 pub new_epoch: HummockEpoch,
85}
86
87impl MonotonicDeleteEvent {
88 pub fn encode(&self, mut buf: impl BufMut) {
89 self.event_key
90 .left_user_key
91 .encode_length_prefixed(&mut buf);
92 buf.put_u8(if self.event_key.is_exclude_left_key {
93 1
94 } else {
95 0
96 });
97 buf.put_u64_le(self.new_epoch);
98 }
99
100 pub fn decode(buf: &mut &[u8]) -> Self {
101 use risingwave_hummock_sdk::key::range_delete_backward_compatibility_serde_struct::*;
102 let user_key = UserKey::decode_length_prefixed(buf);
103 let exclude_left_key_flag = buf.get_u8();
104 let is_exclude_left_key = match exclude_left_key_flag {
105 0 => false,
106 1 => true,
107 _ => panic!("exclusive flag should be either 0 or 1"),
108 };
109 let new_epoch = buf.get_u64_le();
110 Self {
111 event_key: PointRange {
112 left_user_key: user_key,
113 is_exclude_left_key,
114 },
115 new_epoch,
116 }
117 }
118}
119
120#[derive(Serialize, Deserialize)]
121struct SerdeSstable {
122 id: HummockSstableObjectId,
123 meta: SstableMeta,
124}
125
126impl From<SerdeSstable> for Sstable {
127 fn from(SerdeSstable { id, meta }: SerdeSstable) -> Self {
128 Sstable::new(id, meta, false)
131 }
132}
133
134#[derive(Clone, Deserialize)]
136#[serde(from = "SerdeSstable")]
137pub struct Sstable {
138 pub id: HummockSstableObjectId,
139 pub meta: SstableMeta,
140 #[serde(skip)]
141 pub filter_reader: XorFilterReader,
142 #[serde(skip)]
146 skip_bloom_filter_in_serde: bool,
147}
148
149impl Serialize for Sstable {
150 fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
151 where
152 S: serde::Serializer,
153 {
154 let mut serde_sstable = SerdeSstable {
155 id: self.id,
156 meta: self.meta.clone(),
157 };
158 if !self.skip_bloom_filter_in_serde {
159 serde_sstable.meta.bloom_filter = self.filter_reader.encode_to_bytes();
160 }
161 serde_sstable.serialize(serializer)
162 }
163}
164
165impl Debug for Sstable {
166 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
167 f.debug_struct("Sstable")
168 .field("id", &self.id)
169 .field("meta", &self.meta)
170 .finish()
171 }
172}
173
174impl Sstable {
175 pub fn new(
176 id: HummockSstableObjectId,
177 mut meta: SstableMeta,
178 skip_bloom_filter_in_serde: bool,
179 ) -> Self {
180 let filter_data = std::mem::take(&mut meta.bloom_filter);
181 let filter_reader = XorFilterReader::new(&filter_data, &meta.block_metas);
182 Self {
183 id,
184 meta,
185 filter_reader,
186 skip_bloom_filter_in_serde,
187 }
188 }
189
190 pub fn from_meta_bytes(
192 id: HummockSstableObjectId,
193 bytes: &[u8],
194 skip_bloom_filter_in_serde: bool,
195 ) -> HummockResult<Self> {
196 let (meta, filter_reader) = SstableMeta::decode_with_filter(bytes, XorFilterReader::new)?;
197 Ok(Self {
198 id,
199 meta,
200 filter_reader,
201 skip_bloom_filter_in_serde,
202 })
203 }
204
205 #[inline(always)]
206 pub fn has_filter(&self) -> bool {
207 !self.filter_reader.is_empty()
208 }
209
210 pub fn calculate_block_info(&self, block_index: usize) -> (Range<usize>, usize) {
211 let block_meta = &self.meta.block_metas[block_index];
212 let range =
213 block_meta.offset as usize..block_meta.offset as usize + block_meta.len as usize;
214 let uncompressed_capacity = block_meta.uncompressed_size as usize;
215 (range, uncompressed_capacity)
216 }
217
218 #[inline(always)]
219 pub fn hash_for_filter(dist_key: &[u8], table_id: u32) -> u64 {
220 let dist_key_hash = xxh64::xxh64(dist_key, 0);
221 (table_id as u64).bitxor(dist_key_hash)
223 }
224
225 #[inline(always)]
226 pub fn may_match_hash(&self, user_key_range: &UserKeyRangeRef<'_>, hash: u64) -> bool {
227 self.filter_reader
228 .may_match(&self.meta.block_metas, user_key_range, hash)
229 }
230
231 #[inline(always)]
232 pub fn block_count(&self) -> usize {
233 self.meta.block_metas.len()
234 }
235
236 #[inline(always)]
237 pub fn estimated_meta_cache_memory_weight(&self) -> usize {
238 std::mem::size_of::<Self>()
241 + self.meta.estimated_heap_size()
242 + self.filter_reader.estimated_heap_size()
243 }
244
245 pub(crate) fn serde_encoded_size(&self) -> usize {
247 let size = bincode::serialized_size(&(self.id, &self.meta)).unwrap() as usize;
251 if self.skip_bloom_filter_in_serde {
252 size
253 } else {
254 size - self.meta.bloom_filter.len() + self.filter_reader.serialized_len()
255 }
256 }
257}
258
259#[derive(Clone, Default, Debug, Eq, PartialEq, Serialize, Deserialize)]
260pub struct BlockMeta {
261 #[serde(with = "serde_bytes")]
262 pub smallest_key: Vec<u8>,
263 pub offset: u32,
264 pub len: u32,
265 pub uncompressed_size: u32,
266 pub total_key_count: u32,
267 pub stale_key_count: u32,
268}
269
270impl BlockMeta {
271 pub fn encode(&self, mut buf: impl BufMut) {
277 buf.put_u32_le(self.offset);
278 buf.put_u32_le(self.len);
279 buf.put_u32_le(self.uncompressed_size);
280 buf.put_u32_le(self.total_key_count);
281 buf.put_u32_le(self.stale_key_count);
282 put_length_prefixed_slice(buf, &self.smallest_key);
283 }
284
285 pub fn decode(buf: &mut &[u8]) -> Self {
286 let offset = buf.get_u32_le();
287 let len = buf.get_u32_le();
288 let uncompressed_size = buf.get_u32_le();
289
290 let total_key_count = buf.get_u32_le();
291 let stale_key_count = buf.get_u32_le();
292 let smallest_key = get_length_prefixed_slice(buf);
293 Self {
294 smallest_key,
295 offset,
296 len,
297 uncompressed_size,
298 total_key_count,
299 stale_key_count,
300 }
301 }
302
303 pub fn decode_from_v1(buf: &mut &[u8]) -> Self {
304 let offset = buf.get_u32_le();
305 let len = buf.get_u32_le();
306 let uncompressed_size = buf.get_u32_le();
307 let total_key_count = 0;
308 let stale_key_count = 0;
309 let smallest_key = get_length_prefixed_slice(buf);
310 Self {
311 smallest_key,
312 offset,
313 len,
314 uncompressed_size,
315 total_key_count,
316 stale_key_count,
317 }
318 }
319
320 #[inline]
321 pub fn encoded_size(&self) -> usize {
322 24 + self.smallest_key.len()
323 }
324
325 pub fn table_id(&self) -> TableId {
326 FullKey::decode(&self.smallest_key).user_key.table_id
327 }
328
329 fn estimated_heap_size(&self) -> usize {
330 self.smallest_key.capacity()
331 }
332}
333
334#[derive(Default, Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
335pub struct SstableMeta {
336 pub block_metas: Vec<BlockMeta>,
337 #[serde(with = "serde_bytes")]
338 pub bloom_filter: Vec<u8>,
339 pub estimated_size: u32,
340 pub key_count: u32,
341 #[serde(with = "serde_bytes")]
342 pub smallest_key: Vec<u8>,
343 #[serde(with = "serde_bytes")]
344 pub largest_key: Vec<u8>,
345 pub meta_offset: u64,
346 #[deprecated]
360 pub monotonic_tombstone_events: Vec<MonotonicDeleteEvent>,
361 pub version: u32,
363}
364
365impl SstableMeta {
366 pub fn encode_to_bytes(&self) -> Vec<u8> {
381 let encoded_size = self.encoded_size();
382 let mut buf = Vec::with_capacity(encoded_size);
383 self.encode_to(&mut buf);
384 buf
385 }
386
387 pub fn encode_to(&self, mut buf: impl BufMut + AsRef<[u8]>) {
388 let start = buf.as_ref().len();
389
390 buf.put_u32_le(
391 utils::checked_into_u32(self.block_metas.len()).unwrap_or_else(|_| {
392 let tmp_full_key = FullKey::decode(&self.smallest_key);
393 panic!(
394 "WARN overflow can't convert block_metas_len {} into u32 table {}",
395 self.block_metas.len(),
396 tmp_full_key.user_key.table_id,
397 )
398 }),
399 );
400 for block_meta in &self.block_metas {
401 block_meta.encode(&mut buf);
402 }
403 put_length_prefixed_slice(&mut buf, &self.bloom_filter);
404 buf.put_u32_le(self.estimated_size);
405 buf.put_u32_le(self.key_count);
406 put_length_prefixed_slice(&mut buf, &self.smallest_key);
407 put_length_prefixed_slice(&mut buf, &self.largest_key);
408 #[expect(deprecated)]
409 buf.put_u32_le(
410 utils::checked_into_u32(self.monotonic_tombstone_events.len()).unwrap_or_else(|_| {
411 let tmp_full_key = FullKey::decode(&self.smallest_key);
412 panic!(
413 "WARN overflow can't convert monotonic_tombstone_events_len {} into u32 table {}",
414 self.monotonic_tombstone_events.len(),
415 tmp_full_key.user_key.table_id,
416 )
417 }),
418 );
419 #[expect(deprecated)]
420 for monotonic_tombstone_event in &self.monotonic_tombstone_events {
421 monotonic_tombstone_event.encode(&mut buf);
422 }
423 buf.put_u64_le(self.meta_offset);
424
425 let end = buf.as_ref().len();
426
427 let checksum = xxhash64_checksum(&buf.as_ref()[start..end]);
428 buf.put_u64_le(checksum);
429 buf.put_u32_le(VERSION);
430 buf.put_u32_le(MAGIC);
431 }
432
433 pub fn decode(buf: &[u8]) -> HummockResult<Self> {
434 let (mut meta, bloom_filter) = Self::decode_with_filter(buf, |data, _| data.to_vec())?;
435 meta.bloom_filter = bloom_filter;
436 Ok(meta)
437 }
438
439 fn decode_with_filter<F>(
440 buf: &[u8],
441 restore_filter: impl FnOnce(&[u8], &[BlockMeta]) -> F,
442 ) -> HummockResult<(Self, F)> {
443 let mut cursor = buf.len();
444
445 cursor -= 4;
446 let magic = (&buf[cursor..]).get_u32_le();
447 if magic != MAGIC {
448 return Err(HummockError::magic_mismatch(MAGIC, magic));
449 }
450
451 cursor -= 4;
452 let version = (&buf[cursor..cursor + 4]).get_u32_le();
453 if version != VERSION && version != OLD_VERSION {
454 return Err(HummockError::invalid_format_version(version));
455 }
456
457 cursor -= 8;
458 let checksum = (&buf[cursor..cursor + 8]).get_u64_le();
459 let buf = &mut &buf[..cursor];
460 xxhash64_verify(buf, checksum)?;
461
462 let block_meta_count = buf.get_u32_le() as usize;
463 let mut block_metas = Vec::with_capacity(block_meta_count);
464 if version == OLD_VERSION {
465 for _ in 0..block_meta_count {
466 block_metas.push(BlockMeta::decode_from_v1(buf));
467 }
468 } else {
469 for _ in 0..block_meta_count {
470 block_metas.push(BlockMeta::decode(buf));
471 }
472 }
473
474 let filter_len = buf.get_u32_le() as usize;
475 let filter_data = &buf[..filter_len];
476 buf.advance(filter_len);
477 let estimated_size = buf.get_u32_le();
478 let key_count = buf.get_u32_le();
479 let smallest_key = get_length_prefixed_slice(buf);
480 let largest_key = get_length_prefixed_slice(buf);
481 let tomb_event_count = buf.get_u32_le() as usize;
482 let mut monotonic_tombstone_events = Vec::with_capacity(tomb_event_count);
483 for _ in 0..tomb_event_count {
484 let monotonic_tombstone_event = MonotonicDeleteEvent::decode(buf);
485 monotonic_tombstone_events.push(monotonic_tombstone_event);
486 }
487 let meta_offset = buf.get_u64_le();
488
489 if !monotonic_tombstone_events.is_empty() {
490 warn!(
491 count = monotonic_tombstone_events.len(),
492 tables = ?monotonic_tombstone_events
493 .iter()
494 .map(|event| event.event_key.left_user_key.table_id)
495 .collect::<HashSet<_>>(),
496 "read non-empty range tombstones");
497 }
498
499 let filter = restore_filter(filter_data, &block_metas);
500 #[expect(deprecated)]
501 let meta = Self {
502 block_metas,
503 bloom_filter: Vec::new(),
504 estimated_size,
505 key_count,
506 smallest_key,
507 largest_key,
508 meta_offset,
509 monotonic_tombstone_events,
510 version,
511 };
512 Ok((meta, filter))
513 }
514
515 #[inline]
516 pub fn encoded_size(&self) -> usize {
517 4 + self
519 .block_metas
520 .iter()
521 .map(|block_meta| block_meta.encoded_size())
522 .sum::<usize>()
523 + 4 + 4 + self.bloom_filter.len()
526 + 4 + 4 + 4 + self.smallest_key.len()
530 + 4 + self.largest_key.len()
532 + 8 + 8 + 4 + 4 }
537
538 #[expect(
539 deprecated,
540 reason = "monotonic_tombstone_events is deprecated but still contributes to decoded meta heap size"
541 )]
542 fn estimated_heap_size(&self) -> usize {
543 self.block_metas.capacity() * std::mem::size_of::<BlockMeta>()
544 + self
545 .block_metas
546 .iter()
547 .map(BlockMeta::estimated_heap_size)
548 .sum::<usize>()
549 + self.bloom_filter.capacity()
550 + self.smallest_key.capacity()
551 + self.largest_key.capacity()
552 + self.monotonic_tombstone_events.capacity()
553 * std::mem::size_of::<MonotonicDeleteEvent>()
554 }
555}
556
557#[derive(Default)]
558pub struct SstableIteratorReadOptions {
559 pub cache_policy: CachePolicy,
560 pub read_table_id: Option<TableId>,
564 pub scan_end_user_key: Option<Bound<UserKey<KeyPayloadType>>>,
566 pub prefetch: bool,
568}
569
570impl SstableIteratorReadOptions {
571 pub fn from_read_options(read_options: &ReadOptions) -> Self {
574 Self {
575 cache_policy: read_options.cache_policy,
576 read_table_id: None,
577 scan_end_user_key: None,
578 prefetch: false,
579 }
580 }
581}
582
583#[cfg(test)]
584mod tests {
585 use super::*;
586 use crate::hummock::HummockValue;
587 use crate::hummock::iterator::test_utils::{
588 default_builder_opt_for_test, iterator_test_key_of,
589 };
590 use crate::hummock::test_utils::gen_test_sstable_data;
591 use crate::store::PrefetchOptions;
592
593 #[test]
594 fn test_iterator_read_options_do_not_enable_prefetch_for_point_get() {
595 let read_options = ReadOptions {
596 prefetch_options: PrefetchOptions::prefetch_for_large_range_scan(),
597 ..Default::default()
598 };
599
600 let iterator_options = SstableIteratorReadOptions::from_read_options(&read_options);
601 assert!(!iterator_options.prefetch);
602 assert!(iterator_options.scan_end_user_key.is_none());
603 }
604
605 #[test]
606 fn test_sstable_meta_enc_dec() {
607 #[expect(deprecated)]
608 let meta = SstableMeta {
609 block_metas: vec![
610 BlockMeta {
611 smallest_key: b"0-smallest-key".to_vec(),
612 len: 100,
613 ..Default::default()
614 },
615 BlockMeta {
616 smallest_key: b"5-some-key".to_vec(),
617 offset: 100,
618 len: 100,
619 ..Default::default()
620 },
621 ],
622 bloom_filter: b"0123456789".to_vec(),
623 estimated_size: 123,
624 key_count: 123,
625 smallest_key: b"0-smallest-key".to_vec(),
626 largest_key: b"9-largest-key".to_vec(),
627 meta_offset: 123,
628 monotonic_tombstone_events: vec![],
629 version: VERSION,
630 };
631 let sz = meta.encoded_size();
632 let buf = meta.encode_to_bytes();
633 assert_eq!(sz, buf.len());
634 let decoded_meta = SstableMeta::decode(&buf[..]).unwrap();
635 assert_eq!(decoded_meta, meta);
636
637 let legacy_block_metas: Vec<_> = meta
640 .block_metas
641 .iter()
642 .map(|block| {
643 (
644 &block.smallest_key,
645 block.offset,
646 block.len,
647 block.uncompressed_size,
648 block.total_key_count,
649 block.stale_key_count,
650 )
651 })
652 .collect();
653 #[expect(deprecated)]
654 let legacy_meta = (
655 legacy_block_metas,
656 &meta.bloom_filter,
657 meta.estimated_size,
658 meta.key_count,
659 &meta.smallest_key,
660 &meta.largest_key,
661 meta.meta_offset,
662 &meta.monotonic_tombstone_events,
663 meta.version,
664 );
665 let legacy_bytes = bincode::serialize(&legacy_meta).unwrap();
666 assert_eq!(bincode::serialize(&meta).unwrap(), legacy_bytes);
667 let decoded_meta: SstableMeta = bincode::deserialize_from(&mut &legacy_bytes[..]).unwrap();
668 assert_eq!(decoded_meta, meta);
669 }
670
671 #[tokio::test]
672 async fn test_sstable_serde() {
673 use foyer::Code;
674
675 use crate::hummock::SstableMetaCacheValue;
676
677 let (_, meta) = gen_test_sstable_data(
678 default_builder_opt_for_test(),
679 (0..100).clone().map(|x| {
680 (
681 iterator_test_key_of(x),
682 HummockValue::put(format!("overlapped_new_{}", x).as_bytes().to_vec()),
683 )
684 }),
685 )
686 .await;
687
688 for skip_filter in [false, true] {
689 let sstable = Sstable::new(42.into(), meta.clone(), skip_filter);
690 let legacy_bytes = bincode::serialize(&sstable).unwrap();
691 let value = SstableMetaCacheValue::from(sstable);
692 let mut bytes = Vec::new();
693 value.encode(&mut bytes).unwrap();
694
695 assert_eq!(bytes, legacy_bytes);
696 assert_eq!(value.estimated_size(), bytes.len());
697
698 let decoded = SstableMetaCacheValue::decode(&mut legacy_bytes.as_slice()).unwrap();
699 assert_eq!(decoded.id, value.id);
700 assert_eq!(decoded.meta, value.meta);
701 assert!(value.has_filter());
702 if skip_filter {
703 assert!(!decoded.has_filter());
704 } else {
705 assert_eq!(
706 decoded.filter_reader.encode_to_bytes(),
707 value.filter_reader.encode_to_bytes()
708 );
709 }
710 }
711 }
712
713 #[tokio::test]
714 async fn test_sstable_from_meta_bytes() {
715 let (_, meta) = gen_test_sstable_data(
716 default_builder_opt_for_test(),
717 (0..100).map(|x| (iterator_test_key_of(x), HummockValue::put(vec![42]))),
718 )
719 .await;
720 let bytes = meta.encode_to_bytes();
721 for skip in [false, true] {
722 let expected = Sstable::new(42.into(), meta.clone(), skip);
723 let restored = {
724 let temporary = bytes.clone();
725 Sstable::from_meta_bytes(42.into(), &temporary, skip).unwrap()
726 };
727 assert_eq!(restored.id, expected.id);
728 assert_eq!(restored.meta, expected.meta);
729 assert_eq!(
730 restored.filter_reader.encode_to_bytes(),
731 expected.filter_reader.encode_to_bytes()
732 );
733 assert_eq!(
734 bincode::serialize(&restored).unwrap(),
735 bincode::serialize(&expected).unwrap()
736 );
737 }
738 let mut corrupt = bytes;
739 corrupt[0] ^= 1;
740 assert!(SstableMeta::decode(&corrupt).is_err());
741 assert!(Sstable::from_meta_bytes(42.into(), &corrupt, false).is_err());
742 }
743}