1pub mod field_row;
6mod gc;
7pub mod handle;
8pub mod ingest;
9#[cfg(feature = "std")]
10pub(crate) mod links;
11pub(crate) mod overwritten;
12pub(crate) mod released;
13
14#[doc(hidden)]
15pub use gc::{FragmentationEntry, FragmentationMap};
16
17use crate::path::PathBuf;
18use crate::tree::inner::{FlushGuard, VersionsWriteGuard};
19use crate::{
20 Config, Memtable, ScanSinceEvent, SeqNo, TableId, TreeId, UserKey, UserValue,
21 abstract_tree::{AbstractTree, RangeItem},
22 coding::{Decode, Encode},
23 iter_guard::{IterGuard, IterGuardImpl},
24 table::Table,
25 tree::inner::MemtableId,
26 value::InternalValue,
27 version::Version,
28 vlog::{
29 Accessor, BlobFile, BlobFileWriter,
30 blob_file::value_encoder::{Separated, ValueEncoder},
31 },
32};
33use alloc::sync::Arc;
34#[cfg(not(feature = "std"))]
35use alloc::{boxed::Box, string::ToString, vec::Vec};
36use core::ops::RangeBounds;
37use handle::BlobIndirection;
38
39pub struct Guard {
41 tree: crate::BlobTree,
42 version: Version,
43 fill: crate::config::BlobCacheFill,
46 prefetched: Vec<(crate::vlog::ValueHandle, UserValue)>,
49 kv: crate::Result<InternalValue>,
50 prefetch: Option<Arc<ScanPrefetch>>,
53 _held: Option<HeldReadAhead>,
56}
57
58struct HeldReadAhead {
61 state: Arc<ScanPrefetch>,
62 bytes: usize,
63}
64
65impl Drop for HeldReadAhead {
66 fn drop(&mut self) {
67 self.state
68 .held
69 .fetch_sub(self.bytes, core::sync::atomic::Ordering::Relaxed);
70 }
71}
72
73impl IterGuard for Guard {
74 fn into_inner_if(
75 self,
76 pred: impl Fn(&UserKey) -> bool,
77 ) -> crate::Result<(UserKey, Option<UserValue>)> {
78 let kv = self.kv?;
79
80 if pred(&kv.key.user_key) {
89 resolve_value_handle(
90 self.tree.id(),
91 &self.tree.index.config.cache,
92 self.fill,
93 &self.prefetched,
94 &self.version,
95 #[cfg(feature = "metrics")]
96 self.tree.metrics(),
97 kv,
98 )
99 .map(|(k, v)| (k, Some(v)))
100 } else {
101 Ok((kv.key.user_key, None))
102 }
103 }
104
105 fn key(self) -> crate::Result<UserKey> {
106 self.kv.map(|kv| kv.key.user_key)
107 }
108
109 fn size(self) -> crate::Result<u32> {
110 let kv = self.kv?;
111
112 if kv.key.value_type.is_indirection() {
113 let mut cursor = crate::io::Cursor::new(kv.value);
114 Ok(BlobIndirection::decode_from(&mut cursor)?.size)
115 } else if kv.key.value_type.is_cell_row() {
116 field_row::logical_len(&kv.value)
117 } else {
118 #[expect(clippy::cast_possible_truncation, reason = "values are u32 max length")]
119 Ok(kv.value.len() as u32)
120 }
121 }
122
123 fn into_inner(self) -> crate::Result<(UserKey, UserValue)> {
124 if let Some(p) = &self.prefetch {
125 p.note_resolved();
126 }
127 resolve_value_handle(
128 self.tree.id(),
129 &self.tree.index.config.cache,
130 self.fill,
131 &self.prefetched,
132 &self.version,
133 #[cfg(feature = "metrics")]
134 self.tree.metrics(),
135 self.kv?,
136 )
137 }
138}
139
140fn write_encoded_value(
143 ahead: &mut ValueEncoder,
144 blob_writer: &mut BlobFileWriter,
145 table_writer: &mut crate::table::multi_writer::MultiWriter,
146 encoded: &mut Vec<u8>,
147) -> crate::Result<bool> {
148 let Some(next) = ahead.take() else {
149 return Ok(false);
150 };
151 let (separated, value) = next?;
152 let Separated {
153 mut key,
154 size,
155 class,
156 } = separated;
157 blob_writer.select_lifetime_class(class)?;
158 let vhandle = blob_writer.write_encoded(&key.user_key, key.seqno, &value, size)?;
159 let indirection = BlobIndirection { vhandle, size };
160 encoded.clear();
161 indirection.encode_into(encoded)?;
162 key.value_type = crate::ValueType::Indirection;
163 table_writer.write(InternalValue::new(key, UserValue::new(encoded)))?;
164 table_writer.register_blob(indirection);
165 Ok(true)
166}
167
168#[cfg_attr(
172 not(feature = "std"),
173 expect(unused_variables, reason = "without threads the two run in turn")
174)]
175fn finish_flush(
176 blob_writer: BlobFileWriter,
177 table_writer: crate::table::multi_writer::MultiWriter,
178 durability: crate::config::FlushDurability,
179) -> (
180 crate::Result<Vec<BlobFile>>,
181 crate::Result<crate::table::multi_writer::Written>,
182) {
183 #[cfg(feature = "std")]
184 if durability == crate::config::FlushDurability::Synced {
185 return std::thread::scope(|scope| {
186 let blob = scope.spawn(|| blob_writer.finish());
187 let table = table_writer.finish();
188 let blob = blob
189 .join()
190 .unwrap_or_else(|panic| std::panic::resume_unwind(panic));
191 (blob, table)
192 });
193 }
194 (blob_writer.finish(), table_writer.finish())
195}
196
197fn prefetched_value(
200 prefetched: &[(crate::vlog::ValueHandle, UserValue)],
201 vhandle: &crate::vlog::ValueHandle,
202) -> Option<UserValue> {
203 prefetched
204 .iter()
205 .find(|(held, _)| {
206 held.blob_file_id == vhandle.blob_file_id
207 && held.offset == vhandle.offset
208 && held.on_disk_size == vhandle.on_disk_size
209 })
210 .map(|(_, value)| value.clone())
211}
212
213pub(crate) fn resolve_value_handle(
214 tree_id: TreeId,
215 cache: &crate::Cache,
216 fill: crate::config::BlobCacheFill,
217 prefetched: &[(crate::vlog::ValueHandle, UserValue)],
218 version: &Version,
219 #[cfg(feature = "metrics")] metrics: &crate::metrics::Metrics,
220 item: InternalValue,
221) -> RangeItem {
222 if item.key.value_type.is_cell_row() {
223 let accessor = Accessor::new(
224 &version.blob_files,
225 fill,
226 #[cfg(feature = "metrics")]
227 Some(metrics),
228 );
229 let value = field_row::resolve_row(&item.value, |vptr| {
230 if let Some(value) = prefetched_value(prefetched, &vptr.vhandle) {
231 return Ok(value);
232 }
233 accessor
236 .get(tree_id, &item.key.user_key, &vptr.vhandle, cache)?
237 .ok_or(crate::Error::InvalidHeader(
238 "field row: a reference names a blob file the version does not hold",
239 ))
240 })?;
241 return Ok((item.key.user_key, UserValue::from(value)));
242 }
243 if item.key.value_type.is_indirection() {
244 let mut cursor = crate::io::Cursor::new(item.value);
245 let vptr = BlobIndirection::decode_from(&mut cursor)?;
246 if let Some(value) = prefetched_value(prefetched, &vptr.vhandle) {
247 return Ok((item.key.user_key, value));
248 }
249
250 let accessor = Accessor::new(
254 &version.blob_files,
255 fill,
256 #[cfg(feature = "metrics")]
257 Some(metrics),
258 );
259
260 let value = accessor
263 .get(tree_id, &item.key.user_key, &vptr.vhandle, cache)?
264 .ok_or(crate::Error::InvalidHeader(
265 "blob tree: an indirection names a blob file the version does not hold",
266 ))?;
267 Ok((item.key.user_key, value))
268 } else {
269 let k = item.key.user_key;
270 let v = item.value;
271 Ok((k, v))
272 }
273}
274
275pub(crate) enum ValuePlan<'v> {
277 Ready(UserValue),
279 Read(ValueRead<'v>),
281 Alone,
283}
284
285pub(crate) struct ValueRead<'v> {
288 pub(crate) blob_file: &'v BlobFile,
289 pub(crate) vhandle: crate::vlog::ValueHandle,
290 pub(crate) len: usize,
291}
292
293#[derive(Clone)]
296pub(crate) struct BlobSource {
297 pub(crate) tree_id: TreeId,
298 pub(crate) cache: Arc<crate::Cache>,
299 pub(crate) fill: crate::config::BlobCacheFill,
302 #[cfg(feature = "metrics")]
303 pub(crate) metrics: Arc<crate::metrics::Metrics>,
304}
305
306impl BlobSource {
307 pub(crate) fn value(&self, version: &Version, item: InternalValue) -> crate::Result<UserValue> {
310 resolve_value_handle(
311 self.tree_id,
312 &self.cache,
313 self.fill,
314 &[],
315 version,
316 #[cfg(feature = "metrics")]
317 &self.metrics,
318 item,
319 )
320 .map(|(_, value)| value)
321 }
322
323 pub(crate) fn plan_value<'v>(
329 &self,
330 version: &'v Version,
331 item: &InternalValue,
332 ) -> ValuePlan<'v> {
333 if !item.key.value_type.is_indirection() {
334 return ValuePlan::Alone;
335 }
336 let mut cursor = crate::io::Cursor::new(&item.value[..]);
337 let Ok(indirection) = BlobIndirection::decode_from(&mut cursor) else {
338 return ValuePlan::Alone;
339 };
340 let vhandle = indirection.vhandle;
341 if let Some(value) = self
342 .cache
343 .get_blob(self.tree_id, &vhandle, &item.key.user_key)
344 {
345 return ValuePlan::Ready(value);
346 }
347 let (Some(blob_file), Ok(len)) = (
348 version.blob_files.get(vhandle.blob_file_id),
349 crate::vlog::blob_file::reader::record_len(item.key.user_key.len(), &vhandle),
350 ) else {
351 return ValuePlan::Alone;
352 };
353 ValuePlan::Read(ValueRead {
354 blob_file,
355 vhandle,
356 len,
357 })
358 }
359
360 pub(crate) fn open_blob_file(
363 &self,
364 blob_file: &BlobFile,
365 ) -> crate::Result<Arc<dyn crate::fs::FsFile>> {
366 let id = crate::GlobalTableId::from((self.tree_id, blob_file.id()));
367 Ok(blob_file
368 .file_accessor()
369 .get_or_open_blob_file(&id, &blob_file.0.path)?
370 .0)
371 }
372
373 pub(crate) fn finish_value(
382 &self,
383 version: &Version,
384 key: &[u8],
385 read: &ValueRead<'_>,
386 file: &dyn crate::fs::FsFile,
387 bytes: &crate::Slice,
388 ) -> crate::Result<UserValue> {
389 let accessor = Accessor::new(
390 &version.blob_files,
391 self.fill,
392 #[cfg(feature = "metrics")]
393 Some(&self.metrics),
394 );
395 let reader = crate::vlog::blob_file::reader::Reader::new(read.blob_file, file);
396 let mut decoded = 0;
397 let parsed = reader.parse_record(key, &read.vhandle, bytes, &mut decoded);
398 accessor.count_decoded(decoded);
399 let value = parsed?;
400 if self.fill.fills() {
401 let detached = self
402 .cache
403 .insert_blob(self.tree_id, &read.vhandle, key, value.clone());
404 accessor.count_gather(detached);
405 }
406 Ok(value)
407 }
408
409 pub(crate) fn count_issued(&self, version: &Version, read: &ValueRead<'_>) {
411 Accessor::new(
412 &version.blob_files,
413 self.fill,
414 #[cfg(feature = "metrics")]
415 Some(&self.metrics),
416 )
417 .count_request(read.len);
418 }
419
420 pub(crate) fn object(
429 &self,
430 version: &Version,
431 key: &[u8],
432 indirection: &BlobIndirection,
433 ) -> crate::Result<UserValue> {
434 Accessor::new(
435 &version.blob_files,
436 self.fill,
437 #[cfg(feature = "metrics")]
438 Some(&self.metrics),
439 )
440 .get(self.tree_id, key, &indirection.vhandle, &self.cache)?
441 .ok_or(crate::Error::InvalidHeader(
442 "field row: a reference names a blob file the version does not hold",
443 ))
444 }
445
446 #[cfg(feature = "columnar")]
449 pub(crate) fn object_prefetched(
450 &self,
451 version: &Version,
452 key: &[u8],
453 indirection: &BlobIndirection,
454 local: &mut crate::vlog::Prefetched,
455 ) -> crate::Result<UserValue> {
456 match local.take(key, &indirection.vhandle) {
457 Some(object) => Ok(object),
458 None => self.object(version, key, indirection),
459 }
460 }
461
462 #[cfg(feature = "columnar")]
469 pub(crate) fn prefetch(
470 &self,
471 version: &Version,
472 items: &mut Vec<(&[u8], crate::vlog::ValueHandle, usize)>,
473 local: &mut crate::vlog::Prefetched,
474 ) {
475 let into = if self.fill.fills() {
476 crate::vlog::PrefetchInto::Cache
477 } else {
478 crate::vlog::PrefetchInto::Local(local)
479 };
480 Accessor::new(
481 &version.blob_files,
482 self.fill,
483 #[cfg(feature = "metrics")]
484 Some(&self.metrics),
485 )
486 .prefetch(
487 self.tree_id,
488 items,
489 &self.cache,
490 PREFETCH_MAX_GAP,
491 PREFETCH_MAX_READ,
492 into,
493 );
494 }
495}
496
497#[derive(Clone)]
507pub struct BlobTree {
508 #[doc(hidden)]
510 pub index: crate::Tree,
511
512 pub(crate) blobs_folder: Arc<PathBuf>,
513}
514
515impl BlobTree {
516 #[must_use]
518 pub fn downgrade(&self) -> crate::WeakTree {
519 crate::WeakTree::of(&self.index, Some(Arc::clone(&self.blobs_folder)))
520 }
521
522 pub fn close(self) -> crate::TreeClose {
526 crate::close::close(self.index)
527 }
528
529 fn full_compaction_blob_need(&self, version: &crate::version::Version) -> crate::Result<u64> {
534 let Some(blob_opts) = &self.index.config.kv_separation_opts else {
535 return Ok(0);
536 };
537 let all_tables: crate::HashSet<TableId> = version.iter_tables().map(Table::id).collect();
538 crate::compaction::worker::pick_blob_files_to_rewrite(&all_tables, version, blob_opts)?
539 .iter()
540 .try_fold(0u64, |acc, bf| bf.physical_size().map(|size| acc + size))
541 }
542
543 pub(crate) fn open(
547 config: Config,
548 #[cfg(feature = "std")] lock: crate::config::DirectoryLock,
549 names: crate::tree::NewFolderNames,
550 ) -> crate::Result<(Self, Option<crate::tree::Creation>)> {
551 use crate::file::BLOBS_FOLDER;
552
553 let (index, creation) = crate::Tree::open(
554 config,
555 #[cfg(feature = "std")]
556 lock,
557 names,
558 )?;
559
560 let blobs_folder = index.config.path.join(BLOBS_FOLDER);
572 let folder = (|| -> crate::Result<()> {
573 let made = !(*index.config.fs).exists(&blobs_folder)?;
574 (*index.config.fs).create_dir_all(&blobs_folder)?;
575 if made {
576 crate::file::fsync_directory(
577 &index.config.path,
578 &*index.config.fs,
579 index.config.sync_mode,
580 )?;
581 }
582 Ok(())
583 })();
584 if let Err(e) = folder {
587 if let Some(creation) = creation {
588 creation.take_back(index);
589 }
590 return Err(e);
591 }
592
593 let blob_file_id_to_continue_with = index
594 .current_version()
595 .blob_files
596 .list_ids()
597 .max()
598 .map(|x| x + 1)
599 .unwrap_or_default();
600
601 index
602 .0
603 .blob_file_id_counter
604 .set(blob_file_id_to_continue_with);
605
606 Ok((
607 Self {
608 index,
609 blobs_folder: Arc::new(blobs_folder),
610 },
611 creation,
612 ))
613 }
614
615 fn resolve_key(
619 &self,
620 super_version: &crate::version::SuperVersion,
621 source: &BlobSource,
622 key: &[u8],
623 seqno: SeqNo,
624 ) -> crate::Result<Option<UserValue>> {
625 let Some(item) = crate::Tree::get_internal_entry_from_version(
626 super_version,
627 key,
628 seqno,
629 self.index.config.comparator.as_ref(),
630 )?
631 else {
632 return Ok(None);
633 };
634 if item.key.value_type.is_merge_operand()
638 && let Some(merge_operator) = &self.index.config.merge_operator
639 {
640 return crate::Tree::resolve_point_merge(
641 super_version,
642 key,
643 seqno,
644 merge_operator,
645 Some(source),
646 );
647 }
648
649 source.value(&super_version.version, item).map(Some)
650 }
651
652 pub fn scan_since_seqno(
671 &self,
672 target_seqno: SeqNo,
673 ) -> crate::Result<impl Iterator<Item = ScanSinceEvent> + use<>> {
674 let fill = self.blob_cache_fill();
675 self.index
676 .scan_since_seqno_with(target_seqno, true, |version, entry| {
677 let seqno = entry.key.seqno;
678 let (key, value) = resolve_value_handle(
679 self.id(),
680 &self.index.config.cache,
681 fill,
682 &[],
683 version,
684 #[cfg(feature = "metrics")]
685 self.metrics(),
686 entry,
687 )?;
688 Ok(ScanSinceEvent::Insert { key, value, seqno })
689 })
690 }
691
692 pub fn start_multi_get<K: Into<UserKey>>(
701 &self,
702 keys: impl IntoIterator<Item = K>,
703 seqno: SeqNo,
704 ) -> crate::Result<crate::resumable::Step> {
705 let snapshot = self.index.snapshot_for_read(seqno)?;
706 let seqno = crate::Tree::read_ceiling(&snapshot, seqno);
707 let keys: Vec<UserKey> = keys.into_iter().map(Into::into).collect();
708 snapshot
711 .version
712 .read_mix()
713 .note_points(u64::try_from(keys.len()).unwrap_or(u64::MAX));
714 let source = self.blob_source();
717 Ok(crate::resumable::ResumableMultiGet::start(
718 crate::tree::read_job::ReadCtx {
719 super_version: crate::version::SnapshotRef::Owned((*snapshot).clone()),
720 keys,
721 seqno,
722 comparator: Arc::clone(&self.index.config.comparator),
723 merge_operator: self.index.config.merge_operator.clone(),
724 merge_base: Some(source.clone()),
725 values: crate::tree::read_job::Values::Blob(source),
726 metadata_budget: self.index.config.multi_get_metadata_budget,
727 },
728 ))
729 }
730
731 fn blob_cache_fill(&self) -> crate::config::BlobCacheFill {
734 self.index.runtime_config.load().blob_cache_fill
735 }
736
737 fn blob_source(&self) -> BlobSource {
739 BlobSource {
740 tree_id: self.id(),
741 cache: Arc::clone(&self.index.config.cache),
742 fill: self.blob_cache_fill(),
743 #[cfg(feature = "metrics")]
744 metrics: Arc::clone(self.metrics()),
745 }
746 }
747
748 pub fn scan_since_seqno_in_range<K: AsRef<[u8]>, R: core::ops::RangeBounds<K>>(
762 &self,
763 target_seqno: SeqNo,
764 range: R,
765 ) -> crate::Result<impl Iterator<Item = ScanSinceEvent> + use<K, R>> {
766 let bounds = crate::tree::range_to_user_bounds(&range);
767 let fill = self.blob_cache_fill();
768 self.index.scan_since_seqno_scoped(
769 target_seqno,
770 true,
771 |version, entry| {
772 let seqno = entry.key.seqno;
773 let (key, value) = resolve_value_handle(
774 self.id(),
775 &self.index.config.cache,
776 fill,
777 &[],
778 version,
779 #[cfg(feature = "metrics")]
780 self.metrics(),
781 entry,
782 )?;
783 Ok(ScanSinceEvent::Insert { key, value, seqno })
784 },
785 Some(&bounds),
786 )
787 }
788
789 pub fn insert_cells<K: Into<UserKey>>(
845 &self,
846 key: K,
847 fields: &[field_row::Field<'_>],
848 seqno: SeqNo,
849 ) -> crate::Result<(u64, u64)> {
850 let key = key.into();
851 let mut row = Vec::with_capacity(fields.len());
852 for field in fields {
853 let cell = match &field.cell {
854 field_row::Cell::Value(bytes) => field_row::RowCell::Value(bytes),
855 field_row::Cell::Ref(reference) => {
856 if reference.tree != self.id() {
857 return Err(crate::Error::BlobRef("reference read from another tree"));
858 }
859 if !crate::comparator::same_user_key(reference.key, &key) {
860 return Err(crate::Error::BlobRef("reference read from another key"));
861 }
862 field_row::RowCell::Ref {
866 indirection: reference.indirection,
867 owner: false,
868 }
869 }
870 };
871 row.push(field_row::RowField {
872 column: field.column,
873 tag: field.tag,
874 cell,
875 });
876 }
877 field_row::order_fields(&mut row)?;
878 let refs = fields.iter().filter_map(|field| match &field.cell {
879 field_row::Cell::Ref(reference) => Some(reference),
880 field_row::Cell::Value(_) => None,
881 });
882 for (i, a) in refs.clone().enumerate() {
883 if refs.clone().skip(i + 1).any(|b| a == b) {
884 return Err(crate::Error::BlobRef("one object in two fields"));
885 }
886 }
887 let value = field_row::encode_row(&row)?;
888 let sourced: Vec<(BlobIndirection, u64)> = refs
889 .map(|reference| (reference.indirection, reference.source))
890 .collect();
891 #[cfg(feature = "metrics")]
894 let payload = key.len()
895 + fields
896 .iter()
897 .map(|field| match &field.cell {
898 field_row::Cell::Value(bytes) => bytes.len(),
899 field_row::Cell::Ref(_) => 0,
900 })
901 .sum::<usize>();
902 let written = self.index.append_cell_row(
903 InternalValue::from_components(key, value, seqno, crate::ValueType::CellRow),
904 &sourced,
905 )?;
906 #[cfg(feature = "metrics")]
909 self.index.0.metrics.accepted_value(payload);
910 Ok(written)
911 }
912
913 pub fn get_cells<K: AsRef<[u8]>>(
949 &self,
950 key: K,
951 seqno: SeqNo,
952 ) -> crate::Result<Option<field_row::RowCells>> {
953 let read = self.cell_read(seqno)?;
954 read.version.version.read_mix().note_points(1);
955 let Some(item) = crate::Tree::get_internal_entry_from_version(
956 &read.version,
957 key.as_ref(),
958 seqno,
959 self.index.config.comparator.as_ref(),
960 )?
961 else {
962 return Ok(None);
963 };
964 if !item.key.value_type.is_cell_row() {
965 return Err(crate::Error::BlobRef("the key's version is not a cell row"));
966 }
967 Ok(Some(field_row::RowCells {
968 key: item.key.user_key,
969 row: item.value,
970 read: Arc::new(read),
971 }))
972 }
973
974 pub fn range_cells<K: AsRef<[u8]>, R: RangeBounds<K>>(
1017 &self,
1018 range: R,
1019 seqno: SeqNo,
1020 ) -> crate::Result<
1021 Box<dyn DoubleEndedIterator<Item = crate::Result<field_row::RowCells>> + Send + 'static>,
1022 > {
1023 let read = Arc::new(self.cell_read(seqno)?);
1024 let rows = crate::Tree::create_internal_range_with_prefix_hash(
1025 read.version.clone(),
1026 &range,
1027 seqno,
1028 None,
1029 self.index.config.merge_operator.clone(),
1030 self.index.config.comparator.clone(),
1031 None,
1032 Some(self.blob_source()),
1033 );
1034 Ok(Box::new(rows.filter_map(move |item| match item {
1035 Ok(item) if item.key.value_type.is_cell_row() => Some(Ok(field_row::RowCells {
1036 key: item.key.user_key,
1037 row: item.value,
1038 read: Arc::clone(&read),
1039 })),
1040 Ok(_) => None,
1041 Err(e) => Some(Err(e)),
1042 })))
1043 }
1044
1045 fn cell_read(&self, seqno: SeqNo) -> crate::Result<field_row::CellRead> {
1050 let history = self.index.version_history.read();
1051 let version = history.latest_version_ref().clone();
1052 version.check_serves(seqno)?;
1053 let token = history.released().register(version.version.id());
1054 drop(history);
1055 Ok(field_row::CellRead {
1056 tree: self.id(),
1057 version,
1058 source: self.blob_source(),
1059 token,
1060 })
1061 }
1062
1063 #[cfg(feature = "columnar")]
1114 pub fn columnar_scan<R: core::ops::RangeBounds<UserKey>>(
1115 &self,
1116 projection: impl Into<crate::Projection>,
1117 predicate: Option<&crate::table::columnar_predicate::ColumnRangePredicate>,
1118 seqno: SeqNo,
1119 range: R,
1120 ) -> crate::Result<crate::tree::columnar_scan::ColumnarScan> {
1121 self.index
1122 .columnar_scan(projection, predicate, seqno, range)
1123 }
1124
1125 pub fn update_runtime_config<F>(&self, mutator: F) -> crate::Result<()>
1140 where
1141 F: FnOnce(&mut crate::runtime_config::RuntimeConfig),
1142 {
1143 self.index.update_runtime_config(mutator)
1144 }
1145
1146 #[must_use]
1149 pub fn runtime_config(&self) -> Arc<crate::runtime_config::RuntimeConfig> {
1150 self.index.runtime_config()
1151 }
1152
1153 #[cfg(zstd_any)]
1162 pub fn register_zstd_dictionary(
1163 &self,
1164 dict: Arc<crate::compression::ZstdDictionary>,
1165 ) -> crate::Result<()> {
1166 self.index.register_zstd_dictionary(dict)
1167 }
1168}
1169
1170impl crate::abstract_tree::sealed::Sealed for BlobTree {}
1171
1172const PREFETCH_MAX_GAP: u64 = 4 * 1_024;
1177
1178const PREFETCH_MAX_READ: usize = 4 * 1_024 * 1_024;
1182
1183impl BlobTree {
1184 fn scan_prefetch_state(&self, fill: crate::config::BlobCacheFill) -> Arc<ScanPrefetch> {
1187 let window = self
1188 .index
1189 .config
1190 .kv_separation_opts
1191 .as_ref()
1192 .map_or(0, |o| o.scan_prefetch as usize);
1193
1194 Arc::new(ScanPrefetch {
1195 resolving: core::sync::atomic::AtomicBool::new(false),
1196 held: core::sync::atomic::AtomicUsize::new(0),
1197 fill,
1198 window,
1199 max_gap: PREFETCH_MAX_GAP,
1200 max_read: PREFETCH_MAX_READ,
1201 })
1202 }
1203}
1204
1205fn take_prefetched(
1208 local: &mut crate::vlog::Prefetched,
1209 kv: &InternalValue,
1210) -> Vec<(crate::vlog::ValueHandle, UserValue)> {
1211 let key = &kv.key.user_key;
1212 if kv.key.value_type.is_cell_row() {
1213 let Ok(refs) = field_row::row_refs(&kv.value) else {
1214 return Vec::new();
1215 };
1216 return refs
1217 .into_iter()
1218 .filter_map(|(vptr, _)| {
1219 local
1220 .take(key, &vptr.vhandle)
1221 .map(|value| (vptr.vhandle, value))
1222 })
1223 .collect();
1224 }
1225 if !kv.key.value_type.is_indirection() {
1226 return Vec::new();
1227 }
1228 let mut cursor = crate::io::Cursor::new(&kv.value[..]);
1229 let Ok(vptr) = BlobIndirection::decode_from(&mut cursor) else {
1230 return Vec::new();
1231 };
1232 local
1233 .take(key, &vptr.vhandle)
1234 .map(|value| alloc::vec![(vptr.vhandle, value)])
1235 .unwrap_or_default()
1236}
1237
1238fn blob_guard(
1241 tree: &crate::BlobTree,
1242 version: &Version,
1243 fill: crate::config::BlobCacheFill,
1244 item: crate::Result<InternalValue>,
1245) -> IterGuardImpl {
1246 IterGuardImpl::Blob(Guard {
1247 tree: tree.clone(),
1248 version: version.clone(),
1249 fill,
1250 prefetched: Vec::new(),
1251 kv: item,
1252 prefetch: None,
1253 _held: None,
1254 })
1255}
1256
1257pub(crate) struct ScanPrefetch {
1268 resolving: core::sync::atomic::AtomicBool,
1269 held: core::sync::atomic::AtomicUsize,
1273 fill: crate::config::BlobCacheFill,
1276 window: usize,
1278 max_gap: u64,
1280 max_read: usize,
1282}
1283
1284impl ScanPrefetch {
1285 pub(crate) fn note_resolved(&self) {
1287 self.resolving
1288 .store(true, core::sync::atomic::Ordering::Relaxed);
1289 }
1290
1291 fn armed(&self) -> bool {
1295 self.window > 0 && self.resolving.load(core::sync::atomic::Ordering::Relaxed)
1296 }
1297}
1298
1299struct PrefetchScan<I> {
1307 inner: I,
1308 buf: alloc::collections::VecDeque<crate::Result<InternalValue>>,
1310 inner_back_done: bool,
1313 armed: bool,
1317 state: Arc<ScanPrefetch>,
1318 tree: crate::BlobTree,
1319 version: Version,
1320 local: crate::vlog::Prefetched,
1323}
1324
1325impl<I: Iterator<Item = crate::Result<InternalValue>>> PrefetchScan<I> {
1326 fn fill(&mut self) {
1328 self.local.clear();
1331 self.buf.reserve(self.state.window);
1335 for _ in 0..self.state.window {
1336 match self.inner.next() {
1337 Some(item) => self.buf.push_back(item),
1338 None => break,
1339 }
1340 }
1341
1342 let mut handles: Vec<(&[u8], crate::vlog::ValueHandle, usize)> =
1352 Vec::with_capacity(self.buf.len());
1353 for item in &self.buf {
1354 let Ok(kv) = item else { continue };
1355 if kv.key.value_type.is_cell_row() {
1356 if let Ok(refs) = field_row::row_refs(&kv.value) {
1359 for (vptr, _) in refs {
1360 handles.push((&kv.key.user_key, vptr.vhandle, 0));
1361 }
1362 }
1363 continue;
1364 }
1365 if !kv.key.value_type.is_indirection() {
1366 continue;
1367 }
1368 let mut cursor = crate::io::Cursor::new(&kv.value[..]);
1369 let Ok(vptr) = BlobIndirection::decode_from(&mut cursor) else {
1370 continue;
1371 };
1372 handles.push((&kv.key.user_key, vptr.vhandle, 0));
1373 }
1374
1375 if handles.len() < 2 {
1376 return;
1377 }
1378
1379 let accessor = Accessor::new(
1380 &self.version.blob_files,
1381 self.state.fill,
1382 #[cfg(feature = "metrics")]
1383 Some(self.tree.metrics()),
1384 );
1385
1386 let into = if self.state.fill.fills() {
1393 crate::vlog::PrefetchInto::Cache
1394 } else if self.state.held.load(core::sync::atomic::Ordering::Relaxed) >= self.state.max_read
1395 {
1396 return;
1397 } else {
1398 crate::vlog::PrefetchInto::Local(&mut self.local)
1399 };
1400 accessor.prefetch(
1401 self.tree.id(),
1402 &mut handles,
1403 &self.tree.index.config.cache,
1404 self.state.max_gap,
1405 self.state.max_read,
1406 into,
1407 );
1408 }
1409
1410 fn guard(&mut self, item: crate::Result<InternalValue>) -> IterGuardImpl {
1411 let prefetched = match &item {
1412 Ok(kv) if !self.local.is_empty() => take_prefetched(&mut self.local, kv),
1413 _ => Vec::new(),
1414 };
1415 let held = (!prefetched.is_empty()).then(|| {
1416 let bytes = prefetched.iter().map(|(_, value)| value.len()).sum();
1417 self.state
1418 .held
1419 .fetch_add(bytes, core::sync::atomic::Ordering::Relaxed);
1420 HeldReadAhead {
1421 state: Arc::clone(&self.state),
1422 bytes,
1423 }
1424 });
1425 IterGuardImpl::Blob(Guard {
1426 tree: self.tree.clone(),
1427 version: self.version.clone(),
1428 fill: self.state.fill,
1429 prefetched,
1430 _held: held,
1431 kv: item,
1432 prefetch: if self.armed || self.state.window == 0 {
1439 None
1440 } else {
1441 Some(self.state.clone())
1442 },
1443 })
1444 }
1445}
1446
1447impl<I: Iterator<Item = crate::Result<InternalValue>>> Iterator for PrefetchScan<I> {
1448 type Item = IterGuardImpl;
1449
1450 fn next(&mut self) -> Option<Self::Item> {
1451 if self.buf.is_empty() {
1452 if !self.armed {
1453 self.armed = self.state.armed();
1454 }
1455 if self.armed {
1456 self.fill();
1457 }
1458 }
1459
1460 let item = match self.buf.pop_front() {
1461 Some(item) => item,
1462 None => self.inner.next()?,
1463 };
1464 Some(self.guard(item))
1465 }
1466}
1467
1468impl<I: DoubleEndedIterator<Item = crate::Result<InternalValue>>> DoubleEndedIterator
1469 for PrefetchScan<I>
1470{
1471 fn next_back(&mut self) -> Option<Self::Item> {
1472 if !self.armed {
1478 self.armed = self.state.armed();
1479 }
1480 if !self.inner_back_done {
1486 if let Some(item) = self.inner.next_back() {
1487 return Some(self.guard(item));
1488 }
1489 self.inner_back_done = true;
1490 }
1491
1492 let item = self.buf.pop_back()?;
1493 Some(self.guard(item))
1494 }
1495}
1496
1497struct BlobSeekable {
1500 inner: crate::range::SeekableTreeIter,
1501 tree: crate::BlobTree,
1502 version: Version,
1503 fill: crate::config::BlobCacheFill,
1505}
1506
1507impl Iterator for BlobSeekable {
1508 type Item = IterGuardImpl;
1509
1510 fn next(&mut self) -> Option<Self::Item> {
1511 self.inner
1512 .next()
1513 .map(|item| blob_guard(&self.tree, &self.version, self.fill, item))
1514 }
1515}
1516
1517impl DoubleEndedIterator for BlobSeekable {
1518 fn next_back(&mut self) -> Option<Self::Item> {
1519 self.inner
1520 .next_back()
1521 .map(|item| blob_guard(&self.tree, &self.version, self.fill, item))
1522 }
1523}
1524
1525impl crate::iter_guard::SeekableGuardIter for BlobSeekable {
1526 fn seek_to(&mut self, key: &[u8]) {
1527 self.inner.seek_to(key);
1528 }
1529
1530 fn seek_to_for_prev(&mut self, key: &[u8]) {
1531 self.inner.seek_to_for_prev(key);
1532 }
1533
1534 fn peek_key(&mut self) -> Option<crate::Result<crate::UserKey>> {
1535 self.inner.peek_key()
1538 }
1539}
1540
1541impl AbstractTree for BlobTree {
1542 #[cfg(feature = "std")]
1543 fn create_checkpoint(
1544 &self,
1545 target_path: &crate::path::Path,
1546 ) -> crate::Result<crate::CheckpointInfo> {
1547 crate::checkpoint::run_checkpoint(
1548 self,
1549 &crate::checkpoint::CheckpointParams {
1550 target_root: target_path,
1551 target_fs: &self.index.config.fs,
1552 src_root: &self.index.config.path,
1553 src_fs: &self.index.config.fs,
1554 deletion_pause: &self.index.deletion_pause,
1555 visible_seqno: &self.index.config.visible_seqno,
1556 include_blobs: true,
1557 runtime_config: self.index.0.runtime_config.load_full(),
1558 encryption: self.index.0.config.encryption.clone(),
1559 source: &self.index.0,
1560 },
1561 )
1562 }
1563
1564 #[cfg(feature = "std")]
1565 fn pin(&self) -> crate::PinnedVersion {
1566 crate::PinnedVersion::take(&self.index)
1567 }
1568
1569 #[cfg(feature = "std")]
1570 fn acknowledge_repair_report(&self, id: u128) -> crate::Result<bool> {
1571 self.index.acknowledge_repair_report(id)
1572 }
1573
1574 fn print_trace(&self, key: &[u8]) -> crate::Result<()> {
1575 self.index.print_trace(key)
1576 }
1577
1578 fn table_file_cache_size(&self) -> usize {
1579 self.index.table_file_cache_size()
1580 }
1581
1582 fn get_version_history_lock(&self) -> VersionsWriteGuard<'_> {
1583 self.index.get_version_history_lock()
1584 }
1585
1586 fn persist_flushes(&self) -> crate::Result<()> {
1587 self.index.persist_flushes()
1588 }
1589
1590 fn next_table_id(&self) -> TableId {
1591 self.index.next_table_id()
1592 }
1593
1594 fn id(&self) -> crate::TreeId {
1595 self.index.id()
1596 }
1597
1598 fn get_internal_entry(&self, key: &[u8], seqno: SeqNo) -> crate::Result<Option<InternalValue>> {
1599 self.index.get_internal_entry(key, seqno)
1600 }
1601
1602 fn current_version(&self) -> Version {
1603 self.index.current_version()
1604 }
1605
1606 #[cfg(feature = "std")]
1607 fn refresh_table_checksum(
1608 &self,
1609 table_id: crate::TableId,
1610 checksum: crate::checksum::Checksum,
1611 expected_restriction: Option<&crate::UserKey>,
1612 ) -> crate::Result<crate::abstract_tree::ChecksumRefreshOutcome> {
1613 self.index
1616 .refresh_table_checksum(table_id, checksum, expected_restriction)
1617 }
1618
1619 fn sync_mode(&self) -> crate::fs::SyncMode {
1620 self.index.sync_mode()
1622 }
1623
1624 fn wait_for_table_file_refills(&self) {
1625 self.index.wait_for_table_file_refills();
1626 }
1627
1628 fn prefix_extractor(&self) -> Option<alloc::sync::Arc<dyn crate::prefix::PrefixExtractor>> {
1629 self.index.prefix_extractor()
1631 }
1632
1633 fn storage_stats(&self) -> crate::Result<crate::StorageStats> {
1634 let version = self.current_version();
1641 let mut stats = crate::storage_stats::compute_storage_stats(
1642 &version,
1643 self.index.is_compacting(),
1644 false,
1645 )?;
1646 let (capacity, available, compaction_possible) =
1651 self.index.admission_capacity(stats.used_bytes);
1652 stats.capacity_bytes = capacity;
1653 stats.available_bytes = available;
1654 stats.compaction_possible = compaction_possible;
1655 let blob_need = self.full_compaction_blob_need(&version)?;
1662 stats.full_compaction_bytes += blob_need;
1663 if self.index.storage_admission_enabled()
1667 && capacity.is_some()
1668 && stats.status == crate::StorageStatus::Healthy
1669 {
1670 let sst_need = crate::storage_stats::full_compaction_demand_bytes(&version)?;
1678 let sst_dest_level = self.index.config.level_count.saturating_sub(1);
1681 let quota_headroom = self.index.quota_headroom(stats.used_bytes);
1682 let full_fits = crate::compaction::worker::merge_would_fit(
1683 &self.index.compaction_capacity,
1684 self.index.id,
1685 &self.index.config,
1686 quota_headroom,
1687 (sst_need, blob_need),
1688 sst_dest_level,
1689 );
1690 stats.status = if full_fits {
1691 crate::StorageStatus::FullCompactionAvailable
1692 } else {
1693 crate::StorageStatus::TightCompactionAvailable
1694 };
1695 }
1696 if self.index.is_read_only() {
1700 stats.status = crate::StorageStatus::ReadOnlyOutOfSpace;
1701 }
1702 Ok(stats)
1703 }
1704
1705 fn write_admission(&self) -> crate::Result<()> {
1706 self.index.write_admission()
1712 }
1713
1714 fn write_backpressure(
1715 &self,
1716 strategy: &dyn crate::compaction::CompactionStrategy,
1717 ) -> crate::Backpressure {
1718 self.index.write_backpressure(strategy)
1723 }
1724
1725 #[cfg(feature = "metrics")]
1726 fn cache_stats(&self) -> crate::CacheStats {
1727 self.index.cache_stats()
1728 }
1729
1730 #[cfg(feature = "metrics")]
1731 fn metrics(&self) -> &Arc<crate::Metrics> {
1732 self.index.metrics()
1733 }
1734
1735 fn prefix<K: AsRef<[u8]>>(
1736 &self,
1737 prefix: K,
1738 seqno: SeqNo,
1739 index: Option<(Arc<Memtable>, SeqNo)>,
1740 ) -> Box<dyn DoubleEndedIterator<Item = IterGuardImpl> + Send + 'static> {
1741 use crate::prefix::compute_prefix_hash;
1742 use crate::range::prefix_to_range;
1743
1744 let prefix_bytes = prefix.as_ref();
1745
1746 let prefix_hash =
1747 compute_prefix_hash(self.index.config.prefix_extractor.as_ref(), prefix_bytes);
1748
1749 let super_version = match self.index.get_version_for_snapshot(seqno) {
1754 Ok(super_version) => super_version,
1755 Err(e) => return Box::new(core::iter::once(crate::tree::error_guard(e))),
1756 };
1757 let tree = self.clone();
1758 let source = self.blob_source();
1761 let state = self.scan_prefetch_state(source.fill);
1762
1763 let range = prefix_to_range(prefix_bytes);
1764
1765 Box::new(PrefetchScan {
1766 inner: crate::Tree::create_internal_range_with_prefix_hash(
1767 super_version.clone(),
1768 &range,
1769 seqno,
1770 index,
1771 self.index.config.merge_operator.clone(),
1772 self.index.config.comparator.clone(),
1773 prefix_hash,
1774 Some(source),
1775 ),
1776 buf: alloc::collections::VecDeque::new(),
1777 inner_back_done: false,
1778 armed: false,
1779 state,
1780 version: super_version.version,
1781 tree,
1782 local: crate::vlog::Prefetched::default(),
1783 })
1784 }
1785
1786 fn range<K: AsRef<[u8]>, R: RangeBounds<K>>(
1787 &self,
1788 range: R,
1789 seqno: SeqNo,
1790 index: Option<(Arc<Memtable>, SeqNo)>,
1791 ) -> Box<dyn DoubleEndedIterator<Item = IterGuardImpl> + Send + 'static> {
1792 let super_version = match self.index.get_version_for_snapshot(seqno) {
1796 Ok(super_version) => super_version,
1797 Err(e) => return Box::new(core::iter::once(crate::tree::error_guard(e))),
1798 };
1799 let tree = self.clone();
1800 let source = self.blob_source();
1802 let state = self.scan_prefetch_state(source.fill);
1803
1804 Box::new(PrefetchScan {
1805 inner: crate::Tree::create_internal_range_with_prefix_hash(
1806 super_version.clone(),
1807 &range,
1808 seqno,
1809 index,
1810 self.index.config.merge_operator.clone(),
1811 self.index.config.comparator.clone(),
1812 None,
1813 Some(source),
1814 ),
1815 buf: alloc::collections::VecDeque::new(),
1816 inner_back_done: false,
1817 armed: false,
1818 state,
1819 version: super_version.version,
1820 tree,
1821 local: crate::vlog::Prefetched::default(),
1822 })
1823 }
1824
1825 fn range_seekable<K: AsRef<[u8]>, R: RangeBounds<K>>(
1826 &self,
1827 range: R,
1828 seqno: SeqNo,
1829 index: Option<(Arc<Memtable>, SeqNo)>,
1830 ) -> Box<dyn crate::iter_guard::SeekableGuardIter + 'static> {
1831 let (lo, hi) = crate::tree::range_to_user_bounds(&range);
1832 let source = self.blob_source();
1834 let fill = source.fill;
1835 let inner =
1836 match self
1837 .index
1838 .create_seekable_range_bounds_with(lo, hi, seqno, index, Some(source))
1839 {
1840 Ok(inner) => inner,
1841 Err(e) => return Box::new(crate::iter_guard::FailedSeekable::new(e)),
1842 };
1843 let version = inner.version();
1844 Box::new(BlobSeekable {
1845 inner,
1846 tree: self.clone(),
1847 version,
1848 fill,
1849 })
1850 }
1851
1852 fn batch_range_scan<K: AsRef<[u8]>, R: RangeBounds<K> + 'static, I: IntoIterator<Item = R>>(
1853 &self,
1854 intervals: I,
1855 seqno: SeqNo,
1856 index: Option<(Arc<Memtable>, SeqNo)>,
1857 ) -> Box<dyn Iterator<Item = IterGuardImpl> + Send + 'static>
1858 where
1859 I::IntoIter: Send + 'static,
1860 {
1861 let source = self.blob_source();
1863 let state = self.scan_prefetch_state(source.fill);
1864 let inner = match self.index.create_seekable_range_bounds_with(
1865 core::ops::Bound::Unbounded,
1866 core::ops::Bound::Unbounded,
1867 seqno,
1868 index,
1869 Some(source),
1870 ) {
1871 Ok(inner) => inner,
1872 Err(e) => return Box::new(core::iter::once(crate::tree::error_guard(e))),
1873 };
1874 let version = inner.version();
1875 let tree = self.clone();
1876 let intervals = intervals
1877 .into_iter()
1878 .map(|r| crate::tree::range_to_user_bounds(&r));
1879 Box::new(PrefetchScan {
1880 inner: crate::range::BatchRangeScan::new(inner, intervals),
1881 buf: alloc::collections::VecDeque::new(),
1882 inner_back_done: false,
1883 armed: false,
1884 state,
1885 version,
1886 tree,
1887 local: crate::vlog::Prefetched::default(),
1888 })
1889 }
1890
1891 fn tombstone_count(&self) -> u64 {
1892 self.index.tombstone_count()
1893 }
1894
1895 fn weak_tombstone_count(&self) -> u64 {
1896 self.index.weak_tombstone_count()
1897 }
1898
1899 fn weak_tombstone_reclaimable_count(&self) -> u64 {
1900 self.index.weak_tombstone_reclaimable_count()
1901 }
1902
1903 fn clear(&self) -> crate::Result<()> {
1904 let config = self.tree_config();
1905 let mut versions = self.get_version_history_lock();
1906
1907 let prior = versions.latest_version();
1910
1911 #[cfg(zstd_any)]
1914 let still_written = self.index.write_referenced_dict_ids();
1915 #[cfg(not(zstd_any))]
1916 let still_written: Vec<crate::file::DictId> = Vec::new();
1917
1918 versions.upgrade_version(
1919 &config.path,
1920 |v| {
1921 let mut copy = v.clone();
1922 copy.active_memtable = Arc::new(Memtable::new(
1923 self.index.memtable_id_counter.next(),
1924 config.comparator.clone(),
1925 ));
1926 copy.sealed_memtables = Arc::default();
1927 copy.version = v.version.cleared(&still_written);
1928 Ok(copy)
1929 },
1930 &config.seqno,
1931 &config.visible_seqno,
1932 &*config.fs,
1933 self.index.0.runtime_config.load_full(),
1934 self.index.0.config.encryption.clone(),
1935 crate::version::RetentionEffect::DropsData,
1938 )?;
1939
1940 drop(versions);
1945
1946 for table in prior.version.iter_tables() {
1947 table.mark_as_deleted();
1948 }
1949 for blob_file in prior.version.blob_files.iter() {
1950 blob_file.mark_as_deleted();
1951 }
1952
1953 Ok(())
1954 }
1955
1956 fn major_compact(
1957 &self,
1958 target_size: u64,
1959 gc_watermark: SeqNo,
1960 ) -> crate::Result<crate::compaction::CompactionResult> {
1961 self.index.major_compact(target_size, gc_watermark)
1962 }
1963
1964 fn clear_active_memtable(&self) {
1965 self.index.clear_active_memtable();
1966 }
1967
1968 fn l0_run_count(&self) -> usize {
1969 self.index.l0_run_count()
1970 }
1971
1972 fn blob_file_count(&self) -> usize {
1973 self.current_version().blob_file_count()
1974 }
1975
1976 fn size_of<K: AsRef<[u8]>>(&self, key: K, seqno: SeqNo) -> crate::Result<Option<u32>> {
1979 let key = key.as_ref();
1980 let super_version = self.index.snapshot_for_read(seqno)?;
1981 super_version.version.read_mix().note_points(1);
1982 let Some(item) = crate::Tree::get_internal_entry_from_version(
1983 &super_version,
1984 key,
1985 seqno,
1986 self.index.config.comparator.as_ref(),
1987 )?
1988 else {
1989 return Ok(None);
1990 };
1991
1992 if item.key.value_type.is_merge_operand()
1996 && let Some(merge_operator) = &self.index.config.merge_operator
1997 {
1998 #[expect(clippy::cast_possible_truncation, reason = "values are u32 length max")]
1999 return Ok(crate::Tree::resolve_point_merge(
2000 &super_version,
2001 key,
2002 seqno,
2003 merge_operator,
2004 Some(&self.blob_source()),
2005 )?
2006 .map(|value| value.len() as u32));
2007 }
2008
2009 Ok(Some(if item.key.value_type.is_indirection() {
2010 let mut cursor = crate::io::Cursor::new(item.value);
2011 let vptr = BlobIndirection::decode_from(&mut cursor)?;
2012 vptr.size
2013 } else if item.key.value_type.is_cell_row() {
2014 field_row::logical_len(&item.value)?
2015 } else {
2016 #[expect(clippy::cast_possible_truncation, reason = "values are u32 length max")]
2017 {
2018 item.value.len() as u32
2019 }
2020 }))
2021 }
2022
2023 fn stale_blob_bytes(&self) -> u64 {
2024 self.current_version().gc_stats().stale_bytes()
2025 }
2026
2027 fn filter_size(&self) -> u64 {
2028 self.index.filter_size()
2029 }
2030
2031 fn pinned_filter_size(&self) -> usize {
2032 self.index.pinned_filter_size()
2033 }
2034
2035 fn filter_memory(&self) -> crate::FilterMemory {
2036 self.index.filter_memory()
2037 }
2038
2039 fn pinned_block_index_size(&self) -> usize {
2040 self.index.pinned_block_index_size()
2041 }
2042
2043 fn sealed_memtable_count(&self) -> usize {
2044 self.index.sealed_memtable_count()
2045 }
2046
2047 fn get_flush_lock(&self) -> FlushGuard<'_> {
2048 self.index.get_flush_lock()
2049 }
2050
2051 #[expect(clippy::too_many_lines, reason = "flush logic is inherently complex")]
2052 fn flush_to_tables_with_rt(
2053 &self,
2054 stream: impl Iterator<Item = crate::Result<InternalValue>>,
2055 range_tombstones: Vec<crate::range_tombstone::RangeTombstone>,
2056 keys: u64,
2057 hashes: u64,
2058 overwritten: Option<&overwritten::OverwrittenKeys>,
2059 ) -> crate::Result<
2060 Option<(
2061 Vec<Table>,
2062 Option<Vec<BlobFile>>,
2063 crate::runtime_config::WritePin,
2064 )>,
2065 > {
2066 use crate::{coding::Encode, file::BLOBS_FOLDER, table::multi_writer::MultiWriter};
2067
2068 let start = crate::time::Instant::now();
2069
2070 let (table_folder, level_fs) = self.index.config.tables_folder_for_level(0);
2071
2072 let data_block_size = self.index.config.data_block_size_policy.get(0);
2073
2074 let data_block_restart_interval =
2075 self.index.config.data_block_restart_interval_policy.get(0);
2076 let index_block_restart_interval =
2077 self.index.config.index_block_restart_interval_policy.get(0);
2078
2079 let rc = self.index.0.runtime_config.load_full();
2084
2085 let data_block_compression = rc.data_block_compression_policy.get(0);
2086 let index_block_compression = rc.index_block_compression_policy.get(0);
2087
2088 let data_block_hash_ratio = self.index.config.data_block_hash_ratio_policy.get(0);
2089
2090 let index_partitioning = self.index.config.index_block_partitioning_policy.get(0);
2091 let filter_partitioning = self.index.config.filter_block_partitioning_policy.get(0);
2092
2093 log::debug!(
2094 "Flushing memtable(s) and performing key-value separation, data_block_restart_interval={data_block_restart_interval}, index_block_restart_interval={index_block_restart_interval}, data_block_size={data_block_size}, data_block_compression={data_block_compression:?}, index_block_compression={index_block_compression:?}"
2095 );
2096 log::debug!("=> to table(s) in {}", table_folder.display());
2097 log::debug!("=> to blob file(s) at {}", self.blobs_folder.display());
2098
2099 let table_writer = MultiWriter::from_pool(
2100 table_folder.clone(),
2101 Arc::clone(&self.index.table_files),
2102 None,
2103 self.index.config.table_target_size,
2104 0,
2105 level_fs.clone(),
2106 crate::fs::WriteOrigin::FlushTable,
2107 )?;
2108 let recency = table_writer
2112 .first_made_now()
2113 .unwrap_or_else(|| self.index.table_id_counter.next());
2114 let mut table_writer = table_writer
2115 .set_comparator(self.index.config.comparator.clone())
2116 .use_recency(Some(recency))
2117 .use_data_block_restart_interval(data_block_restart_interval)
2118 .use_index_block_restart_interval(index_block_restart_interval)
2119 .use_data_block_compression(data_block_compression)
2120 .use_index_block_compression(index_block_compression)
2121 .use_data_block_size(data_block_size)
2122 .use_index_partition_size(self.index.config.index_block_partition_size_policy.get(0))
2123 .use_filter_partition_size(self.index.config.filter_block_partition_size_policy.get(0))
2124 .use_row_group_size(self.index.config.columnar_row_group_size_policy.get(0))
2125 .use_columnar_page_size(self.index.config.columnar_page_size_policy.get(0))
2126 .use_column_encoding(self.index.config.column_encoding_policy.get(0))
2127 .use_data_block_hash_ratio(data_block_hash_ratio);
2128
2129 let bloom_policy = {
2130 use crate::config::FilterPolicyEntry::{Bloom, None};
2131 use crate::table::filter::BloomConstructionPolicy;
2132
2133 match self.index.config.filter_policy.get(0) {
2134 Bloom(policy) => policy,
2135 None => BloomConstructionPolicy::BitsPerKey(0.0),
2136 }
2137 };
2138 table_writer = table_writer.use_bloom_policy(bloom_policy);
2139 let filter_sizing = self.index.new_data_filter_sizing(
2140 0,
2141 bloom_policy,
2142 crate::filter_budget::FilterCount { keys, hashes },
2143 rc.ecc_scheme,
2144 );
2145 table_writer = table_writer.use_filter_sizing(filter_sizing.clone());
2146
2147 if index_partitioning {
2148 table_writer = table_writer.use_adaptive_index(rc.index_partition_spill_threshold);
2151 }
2152 if filter_partitioning {
2153 table_writer = table_writer.use_partitioned_filter();
2154 }
2155
2156 table_writer =
2157 table_writer.use_prefix_extractor(self.index.config.prefix_extractor.clone());
2158 table_writer = table_writer.use_key_domains(self.index.config.key_domains.clone());
2159 table_writer = table_writer.use_encryption(self.index.config.encryption.clone());
2160 table_writer = table_writer.use_clock(self.index.config.clock.clone());
2161 table_writer = table_writer.use_page_ecc(self.index.config.page_ecc, rc.ecc_scheme);
2165 table_writer = table_writer
2166 .use_sync_mode(self.index.config.sync_mode)
2167 .use_durability(self.index.config.flush_durability)
2168 .use_writeback_bytes(self.index.config.writeback_bytes);
2169 table_writer = table_writer.use_seqno_in_index(rc.seqno_in_index);
2170 table_writer = table_writer.use_zone_map(rc.zone_map);
2171 table_writer = table_writer
2172 .use_columnar(rc.columnar)
2173 .use_cell_rows(rc.columnar);
2174 table_writer = table_writer.use_disable_cow_on_sst(rc.disable_cow_on_sst_files);
2175 table_writer = table_writer.use_kv_checksums(rc.kv_checksums, rc.kv_checksum_algo);
2176 table_writer = table_writer.use_locator(self.index.config.locator_policy.get(0));
2177
2178 #[cfg(zstd_any)]
2181 let dicts = self.index.config.current_zstd_dictionaries();
2182 #[cfg(zstd_any)]
2183 {
2184 table_writer = table_writer
2185 .use_zstd_dictionary(dicts.for_compression(data_block_compression)?)
2186 .use_zstd_two_pass_seed(rc.zstd_two_pass_seed);
2187 }
2188 let write_pin = crate::runtime_config::WritePin::new(&rc).with_filter_sizing(filter_sizing);
2189
2190 #[cfg(feature = "std")]
2196 {
2197 let transform_does_work = data_block_compression != crate::CompressionType::None
2198 || self.index.config.encryption.is_some()
2199 || self.index.config.page_ecc;
2200 if transform_does_work {
2201 table_writer =
2202 table_writer.use_parallel_compression(self.index.config.parallel_compression());
2203 }
2204 }
2205
2206 #[expect(
2207 clippy::expect_used,
2208 reason = "cannot create blob tree without defining kv separation options"
2209 )]
2210 let kv_opts = self
2211 .index
2212 .config
2213 .kv_separation_opts
2214 .as_ref()
2215 .expect("kv separation options should exist");
2216
2217 let mut blob_writer = {
2218 let w = BlobFileWriter::new_for(
2219 self.index.0.blob_file_id_counter.clone(),
2220 self.index.config.path.join(BLOBS_FOLDER),
2221 self.id(),
2222 self.index.config.descriptor_table.clone(),
2223 self.index.config.fs.clone(),
2224 crate::fs::WriteOrigin::FlushBlob,
2225 )?
2226 .use_target_size(kv_opts.file_target_size)
2227 .use_compression(rc.blob_compression)
2228 .use_sync_mode(self.index.config.sync_mode)
2229 .use_durability(self.index.config.flush_durability)
2230 .use_writeback_bytes(self.index.config.writeback_bytes)
2231 .use_lifetime_groups(kv_opts.lifetime_groups)
2232 .use_clock(self.index.config.clock.clone())
2233 .use_key_domains(
2234 self.index.config.key_domains.clone(),
2235 self.index.config.comparator.clone(),
2236 );
2237 #[cfg(zstd_any)]
2238 let w = w
2239 .use_zstd_dictionary(dicts.for_compression(rc.blob_compression)?)
2240 .use_zstd_two_pass_seed(rc.zstd_two_pass_seed)
2241 .use_zstd_dictionaries(dicts);
2242 w
2243 };
2244
2245 let separation_threshold = kv_opts.separation_threshold;
2246 let lifetime = kv_opts.lifetime_classifier();
2247 let comparator = &self.index.config.comparator;
2248
2249 table_writer.set_range_tombstones(range_tombstones);
2253
2254 let mut encoded = Vec::new();
2256
2257 #[cfg(all(feature = "std", zstd_any))]
2260 let mut ahead = if self.index.config.key_domains.is_none() {
2261 ValueEncoder::for_flush(
2262 self.index.config.parallel_compression(),
2263 rc.blob_compression,
2264 rc.zstd_two_pass_seed,
2265 )
2266 } else {
2267 None
2268 };
2269 #[cfg(not(all(feature = "std", zstd_any)))]
2270 let mut ahead: Option<ValueEncoder> = None;
2271
2272 for item in stream {
2273 let item = item?;
2274
2275 let overwritten = overwritten
2278 .is_some_and(|keys| keys.take_before_and_check(&item.key.user_key, comparator));
2279
2280 if let Some(ahead) = &mut ahead {
2281 #[expect(clippy::cast_possible_truncation, reason = "values are u32 length max")]
2282 let size = item.value.len() as u32;
2283 if item.key.value_type == crate::ValueType::Value && size >= separation_threshold {
2284 if ahead.is_full() {
2285 write_encoded_value(
2286 ahead,
2287 &mut blob_writer,
2288 &mut table_writer,
2289 &mut encoded,
2290 )?;
2291 }
2292 let class = lifetime.class_of(&item.key.user_key, overwritten);
2293 let separated = Separated {
2294 key: item.key,
2295 size,
2296 class,
2297 };
2298 ahead.submit(separated, item.value);
2299 continue;
2300 }
2301 while write_encoded_value(ahead, &mut blob_writer, &mut table_writer, &mut encoded)?
2303 {
2304 }
2305 }
2306
2307 if item.is_tombstone() {
2308 table_writer.write(InternalValue::new(item.key, UserValue::empty()))?;
2311 continue;
2312 }
2313
2314 let value = item.value;
2315
2316 if item.key.value_type.is_cell_row() {
2317 let class = lifetime.class_of(&item.key.user_key, overwritten);
2318 let row = field_row::separate_row(
2321 &value,
2322 |column| kv_opts.cell_threshold(column),
2323 |bytes| {
2324 blob_writer.select_lifetime_class(class)?;
2325 blob_writer.write(&item.key.user_key, item.key.seqno, bytes)
2326 },
2327 )?
2328 .map_or(value, UserValue::from);
2329 table_writer.write(InternalValue::new(item.key, row.clone()))?;
2330 table_writer.register_cell_row(&row)?;
2331 continue;
2332 }
2333
2334 #[expect(clippy::cast_possible_truncation, reason = "values are u32 length max")]
2335 let value_size = value.len() as u32;
2336
2337 if item.key.value_type == crate::ValueType::Value && value_size >= separation_threshold
2340 {
2341 blob_writer
2342 .select_lifetime_class(lifetime.class_of(&item.key.user_key, overwritten))?;
2343 let vhandle = blob_writer.write(&item.key.user_key, item.key.seqno, &value)?;
2344
2345 let indirection = BlobIndirection {
2346 vhandle,
2347 size: value_size,
2348 };
2349
2350 encoded.clear();
2351 indirection.encode_into(&mut encoded)?;
2352 table_writer.write({
2353 let mut vptr = InternalValue::new(item.key, UserValue::new(&encoded));
2354 vptr.key.value_type = crate::ValueType::Indirection;
2355 vptr
2356 })?;
2357
2358 table_writer.register_blob(indirection);
2359 } else {
2360 table_writer.write(InternalValue::new(item.key, value))?;
2361 }
2362 }
2363
2364 if let Some(ahead) = &mut ahead {
2365 while write_encoded_value(ahead, &mut blob_writer, &mut table_writer, &mut encoded)? {}
2366 }
2367
2368 let (blob_files, written) = finish_flush(
2369 blob_writer,
2370 table_writer,
2371 self.index.config.flush_durability,
2372 );
2373 let blob_files = blob_files?;
2374 let written = written?;
2375 let entries_durable = written.entries_durable;
2376
2377 log::debug!("Flushed memtable(s) in {:?}", start.elapsed());
2378
2379 let pin_filter = self.index.config.filter_block_pinning_policy.get(0);
2380 let pin_index = self.index.config.index_block_pinning_policy.get(0);
2381
2382 let tables = written
2384 .tables
2385 .into_iter()
2386 .map(|(table_id, checksum)| -> crate::Result<Table> {
2387 let mut params = crate::table::RecoverParams::new(
2388 table_folder.join(table_id.to_string()),
2389 checksum,
2390 table_id,
2391 level_fs.clone(),
2392 self.index.config.comparator.clone(),
2393 self.index.config.cache.clone(),
2394 );
2395 params.tree_id = self.index.id;
2396 params
2397 .descriptor_table
2398 .clone_from(&self.index.config.descriptor_table);
2399 params.pin_filter = pin_filter;
2400 params.pin_index = pin_index;
2401 params.encryption.clone_from(&self.index.config.encryption);
2402 params
2403 .prefix_extractor
2404 .clone_from(&self.index.config.prefix_extractor);
2405 #[cfg(zstd_any)]
2406 {
2407 params.zstd_dictionaries = self.index.config.current_zstd_dictionaries();
2408 }
2409 #[cfg(feature = "metrics")]
2410 {
2411 params.metrics = self.index.metrics.clone();
2412 }
2413 let table = Table::recover(params)?;
2414 if entries_durable {
2415 table.mark_entry_durable();
2416 }
2417 Ok(table)
2418 })
2419 .collect::<crate::Result<Vec<_>>>()?;
2420
2421 Ok(Some((tables, Some(blob_files), write_pin)))
2425 }
2426
2427 fn register_tables(
2428 &self,
2429 tables: &[Table],
2430 blob_files: Option<&[BlobFile]>,
2431 frag_map: Option<FragmentationMap>,
2432 sealed_memtables_to_delete: &[MemtableId],
2433 gc_watermark: SeqNo,
2434 collected_below_watermark: bool,
2435 ) -> crate::Result<()> {
2436 self.index.register_tables(
2437 tables,
2438 blob_files,
2439 frag_map,
2440 sealed_memtables_to_delete,
2441 gc_watermark,
2442 collected_below_watermark,
2443 )
2444 }
2445
2446 fn compact(
2447 &self,
2448 strategy: Arc<dyn crate::compaction::CompactionStrategy>,
2449 gc_watermark: SeqNo,
2450 ) -> crate::Result<crate::compaction::CompactionResult> {
2451 self.index.compact(strategy, gc_watermark)
2452 }
2453
2454 fn get_next_table_id(&self) -> TableId {
2455 self.index.get_next_table_id()
2456 }
2457
2458 fn tree_config(&self) -> &Config {
2459 &self.index.config
2460 }
2461
2462 fn get_highest_seqno(&self) -> Option<SeqNo> {
2463 self.index.get_highest_seqno()
2464 }
2465
2466 fn active_memtable(&self) -> Arc<Memtable> {
2467 self.index.active_memtable()
2468 }
2469
2470 fn rotate_memtable(&self) -> Option<Arc<Memtable>> {
2471 self.index.rotate_memtable()
2472 }
2473
2474 fn table_count(&self) -> usize {
2475 self.index.table_count()
2476 }
2477
2478 fn level_table_count(&self, idx: usize) -> Option<usize> {
2479 self.index.level_table_count(idx)
2480 }
2481
2482 fn approximate_len(&self) -> usize {
2483 self.index.approximate_len()
2484 }
2485
2486 fn is_empty(&self, seqno: SeqNo, index: Option<(Arc<Memtable>, SeqNo)>) -> crate::Result<bool> {
2489 self.index.is_empty(seqno, index)
2490 }
2491
2492 fn contains_key<K: AsRef<[u8]>>(&self, key: K, seqno: SeqNo) -> crate::Result<bool> {
2495 self.index.contains_key(key, seqno)
2496 }
2497
2498 fn contains_prefix<K: AsRef<[u8]>>(
2502 &self,
2503 prefix: K,
2504 seqno: SeqNo,
2505 index: Option<(Arc<Memtable>, SeqNo)>,
2506 ) -> crate::Result<bool> {
2507 self.index.contains_prefix(prefix, seqno, index)
2508 }
2509
2510 fn len(&self, seqno: SeqNo, index: Option<(Arc<Memtable>, SeqNo)>) -> crate::Result<usize> {
2513 self.index.len(seqno, index)
2514 }
2515
2516 fn disk_space(&self) -> u64 {
2517 let version = self.current_version();
2518 self.index.disk_space() + version.blob_files.on_disk_size()
2519 }
2520
2521 fn approximate_range_stats<K: AsRef<[u8]>, R: core::ops::RangeBounds<K>>(
2522 &self,
2523 range: R,
2524 seqno: SeqNo,
2525 ) -> crate::Result<crate::ApproximateRangeStats> {
2526 self.index.approximate_range_stats(range, seqno)
2537 }
2538
2539 fn approximate_range_cardinality<K: AsRef<[u8]>, R: core::ops::RangeBounds<K>>(
2540 &self,
2541 range: R,
2542 seqno: SeqNo,
2543 ) -> crate::Result<crate::RangeCardinality> {
2544 self.index.approximate_range_cardinality(range, seqno)
2548 }
2549
2550 fn get_highest_memtable_seqno(&self) -> Option<SeqNo> {
2551 self.index.get_highest_memtable_seqno()
2552 }
2553
2554 fn get_highest_persisted_seqno(&self) -> Option<SeqNo> {
2555 self.index.get_highest_persisted_seqno()
2556 }
2557
2558 fn oldest_retained_seqno(&self) -> SeqNo {
2559 self.index.oldest_retained_seqno()
2561 }
2562
2563 fn retention_floor(&self) -> SeqNo {
2564 self.index.retention_floor()
2565 }
2566
2567 fn lower_retention_floor(&self, floor: SeqNo) -> crate::Result<()> {
2568 self.index.lower_retention_floor(floor)
2570 }
2571
2572 fn compaction_rate_limiter(&self) -> alloc::sync::Arc<crate::rate_limiter::RateLimiter> {
2573 self.index.compaction_rate_limiter()
2575 }
2576
2577 fn write_rate_limiter(&self) -> alloc::sync::Arc<crate::rate_limiter::RateLimiter> {
2578 self.index.write_rate_limiter()
2580 }
2581
2582 fn apply_batch(&self, batch: crate::WriteBatch, seqno: SeqNo) -> crate::Result<(u64, u64)> {
2583 self.index.apply_batch(batch, seqno)
2584 }
2585
2586 fn insert<K: Into<UserKey>, V: Into<UserValue>>(
2587 &self,
2588 key: K,
2589 value: V,
2590 seqno: SeqNo,
2591 ) -> (u64, u64) {
2592 self.index.insert(key, value.into(), seqno)
2593 }
2594
2595 fn get<K: AsRef<[u8]>>(&self, key: K, seqno: SeqNo) -> crate::Result<Option<crate::UserValue>> {
2596 let super_version = self.index.snapshot_for_read(seqno)?;
2601 super_version.version.read_mix().note_points(1);
2602 self.resolve_key(&super_version, &self.blob_source(), key.as_ref(), seqno)
2603 }
2604
2605 fn multi_get<K: AsRef<[u8]>>(
2606 &self,
2607 keys: impl IntoIterator<Item = K>,
2608 seqno: SeqNo,
2609 ) -> crate::Result<Vec<Option<crate::UserValue>>> {
2610 let super_version = self.index.snapshot_for_read(seqno)?;
2614
2615 let keys: Vec<_> = keys.into_iter().collect();
2616 let n = keys.len();
2617 if n == 0 {
2618 return Ok(Vec::new());
2619 }
2620
2621 if n <= 2 {
2623 super_version
2624 .version
2625 .read_mix()
2626 .note_points(u64::try_from(n).unwrap_or(u64::MAX));
2627 let source = self.blob_source();
2628 return keys
2629 .iter()
2630 .map(|key| self.resolve_key(&super_version, &source, key.as_ref(), seqno))
2631 .collect();
2632 }
2633
2634 let source = self.blob_source();
2638 crate::Tree::read_many(&crate::tree::read_job::ReadCtx {
2639 super_version,
2640 keys,
2641 seqno,
2642 comparator: Arc::clone(&self.index.config.comparator),
2643 merge_operator: self.index.config.merge_operator.clone(),
2644 merge_base: Some(source.clone()),
2645 values: crate::tree::read_job::Values::Blob(source),
2646 metadata_budget: self.index.config.multi_get_metadata_budget,
2647 })
2648 }
2649
2650 fn merge<K: Into<UserKey>, V: Into<UserValue>>(
2651 &self,
2652 key: K,
2653 operand: V,
2654 seqno: SeqNo,
2655 ) -> (u64, u64) {
2656 self.index.merge(key, operand, seqno)
2657 }
2658
2659 fn remove<K: Into<UserKey>>(&self, key: K, seqno: SeqNo) -> (u64, u64) {
2660 self.index.remove(key, seqno)
2661 }
2662
2663 fn remove_weak<K: Into<UserKey>>(&self, key: K, seqno: SeqNo) -> (u64, u64) {
2664 self.index.remove_weak(key, seqno)
2665 }
2666
2667 fn remove_range<K: Into<UserKey>>(&self, start: K, end: K, seqno: SeqNo) -> u64 {
2668 self.index.remove_range(start, end, seqno)
2669 }
2670}
2671
2672#[cfg(test)]
2673mod tests;