Skip to main content

rust_rocksdb/
db_options.rs

1// Copyright 2020 Tyler Neely
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::panic::{AssertUnwindSafe, catch_unwind};
16use std::path::Path;
17use std::ptr::{NonNull, null_mut};
18use std::slice;
19use std::sync::Arc;
20
21use libc::{self, c_char, c_double, c_int, c_uchar, c_uint, c_void, size_t};
22
23use crate::cache::Cache;
24use crate::column_family::ColumnFamilyTtl;
25use crate::event_listener::{EventListener, new_event_listener};
26use crate::ffi_util::from_cstr_and_free;
27use crate::sst_file_manager::SstFileManager;
28use crate::statistics::{Histogram, HistogramData, StatsLevel};
29use crate::write_buffer_manager::WriteBufferManager;
30use crate::{
31    ColumnFamilyDescriptor, Error, SnapshotWithThreadMode,
32    compaction_filter::{self, CompactionFilterCallback, CompactionFilterFn},
33    compaction_filter_factory::{self, CompactionFilterFactory},
34    comparator::{
35        ComparatorCallback, ComparatorWithTsCallback, CompareFn, CompareTsFn, CompareWithoutTsFn,
36    },
37    db::DBAccess,
38    env::Env,
39    ffi,
40    ffi_util::{CStrLike, to_cpath},
41    merge_operator::{
42        self, MergeFn, MergeOperatorCallback, full_merge_callback, partial_merge_callback,
43    },
44    slice_transform::SliceTransform,
45    statistics::Ticker,
46};
47
48// must be Send and Sync because it will be called by RocksDB from different threads
49type LogCallbackFn = dyn Fn(LogLevel, &str) + 'static + Send + Sync;
50
51/// Type for log callbacks used by [`Options::set_info_logger`]. Use Box to pass a thin pointer to
52/// the C callback.
53type LoggerCallback = Box<dyn Fn(LogLevel, &str) + Sync + Send>;
54
55// Holds a log callback to ensure it outlives any Options and DBs that use it.
56struct LogCallback {
57    callback: Box<LogCallbackFn>,
58}
59
60/// Options that must outlive the DB, and may be shared between DBs. This is cloned and stored
61/// with every DB that is created from the options.
62#[derive(Default)]
63pub(crate) struct OptionsMustOutliveDB {
64    env: Option<Env>,
65    row_cache: Option<Cache>,
66    blob_cache: Option<Cache>,
67    block_based: Option<BlockBasedOptionsMustOutliveDB>,
68    write_buffer_manager: Option<WriteBufferManager>,
69    sst_file_manager: Option<SstFileManager>,
70    log_callback: Option<Arc<LogCallback>>,
71    comparator: Option<Arc<OwnedComparator>>,
72    compaction_filter: Option<Arc<OwnedCompactionFilter>>,
73    logger_callback: Option<Arc<LoggerCallback>>,
74}
75
76impl OptionsMustOutliveDB {
77    pub(crate) fn clone(&self) -> Self {
78        Self {
79            env: self.env.clone(),
80            row_cache: self.row_cache.clone(),
81            blob_cache: self.blob_cache.clone(),
82            block_based: self
83                .block_based
84                .as_ref()
85                .map(BlockBasedOptionsMustOutliveDB::clone),
86            write_buffer_manager: self.write_buffer_manager.clone(),
87            sst_file_manager: self.sst_file_manager.clone(),
88            log_callback: self.log_callback.clone(),
89            comparator: self.comparator.clone(),
90            compaction_filter: self.compaction_filter.clone(),
91            logger_callback: self.logger_callback.clone(),
92        }
93    }
94}
95
96/// Stores a `rocksdb_comparator_t` and destroys it when dropped.
97///
98/// This has an unsafe implementation of Send and Sync because it wraps a RocksDB pointer that
99/// is safe to share between threads.
100struct OwnedComparator {
101    inner: NonNull<ffi::rocksdb_comparator_t>,
102}
103
104impl OwnedComparator {
105    fn new(inner: NonNull<ffi::rocksdb_comparator_t>) -> Self {
106        Self { inner }
107    }
108}
109
110impl Drop for OwnedComparator {
111    fn drop(&mut self) {
112        unsafe {
113            ffi::rocksdb_comparator_destroy(self.inner.as_ptr());
114        }
115    }
116}
117
118/// Stores a `rocksdb_compactionfilter_t` and destroys it when dropped.
119///
120/// This has an unsafe implementation of Send and Sync because it wraps a RocksDB pointer that
121/// is safe to share between threads.
122struct OwnedCompactionFilter {
123    inner: NonNull<ffi::rocksdb_compactionfilter_t>,
124}
125
126impl OwnedCompactionFilter {
127    fn new(inner: NonNull<ffi::rocksdb_compactionfilter_t>) -> Self {
128        Self { inner }
129    }
130}
131
132impl Drop for OwnedCompactionFilter {
133    fn drop(&mut self) {
134        unsafe {
135            ffi::rocksdb_compactionfilter_destroy(self.inner.as_ptr());
136        }
137    }
138}
139
140#[derive(Default)]
141struct BlockBasedOptionsMustOutliveDB {
142    block_cache: Option<Cache>,
143}
144
145impl BlockBasedOptionsMustOutliveDB {
146    fn clone(&self) -> Self {
147        Self {
148            block_cache: self.block_cache.clone(),
149        }
150    }
151}
152
153/// Database-wide options around performance and behavior.
154///
155/// Please read the official tuning [guide](https://github.com/facebook/rocksdb/wiki/RocksDB-Tuning-Guide)
156/// and most importantly, measure performance under realistic workloads with realistic hardware.
157///
158/// # Examples
159///
160/// ```
161/// use rust_rocksdb::{Options, DB};
162/// use rust_rocksdb::DBCompactionStyle;
163///
164/// fn badly_tuned_for_somebody_elses_disk() -> DB {
165///    let path = "path/for/rocksdb/storageX";
166///    let mut opts = Options::default();
167///    opts.create_if_missing(true);
168///    opts.set_max_open_files(10000);
169///    opts.set_use_fsync(false);
170///    opts.set_bytes_per_sync(8388608);
171///    opts.optimize_for_point_lookup(1024);
172///    opts.set_table_cache_num_shard_bits(6);
173///    opts.set_max_write_buffer_number(32);
174///    opts.set_write_buffer_size(536870912);
175///    opts.set_target_file_size_base(1073741824);
176///    opts.set_min_write_buffer_number_to_merge(4);
177///    opts.set_level_zero_stop_writes_trigger(2000);
178///    opts.set_level_zero_slowdown_writes_trigger(0);
179///    opts.set_compaction_style(DBCompactionStyle::Universal);
180///    opts.set_disable_auto_compactions(true);
181///
182///    DB::open(&opts, path).unwrap()
183/// }
184/// ```
185pub struct Options {
186    pub(crate) inner: *mut ffi::rocksdb_options_t,
187    pub(crate) outlive: OptionsMustOutliveDB,
188}
189
190/// Optionally disable WAL or sync for this write.
191///
192/// # Examples
193///
194/// Making an unsafe write of a batch:
195///
196/// ```
197/// use rust_rocksdb::{DB, Options, WriteBatch, WriteOptions};
198///
199/// let tempdir = tempfile::Builder::new()
200///     .prefix("_path_for_rocksdb_storageY1")
201///     .tempdir()
202///     .expect("Failed to create temporary path for the _path_for_rocksdb_storageY1");
203/// let path = tempdir.path();
204/// {
205///     let db = DB::open_default(path).unwrap();
206///     let mut batch = WriteBatch::default();
207///     batch.put(b"my key", b"my value");
208///     batch.put(b"key2", b"value2");
209///     batch.put(b"key3", b"value3");
210///
211///     let mut write_options = WriteOptions::default();
212///     write_options.set_sync(false);
213///     write_options.disable_wal(true);
214///
215///     db.write_opt(&batch, &write_options);
216/// }
217/// let _ = DB::destroy(&Options::default(), path);
218/// ```
219pub struct WriteOptions {
220    pub(crate) inner: *mut ffi::rocksdb_writeoptions_t,
221}
222
223pub struct LruCacheOptions {
224    pub(crate) inner: *mut ffi::rocksdb_lru_cache_options_t,
225}
226
227/// Optionally wait for the memtable flush to be performed.
228///
229/// # Examples
230///
231/// Manually flushing the memtable:
232///
233/// ```
234/// use rust_rocksdb::{DB, Options, FlushOptions};
235///
236/// let tempdir = tempfile::Builder::new()
237///     .prefix("_path_for_rocksdb_storageY2")
238///     .tempdir()
239///     .expect("Failed to create temporary path for the _path_for_rocksdb_storageY2");
240/// let path = tempdir.path();
241/// {
242///     let db = DB::open_default(path).unwrap();
243///
244///     let mut flush_options = FlushOptions::default();
245///     flush_options.set_wait(true);
246///
247///     db.flush_opt(&flush_options);
248/// }
249/// let _ = DB::destroy(&Options::default(), path);
250/// ```
251pub struct FlushOptions {
252    pub(crate) inner: *mut ffi::rocksdb_flushoptions_t,
253}
254
255/// For configuring block-based file storage.
256pub struct BlockBasedOptions {
257    pub(crate) inner: *mut ffi::rocksdb_block_based_table_options_t,
258    outlive: BlockBasedOptionsMustOutliveDB,
259}
260
261pub struct ReadOptions {
262    pub(crate) inner: *mut ffi::rocksdb_readoptions_t,
263    // The `ReadOptions` owns a copy of the timestamp and iteration bounds.
264    // This is necessary to ensure the pointers we pass over the FFI live as
265    // long as the `ReadOptions`. This way, when performing the read operation,
266    // the pointers are guaranteed to be valid.
267    timestamp: Option<Vec<u8>>,
268    iter_start_ts: Option<Vec<u8>>,
269    iterate_upper_bound: Option<Vec<u8>>,
270    iterate_lower_bound: Option<Vec<u8>>,
271}
272
273/// Configuration of cuckoo-based storage.
274pub struct CuckooTableOptions {
275    pub(crate) inner: *mut ffi::rocksdb_cuckoo_table_options_t,
276}
277
278/// For configuring external files ingestion.
279///
280/// # Examples
281///
282/// Move files instead of copying them:
283///
284/// ```
285/// use rust_rocksdb::{DB, IngestExternalFileOptions, SstFileWriter, Options};
286///
287/// let writer_opts = Options::default();
288/// let mut writer = SstFileWriter::create(&writer_opts);
289/// let tempdir = tempfile::Builder::new()
290///     .tempdir()
291///     .expect("Failed to create temporary folder for the _path_for_sst_file");
292/// let path1 = tempdir.path().join("_path_for_sst_file");
293/// writer.open(path1.clone()).unwrap();
294/// writer.put(b"k1", b"v1").unwrap();
295/// writer.finish().unwrap();
296///
297/// let tempdir2 = tempfile::Builder::new()
298///     .prefix("_path_for_rocksdb_storageY3")
299///     .tempdir()
300///     .expect("Failed to create temporary path for the _path_for_rocksdb_storageY3");
301/// let path2 = tempdir2.path();
302/// {
303///   let db = DB::open_default(&path2).unwrap();
304///   let mut ingest_opts = IngestExternalFileOptions::default();
305///   ingest_opts.set_move_files(true);
306///   db.ingest_external_file_opts(&ingest_opts, vec![path1]).unwrap();
307/// }
308/// let _ = DB::destroy(&Options::default(), path2);
309/// ```
310pub struct IngestExternalFileOptions {
311    pub(crate) inner: *mut ffi::rocksdb_ingestexternalfileoptions_t,
312}
313
314// Safety note: auto-implementing Send on most db-related types is prevented by the inner FFI
315// pointer. In most cases, however, this pointer is Send-safe because it is never aliased and
316// rocksdb internally does not rely on thread-local information for its user-exposed types.
317unsafe impl Send for Options {}
318unsafe impl Send for WriteOptions {}
319unsafe impl Send for LruCacheOptions {}
320unsafe impl Send for FlushOptions {}
321unsafe impl Send for BlockBasedOptions {}
322unsafe impl Send for CuckooTableOptions {}
323unsafe impl Send for ReadOptions {}
324unsafe impl Send for IngestExternalFileOptions {}
325unsafe impl Send for CompactOptions {}
326unsafe impl Send for ImportColumnFamilyOptions {}
327unsafe impl Send for OwnedComparator {}
328unsafe impl Send for OwnedCompactionFilter {}
329
330// Sync is similarly safe for many types because they do not expose interior mutability, and their
331// use within the rocksdb library is generally behind a const reference
332unsafe impl Sync for Options {}
333unsafe impl Sync for WriteOptions {}
334unsafe impl Sync for LruCacheOptions {}
335unsafe impl Sync for FlushOptions {}
336unsafe impl Sync for BlockBasedOptions {}
337unsafe impl Sync for CuckooTableOptions {}
338unsafe impl Sync for ReadOptions {}
339unsafe impl Sync for IngestExternalFileOptions {}
340unsafe impl Sync for CompactOptions {}
341unsafe impl Sync for ImportColumnFamilyOptions {}
342unsafe impl Sync for OwnedComparator {}
343unsafe impl Sync for OwnedCompactionFilter {}
344
345impl Drop for Options {
346    fn drop(&mut self) {
347        unsafe {
348            ffi::rocksdb_options_destroy(self.inner);
349        }
350    }
351}
352
353impl Clone for Options {
354    fn clone(&self) -> Self {
355        let inner = unsafe { ffi::rocksdb_options_create_copy(self.inner) };
356        assert!(!inner.is_null(), "Could not copy RocksDB options");
357
358        Self {
359            inner,
360            outlive: self.outlive.clone(),
361        }
362    }
363}
364
365impl Drop for BlockBasedOptions {
366    fn drop(&mut self) {
367        unsafe {
368            ffi::rocksdb_block_based_options_destroy(self.inner);
369        }
370    }
371}
372
373impl Drop for CuckooTableOptions {
374    fn drop(&mut self) {
375        unsafe {
376            ffi::rocksdb_cuckoo_options_destroy(self.inner);
377        }
378    }
379}
380
381impl Drop for FlushOptions {
382    fn drop(&mut self) {
383        unsafe {
384            ffi::rocksdb_flushoptions_destroy(self.inner);
385        }
386    }
387}
388
389impl Drop for WriteOptions {
390    fn drop(&mut self) {
391        unsafe {
392            ffi::rocksdb_writeoptions_destroy(self.inner);
393        }
394    }
395}
396
397impl Drop for LruCacheOptions {
398    fn drop(&mut self) {
399        unsafe {
400            ffi::rocksdb_lru_cache_options_destroy(self.inner);
401        }
402    }
403}
404
405impl Drop for ReadOptions {
406    fn drop(&mut self) {
407        unsafe {
408            ffi::rocksdb_readoptions_destroy(self.inner);
409        }
410    }
411}
412
413impl Drop for IngestExternalFileOptions {
414    fn drop(&mut self) {
415        unsafe {
416            ffi::rocksdb_ingestexternalfileoptions_destroy(self.inner);
417        }
418    }
419}
420
421impl BlockBasedOptions {
422    /// Approximate size of user data packed per block. Note that the
423    /// block size specified here corresponds to uncompressed data. The
424    /// actual size of the unit read from disk may be smaller if
425    /// compression is enabled. This parameter can be changed dynamically.
426    pub fn set_block_size(&mut self, size: usize) {
427        unsafe {
428            ffi::rocksdb_block_based_options_set_block_size(self.inner, size);
429        }
430    }
431
432    /// Block size for partitioned metadata. Currently applied to indexes when
433    /// kTwoLevelIndexSearch is used and to filters when partition_filters is used.
434    /// Note: Since in the current implementation the filters and index partitions
435    /// are aligned, an index/filter block is created when either index or filter
436    /// block size reaches the specified limit.
437    ///
438    /// Note: this limit is currently applied to only index blocks; a filter
439    /// partition is cut right after an index block is cut.
440    pub fn set_metadata_block_size(&mut self, size: usize) {
441        unsafe {
442            ffi::rocksdb_block_based_options_set_metadata_block_size(self.inner, size as u64);
443        }
444    }
445
446    /// Note: currently this option requires kTwoLevelIndexSearch to be set as
447    /// well.
448    ///
449    /// Use partitioned full filters for each SST file. This option is
450    /// incompatible with block-based filters.
451    pub fn set_partition_filters(&mut self, size: bool) {
452        unsafe {
453            ffi::rocksdb_block_based_options_set_partition_filters(self.inner, c_uchar::from(size));
454        }
455    }
456
457    /// Sets global cache for blocks (user data is stored in a set of blocks, and
458    /// a block is the unit of reading from disk).
459    ///
460    /// If set, use the specified cache for blocks.
461    /// By default, rocksdb will automatically create and use an 8MB internal cache.
462    pub fn set_block_cache(&mut self, cache: &Cache) {
463        unsafe {
464            ffi::rocksdb_block_based_options_set_block_cache(self.inner, cache.0.inner.as_ptr());
465        }
466        self.outlive.block_cache = Some(cache.clone());
467    }
468
469    /// Disable block cache
470    pub fn disable_cache(&mut self) {
471        unsafe {
472            ffi::rocksdb_block_based_options_set_no_block_cache(self.inner, c_uchar::from(true));
473        }
474    }
475
476    /// Sets a [Bloom filter](https://github.com/facebook/rocksdb/wiki/RocksDB-Bloom-Filter)
477    /// policy to reduce disk reads.
478    ///
479    /// # Examples
480    ///
481    /// ```
482    /// use rust_rocksdb::BlockBasedOptions;
483    ///
484    /// let mut opts = BlockBasedOptions::default();
485    /// opts.set_bloom_filter(10.0, true);
486    /// ```
487    pub fn set_bloom_filter(&mut self, bits_per_key: c_double, block_based: bool) {
488        unsafe {
489            let bloom = if block_based {
490                ffi::rocksdb_filterpolicy_create_bloom(bits_per_key as _)
491            } else {
492                ffi::rocksdb_filterpolicy_create_bloom_full(bits_per_key as _)
493            };
494
495            ffi::rocksdb_block_based_options_set_filter_policy(self.inner, bloom);
496        }
497    }
498
499    /// Sets a [Ribbon filter](http://rocksdb.org/blog/2021/12/29/ribbon-filter.html)
500    /// policy to reduce disk reads.
501    ///
502    /// Ribbon filters use less memory in exchange for slightly more CPU usage
503    /// compared to an equivalent bloom filter.
504    ///
505    /// # Examples
506    ///
507    /// ```
508    /// use rust_rocksdb::BlockBasedOptions;
509    ///
510    /// let mut opts = BlockBasedOptions::default();
511    /// opts.set_ribbon_filter(10.0);
512    /// ```
513    pub fn set_ribbon_filter(&mut self, bloom_equivalent_bits_per_key: c_double) {
514        unsafe {
515            let ribbon = ffi::rocksdb_filterpolicy_create_ribbon(bloom_equivalent_bits_per_key);
516            ffi::rocksdb_block_based_options_set_filter_policy(self.inner, ribbon);
517        }
518    }
519
520    /// Sets a hybrid [Ribbon filter](http://rocksdb.org/blog/2021/12/29/ribbon-filter.html)
521    /// policy to reduce disk reads.
522    ///
523    /// Uses Bloom filters before the given level, and Ribbon filters for all
524    /// other levels. This combines the memory savings from Ribbon filters
525    /// with the lower CPU usage of Bloom filters.
526    ///
527    /// # Examples
528    ///
529    /// ```
530    /// use rust_rocksdb::BlockBasedOptions;
531    ///
532    /// let mut opts = BlockBasedOptions::default();
533    /// opts.set_hybrid_ribbon_filter(10.0, 2);
534    /// ```
535    pub fn set_hybrid_ribbon_filter(
536        &mut self,
537        bloom_equivalent_bits_per_key: c_double,
538        bloom_before_level: c_int,
539    ) {
540        unsafe {
541            let ribbon = ffi::rocksdb_filterpolicy_create_ribbon_hybrid(
542                bloom_equivalent_bits_per_key,
543                bloom_before_level,
544            );
545            ffi::rocksdb_block_based_options_set_filter_policy(self.inner, ribbon);
546        }
547    }
548
549    /// Whether to put index/filter blocks in the block cache. When false,
550    /// each "table reader" object will pre-load index/filter blocks during
551    /// table initialization. Index and filter partition blocks always use
552    /// block cache regardless of this option.
553    ///
554    /// Default: false
555    pub fn set_cache_index_and_filter_blocks(&mut self, v: bool) {
556        unsafe {
557            ffi::rocksdb_block_based_options_set_cache_index_and_filter_blocks(
558                self.inner,
559                c_uchar::from(v),
560            );
561        }
562    }
563
564    /// If `cache_index_and_filter_blocks` is enabled, cache index and filter
565    /// blocks with high priority. Depending on the block cache implementation,
566    /// index, filter, and other metadata blocks may be less likely to be
567    /// evicted than data blocks when this is set to true.
568    ///
569    /// Default: true.
570    pub fn set_cache_index_and_filter_blocks_with_high_priority(&mut self, v: bool) {
571        unsafe {
572            ffi::rocksdb_block_based_options_set_cache_index_and_filter_blocks_with_high_priority(
573                self.inner,
574                c_uchar::from(v),
575            );
576        }
577    }
578
579    /// Defines the index type to be used for SS-table lookups.
580    ///
581    /// # Examples
582    ///
583    /// ```
584    /// use rust_rocksdb::{BlockBasedOptions, BlockBasedIndexType, Options};
585    ///
586    /// let mut opts = Options::default();
587    /// let mut block_opts = BlockBasedOptions::default();
588    /// block_opts.set_index_type(BlockBasedIndexType::HashSearch);
589    /// ```
590    pub fn set_index_type(&mut self, index_type: BlockBasedIndexType) {
591        let index = index_type as i32;
592        unsafe {
593            ffi::rocksdb_block_based_options_set_index_type(self.inner, index);
594        }
595    }
596
597    /// Selects the search algorithm used inside each index block at lookup
598    /// time.
599    ///
600    /// Use [`IndexBlockSearchType::Interpolation`] when keys in index blocks
601    /// are known to be uniformly distributed and the byte-wise comparator is
602    /// in use, or [`IndexBlockSearchType::Auto`] to let RocksDB choose per
603    /// block. `Auto` requires the corresponding write-path threshold to be
604    /// set via [`Self::set_uniform_cv_threshold`]; otherwise it falls back to
605    /// binary search.
606    ///
607    /// Default: `IndexBlockSearchType::Binary`
608    ///
609    /// # Examples
610    ///
611    /// ```
612    /// use rust_rocksdb::{BlockBasedOptions, IndexBlockSearchType};
613    ///
614    /// let mut block_opts = BlockBasedOptions::default();
615    /// block_opts.set_index_block_search_type(IndexBlockSearchType::Auto);
616    /// block_opts.set_uniform_cv_threshold(0.2);
617    /// ```
618    pub fn set_index_block_search_type(&mut self, search_type: IndexBlockSearchType) {
619        unsafe {
620            ffi::rocksdb_block_based_options_set_index_block_search_type(
621                self.inner,
622                search_type as c_int,
623            );
624        }
625    }
626
627    /// Coefficient of variation (CV) threshold used on the write path to
628    /// decide whether an index block's keys are "uniform" enough to benefit
629    /// from interpolation search at read time. When the CV of key gaps within
630    /// an index block is below this threshold, the per-block "is_uniform"
631    /// footer bit is set, which
632    /// [`IndexBlockSearchType::Auto`](Self::set_index_block_search_type)
633    /// consults at lookup time.
634    ///
635    /// Any negative value disables the feature; the magnitude is ignored.
636    /// With the default disabled value, [`IndexBlockSearchType::Auto`]
637    /// degenerates to binary search at read time because the per-block
638    /// "is_uniform" bit is never written. The recommended enabled range is
639    /// `0.0..=1.0`; a typical value is `0.2`.
640    ///
641    /// Note: currently only index blocks honour this; the value has no effect
642    /// on data blocks today.
643    ///
644    /// Default: `-1.0` (disabled)
645    ///
646    /// # Examples
647    ///
648    /// ```
649    /// use rust_rocksdb::BlockBasedOptions;
650    ///
651    /// let mut block_opts = BlockBasedOptions::default();
652    /// block_opts.set_uniform_cv_threshold(0.2);
653    /// ```
654    pub fn set_uniform_cv_threshold(&mut self, threshold: f64) {
655        unsafe {
656            ffi::rocksdb_block_based_options_set_uniform_cv_threshold(self.inner, threshold);
657        }
658    }
659
660    /// If cache_index_and_filter_blocks is true and the below is true, then
661    /// filter and index blocks are stored in the cache, but a reference is
662    /// held in the "table reader" object so the blocks are pinned and only
663    /// evicted from cache when the table reader is freed.
664    ///
665    /// Default: false.
666    pub fn set_pin_l0_filter_and_index_blocks_in_cache(&mut self, v: bool) {
667        unsafe {
668            ffi::rocksdb_block_based_options_set_pin_l0_filter_and_index_blocks_in_cache(
669                self.inner,
670                c_uchar::from(v),
671            );
672        }
673    }
674
675    /// If cache_index_and_filter_blocks is true and the below is true, then
676    /// the top-level index of partitioned filter and index blocks are stored in
677    /// the cache, but a reference is held in the "table reader" object so the
678    /// blocks are pinned and only evicted from cache when the table reader is
679    /// freed. This is not limited to l0 in LSM tree.
680    ///
681    /// Default: true.
682    pub fn set_pin_top_level_index_and_filter(&mut self, v: bool) {
683        unsafe {
684            ffi::rocksdb_block_based_options_set_pin_top_level_index_and_filter(
685                self.inner,
686                c_uchar::from(v),
687            );
688        }
689    }
690
691    /// Format version, reserved for backward compatibility.
692    ///
693    /// See full [list](https://github.com/facebook/rocksdb/blob/v11.8.1/include/rocksdb/table.h#L702-L731)
694    /// of the supported versions.
695    ///
696    /// Default: 7, which needs RocksDB 10.4.0 or newer to read. Lower it if
697    /// older readers have to open the files.
698    pub fn set_format_version(&mut self, version: i32) {
699        unsafe {
700            ffi::rocksdb_block_based_options_set_format_version(self.inner, version);
701        }
702    }
703
704    /// Use delta encoding to compress keys in blocks.
705    /// ReadOptions::pin_data requires this option to be disabled.
706    ///
707    /// Default: true
708    pub fn set_use_delta_encoding(&mut self, enable: bool) {
709        unsafe {
710            ffi::rocksdb_block_based_options_set_use_delta_encoding(
711                self.inner,
712                c_uchar::from(enable),
713            );
714        }
715    }
716
717    /// Number of keys between restart points for delta encoding of keys.
718    /// This parameter can be changed dynamically. Most clients should
719    /// leave this parameter alone. The minimum value allowed is 1. Any smaller
720    /// value will be silently overwritten with 1.
721    ///
722    /// Default: 16.
723    pub fn set_block_restart_interval(&mut self, interval: i32) {
724        unsafe {
725            ffi::rocksdb_block_based_options_set_block_restart_interval(self.inner, interval);
726        }
727    }
728
729    /// Same as block_restart_interval but used for the index block.
730    /// If you don't plan to run RocksDB before version 5.16 and you are
731    /// using `index_block_restart_interval` > 1, you should
732    /// probably set the `format_version` to >= 4 as it would reduce the index size.
733    ///
734    /// Default: 1.
735    pub fn set_index_block_restart_interval(&mut self, interval: i32) {
736        unsafe {
737            ffi::rocksdb_block_based_options_set_index_block_restart_interval(self.inner, interval);
738        }
739    }
740
741    /// Set the data block index type for point lookups:
742    ///  `DataBlockIndexType::BinarySearch` to use binary search within the data block.
743    ///  `DataBlockIndexType::BinaryAndHash` to use the data block hash index in combination with
744    ///  the normal binary search.
745    ///
746    /// The hash table utilization ratio is adjustable using [`set_data_block_hash_ratio`](#method.set_data_block_hash_ratio), which is
747    /// valid only when using `DataBlockIndexType::BinaryAndHash`.
748    ///
749    /// Default: `BinarySearch`
750    /// # Examples
751    ///
752    /// ```
753    /// use rust_rocksdb::{BlockBasedOptions, DataBlockIndexType, Options};
754    ///
755    /// let mut opts = Options::default();
756    /// let mut block_opts = BlockBasedOptions::default();
757    /// block_opts.set_data_block_index_type(DataBlockIndexType::BinaryAndHash);
758    /// block_opts.set_data_block_hash_ratio(0.85);
759    /// ```
760    pub fn set_data_block_index_type(&mut self, index_type: DataBlockIndexType) {
761        let index_t = index_type as i32;
762        unsafe {
763            ffi::rocksdb_block_based_options_set_data_block_index_type(self.inner, index_t);
764        }
765    }
766
767    /// Set the data block hash index utilization ratio.
768    ///
769    /// The smaller the utilization ratio, the less hash collisions happen, and so reduce the risk for a
770    /// point lookup to fall back to binary search due to the collisions. A small ratio means faster
771    /// lookup at the price of more space overhead.
772    ///
773    /// Default: 0.75
774    pub fn set_data_block_hash_ratio(&mut self, ratio: f64) {
775        unsafe {
776            ffi::rocksdb_block_based_options_set_data_block_hash_ratio(self.inner, ratio);
777        }
778    }
779
780    /// If false, place only prefixes in the filter, not whole keys.
781    ///
782    /// Defaults to true.
783    pub fn set_whole_key_filtering(&mut self, v: bool) {
784        unsafe {
785            ffi::rocksdb_block_based_options_set_whole_key_filtering(self.inner, c_uchar::from(v));
786        }
787    }
788
789    /// Use the specified checksum type.
790    /// Newly created table files will be protected with this checksum type.
791    /// Old table files will still be readable, even though they have different checksum type.
792    pub fn set_checksum_type(&mut self, checksum_type: ChecksumType) {
793        unsafe {
794            ffi::rocksdb_block_based_options_set_checksum(self.inner, checksum_type as c_char);
795        }
796    }
797
798    /// If true, generate Bloom/Ribbon filters that minimize memory internal
799    /// fragmentation.
800    /// See official [wiki](
801    /// https://github.com/facebook/rocksdb/wiki/RocksDB-Bloom-Filter#reducing-internal-fragmentation)
802    /// for more information.
803    ///
804    /// Default: true.
805    /// # Examples
806    ///
807    /// ```
808    /// use rust_rocksdb::BlockBasedOptions;
809    ///
810    /// let mut opts = BlockBasedOptions::default();
811    /// opts.set_bloom_filter(10.0, true);
812    /// opts.set_optimize_filters_for_memory(true);
813    /// ```
814    pub fn set_optimize_filters_for_memory(&mut self, v: bool) {
815        unsafe {
816            ffi::rocksdb_block_based_options_set_optimize_filters_for_memory(
817                self.inner,
818                c_uchar::from(v),
819            );
820        }
821    }
822
823    /// The tier of block-based tables whose top-level index into metadata
824    /// partitions will be pinned. Currently indexes and filters may be
825    /// partitioned.
826    ///
827    /// Note `cache_index_and_filter_blocks` must be true for this option to have
828    /// any effect. Otherwise any top-level index into metadata partitions would be
829    /// held in table reader memory, outside the block cache.
830    ///
831    /// Default: `BlockBasedPinningTier:Fallback`
832    ///
833    /// # Example
834    ///
835    /// ```
836    /// use rust_rocksdb::{BlockBasedOptions, BlockBasedPinningTier, Options};
837    ///
838    /// let mut opts = Options::default();
839    /// let mut block_opts = BlockBasedOptions::default();
840    /// block_opts.set_top_level_index_pinning_tier(BlockBasedPinningTier::FlushAndSimilar);
841    /// ```
842    pub fn set_top_level_index_pinning_tier(&mut self, tier: BlockBasedPinningTier) {
843        unsafe {
844            ffi::rocksdb_block_based_options_set_top_level_index_pinning_tier(
845                self.inner,
846                tier as c_int,
847            );
848        }
849    }
850
851    /// The tier of block-based tables whose metadata partitions will be pinned.
852    /// Currently indexes and filters may be partitioned.
853    ///
854    /// Default: `BlockBasedPinningTier:Fallback`
855    ///
856    /// # Example
857    ///
858    /// ```
859    /// use rust_rocksdb::{BlockBasedOptions, BlockBasedPinningTier, Options};
860    ///
861    /// let mut opts = Options::default();
862    /// let mut block_opts = BlockBasedOptions::default();
863    /// block_opts.set_partition_pinning_tier(BlockBasedPinningTier::FlushAndSimilar);
864    /// ```
865    pub fn set_partition_pinning_tier(&mut self, tier: BlockBasedPinningTier) {
866        unsafe {
867            ffi::rocksdb_block_based_options_set_partition_pinning_tier(self.inner, tier as c_int);
868        }
869    }
870
871    /// The tier of block-based tables whose unpartitioned metadata blocks will be
872    /// pinned.
873    ///
874    /// Note `cache_index_and_filter_blocks` must be true for this option to have
875    /// any effect. Otherwise the unpartitioned meta-blocks would be held in table
876    /// reader memory, outside the block cache.
877    ///
878    /// Default: `BlockBasedPinningTier:Fallback`
879    ///
880    /// # Example
881    ///
882    /// ```
883    /// use rust_rocksdb::{BlockBasedOptions, BlockBasedPinningTier, Options};
884    ///
885    /// let mut opts = Options::default();
886    /// let mut block_opts = BlockBasedOptions::default();
887    /// block_opts.set_unpartitioned_pinning_tier(BlockBasedPinningTier::FlushAndSimilar);
888    /// ```
889    pub fn set_unpartitioned_pinning_tier(&mut self, tier: BlockBasedPinningTier) {
890        unsafe {
891            ffi::rocksdb_block_based_options_set_unpartitioned_pinning_tier(
892                self.inner,
893                tier as c_int,
894            );
895        }
896    }
897}
898
899impl Default for BlockBasedOptions {
900    fn default() -> Self {
901        let block_opts = unsafe { ffi::rocksdb_block_based_options_create() };
902        assert!(
903            !block_opts.is_null(),
904            "Could not create RocksDB block based options"
905        );
906
907        Self {
908            inner: block_opts,
909            outlive: BlockBasedOptionsMustOutliveDB::default(),
910        }
911    }
912}
913
914impl CuckooTableOptions {
915    /// Determines the utilization of hash tables. Smaller values
916    /// result in larger hash tables with fewer collisions.
917    /// Default: 0.9
918    pub fn set_hash_ratio(&mut self, ratio: f64) {
919        unsafe {
920            ffi::rocksdb_cuckoo_options_set_hash_ratio(self.inner, ratio);
921        }
922    }
923
924    /// A property used by builder to determine the depth to go to
925    /// to search for a path to displace elements in case of
926    /// collision. See Builder.MakeSpaceForKey method. Higher
927    /// values result in more efficient hash tables with fewer
928    /// lookups but take more time to build.
929    /// Default: 100
930    pub fn set_max_search_depth(&mut self, depth: u32) {
931        unsafe {
932            ffi::rocksdb_cuckoo_options_set_max_search_depth(self.inner, depth);
933        }
934    }
935
936    /// In case of collision while inserting, the builder
937    /// attempts to insert in the next cuckoo_block_size
938    /// locations before skipping over to the next Cuckoo hash
939    /// function. This makes lookups more cache friendly in case
940    /// of collisions.
941    /// Default: 5
942    pub fn set_cuckoo_block_size(&mut self, size: u32) {
943        unsafe {
944            ffi::rocksdb_cuckoo_options_set_cuckoo_block_size(self.inner, size);
945        }
946    }
947
948    /// If this option is enabled, user key is treated as uint64_t and its value
949    /// is used as hash value directly. This option changes builder's behavior.
950    /// Reader ignore this option and behave according to what specified in
951    /// table property.
952    /// Default: false
953    pub fn set_identity_as_first_hash(&mut self, flag: bool) {
954        unsafe {
955            ffi::rocksdb_cuckoo_options_set_identity_as_first_hash(self.inner, c_uchar::from(flag));
956        }
957    }
958
959    /// If this option is set to true, module is used during hash calculation.
960    /// This often yields better space efficiency at the cost of performance.
961    /// If this option is set to false, # of entries in table is constrained to
962    /// be power of two, and bit and is used to calculate hash, which is faster in general.
963    /// Default: true
964    pub fn set_use_module_hash(&mut self, flag: bool) {
965        unsafe {
966            ffi::rocksdb_cuckoo_options_set_use_module_hash(self.inner, c_uchar::from(flag));
967        }
968    }
969}
970
971impl Default for CuckooTableOptions {
972    fn default() -> Self {
973        let opts = unsafe { ffi::rocksdb_cuckoo_options_create() };
974        assert!(!opts.is_null(), "Could not create RocksDB cuckoo options");
975
976        Self { inner: opts }
977    }
978}
979
980// Verbosity of the LOG.
981#[derive(Debug, Copy, Clone, PartialEq, Eq)]
982#[repr(i32)]
983pub enum LogLevel {
984    Debug = 0,
985    Info,
986    Warn,
987    Error,
988    Fatal,
989    Header,
990}
991
992impl LogLevel {
993    pub(crate) fn try_from_raw(raw: i32) -> Option<Self> {
994        match raw {
995            n if n == LogLevel::Debug as i32 => Some(LogLevel::Debug),
996            n if n == LogLevel::Info as i32 => Some(LogLevel::Info),
997            n if n == LogLevel::Warn as i32 => Some(LogLevel::Warn),
998            n if n == LogLevel::Error as i32 => Some(LogLevel::Error),
999            n if n == LogLevel::Fatal as i32 => Some(LogLevel::Fatal),
1000            n if n == LogLevel::Header as i32 => Some(LogLevel::Header),
1001            _ => None,
1002        }
1003    }
1004}
1005
1006impl Options {
1007    /// Constructs the DBOptions and ColumnFamilyDescriptors by loading the
1008    /// latest RocksDB options file stored in the specified rocksdb database.
1009    ///
1010    /// *IMPORTANT*:
1011    /// ROCKSDB DOES NOT STORE cf ttl in the options file. If you have set it via
1012    /// [`ColumnFamilyDescriptor::new_with_ttl`] then you need to set it again after loading the options file.
1013    /// Tll will be set to [`ColumnFamilyTtl::Disabled`] for all column families for your safety.
1014    pub fn load_latest<P: AsRef<Path>>(
1015        path: P,
1016        env: Env,
1017        ignore_unknown_options: bool,
1018        cache: Cache,
1019    ) -> Result<(Options, Vec<ColumnFamilyDescriptor>), Error> {
1020        let path = to_cpath(path)?;
1021        let mut db_options: *mut ffi::rocksdb_options_t = null_mut();
1022        let mut num_column_families: usize = 0;
1023        let mut column_family_names: *mut *mut c_char = null_mut();
1024        let mut column_family_options: *mut *mut ffi::rocksdb_options_t = null_mut();
1025        unsafe {
1026            ffi_try!(ffi::rocksdb_load_latest_options(
1027                path.as_ptr(),
1028                env.0.inner,
1029                ignore_unknown_options,
1030                cache.0.inner.as_ptr(),
1031                &raw mut db_options,
1032                &raw mut num_column_families,
1033                &raw mut column_family_names,
1034                &raw mut column_family_options,
1035            ));
1036        }
1037        let options = Options {
1038            inner: db_options,
1039            outlive: OptionsMustOutliveDB::default(),
1040        };
1041        // read_column_descriptors frees column_family_names and the column_family_options array.
1042        // We can't call rocksdb_load_latest_options_destroy because it also frees options, and
1043        // the individual `column_family_options` pointers. We want to return them.
1044        let column_families = unsafe {
1045            Options::read_column_descriptors(
1046                num_column_families,
1047                column_family_names,
1048                column_family_options,
1049            )
1050        };
1051        Ok((options, column_families))
1052    }
1053
1054    /// Constructs a new `DBOptions` from `self` and a string `opts_str` with the syntax detailed in the blogpost
1055    /// [Reading RocksDB options from a file](https://rocksdb.org/blog/2015/02/24/reading-rocksdb-options-from-a-file.html)
1056    pub fn get_options_from_string<S: AsRef<str>>(
1057        &mut self,
1058        opts_str: S,
1059    ) -> Result<Options, Error> {
1060        // create the rocksdb_options_t and immediately wrap it so we don't forget to free it
1061        let options = Options {
1062            inner: unsafe { ffi::rocksdb_options_create() },
1063            outlive: OptionsMustOutliveDB::default(),
1064        };
1065
1066        let opts_cstr = opts_str.as_ref().into_c_string().map_err(|e| {
1067            Error::new(format!(
1068                "options string must not contain NUL (0x00) bytes: {e}"
1069            ))
1070        })?;
1071        unsafe {
1072            ffi_try!(ffi::rocksdb_get_options_from_string(
1073                self.inner.cast_const(),
1074                opts_cstr.as_ptr(),
1075                options.inner,
1076            ));
1077        }
1078        Ok(options)
1079    }
1080
1081    /// Reads column descriptors from C pointers. This frees the `column_family_names` and
1082    /// `column_family_options` arrays, and the strings contained in `column_family_names`. It does
1083    /// *not* free the `rocksdb_options_t*` pointers contained in `column_family_options`.
1084    #[inline]
1085    unsafe fn read_column_descriptors(
1086        num_column_families: usize,
1087        column_family_names: *mut *mut c_char,
1088        column_family_options: *mut *mut ffi::rocksdb_options_t,
1089    ) -> Vec<ColumnFamilyDescriptor> {
1090        let column_family_names_iter = unsafe {
1091            slice::from_raw_parts(column_family_names, num_column_families)
1092                .iter()
1093                .map(|ptr| from_cstr_and_free(*ptr))
1094        };
1095        let column_family_options_iter = unsafe {
1096            slice::from_raw_parts(column_family_options, num_column_families)
1097                .iter()
1098                .map(|ptr| Options {
1099                    inner: *ptr,
1100                    outlive: OptionsMustOutliveDB::default(),
1101                })
1102        };
1103        let column_descriptors = column_family_names_iter
1104            .zip(column_family_options_iter)
1105            .map(|(name, options)| ColumnFamilyDescriptor {
1106                name,
1107                options,
1108                ttl: ColumnFamilyTtl::Disabled,
1109            })
1110            .collect::<Vec<_>>();
1111
1112        // free the arrays
1113        unsafe {
1114            // we freed each string in the column_family_names array using from_cstr_and_free
1115            ffi::rocksdb_free(column_family_names as *mut c_void);
1116            // we don't want to free the contents of this array because we return it
1117            ffi::rocksdb_free(column_family_options as *mut c_void);
1118            column_descriptors
1119        }
1120    }
1121
1122    /// By default, RocksDB uses only one background thread for flush and
1123    /// compaction. Calling this function will set it up such that total of
1124    /// `total_threads` is used. Good value for `total_threads` is the number of
1125    /// cores. You almost definitely want to call this function if your system is
1126    /// bottlenecked by RocksDB.
1127    ///
1128    /// # Examples
1129    ///
1130    /// ```
1131    /// use rust_rocksdb::Options;
1132    ///
1133    /// let mut opts = Options::default();
1134    /// opts.increase_parallelism(3);
1135    /// ```
1136    pub fn increase_parallelism(&mut self, parallelism: i32) {
1137        unsafe {
1138            ffi::rocksdb_options_increase_parallelism(self.inner, parallelism);
1139        }
1140    }
1141
1142    /// Optimize level style compaction.
1143    ///
1144    /// Default values for some parameters in `Options` are not optimized for heavy
1145    /// workloads and big datasets, which means you might observe write stalls under
1146    /// some conditions.
1147    ///
1148    /// This can be used as one of the starting points for tuning RocksDB options in
1149    /// such cases.
1150    ///
1151    /// Internally, it sets `write_buffer_size`, `min_write_buffer_number_to_merge`,
1152    /// `max_write_buffer_number`, `level0_file_num_compaction_trigger`,
1153    /// `target_file_size_base`, `max_bytes_for_level_base`, so it can override if those
1154    /// parameters were set before.
1155    ///
1156    /// It sets buffer sizes so that memory consumption would be constrained by
1157    /// `memtable_memory_budget`.
1158    pub fn optimize_level_style_compaction(&mut self, memtable_memory_budget: usize) {
1159        unsafe {
1160            ffi::rocksdb_options_optimize_level_style_compaction(
1161                self.inner,
1162                memtable_memory_budget as u64,
1163            );
1164        }
1165    }
1166
1167    /// Optimize universal style compaction.
1168    ///
1169    /// Default values for some parameters in `Options` are not optimized for heavy
1170    /// workloads and big datasets, which means you might observe write stalls under
1171    /// some conditions.
1172    ///
1173    /// This can be used as one of the starting points for tuning RocksDB options in
1174    /// such cases.
1175    ///
1176    /// Internally, it sets `write_buffer_size`, `min_write_buffer_number_to_merge`,
1177    /// `max_write_buffer_number`, `level0_file_num_compaction_trigger`,
1178    /// `target_file_size_base`, `max_bytes_for_level_base`, so it can override if those
1179    /// parameters were set before.
1180    ///
1181    /// It sets buffer sizes so that memory consumption would be constrained by
1182    /// `memtable_memory_budget`.
1183    pub fn optimize_universal_style_compaction(&mut self, memtable_memory_budget: usize) {
1184        unsafe {
1185            ffi::rocksdb_options_optimize_universal_style_compaction(
1186                self.inner,
1187                memtable_memory_budget as u64,
1188            );
1189        }
1190    }
1191
1192    /// If true, the database will be created if it is missing.
1193    ///
1194    /// Default: `false`
1195    ///
1196    /// # Examples
1197    ///
1198    /// ```
1199    /// use rust_rocksdb::Options;
1200    ///
1201    /// let mut opts = Options::default();
1202    /// opts.create_if_missing(true);
1203    /// ```
1204    pub fn create_if_missing(&mut self, create_if_missing: bool) {
1205        unsafe {
1206            ffi::rocksdb_options_set_create_if_missing(
1207                self.inner,
1208                c_uchar::from(create_if_missing),
1209            );
1210        }
1211    }
1212
1213    /// If true, any column families that didn't exist when opening the database
1214    /// will be created.
1215    ///
1216    /// Default: `false`
1217    ///
1218    /// # Examples
1219    ///
1220    /// ```
1221    /// use rust_rocksdb::Options;
1222    ///
1223    /// let mut opts = Options::default();
1224    /// opts.create_missing_column_families(true);
1225    /// ```
1226    pub fn create_missing_column_families(&mut self, create_missing_cfs: bool) {
1227        unsafe {
1228            ffi::rocksdb_options_set_create_missing_column_families(
1229                self.inner,
1230                c_uchar::from(create_missing_cfs),
1231            );
1232        }
1233    }
1234
1235    /// Specifies whether an error should be raised if the database already exists.
1236    ///
1237    /// Default: false
1238    pub fn set_error_if_exists(&mut self, enabled: bool) {
1239        unsafe {
1240            ffi::rocksdb_options_set_error_if_exists(self.inner, c_uchar::from(enabled));
1241        }
1242    }
1243
1244    /// Enable/disable paranoid checks.
1245    ///
1246    /// If true, the implementation will do aggressive checking of the
1247    /// data it is processing and will stop early if it detects any
1248    /// errors. This may have unforeseen ramifications: for example, a
1249    /// corruption of one DB entry may cause a large number of entries to
1250    /// become unreadable or for the entire DB to become unopenable.
1251    /// If any of the  writes to the database fails (Put, Delete, Merge, Write),
1252    /// the database will switch to read-only mode and fail all other
1253    /// Write operations.
1254    ///
1255    /// Default: true
1256    pub fn set_paranoid_checks(&mut self, enabled: bool) {
1257        unsafe {
1258            ffi::rocksdb_options_set_paranoid_checks(self.inner, c_uchar::from(enabled));
1259        }
1260    }
1261
1262    /// A list of paths where SST files can be put into, with its target size.
1263    /// Newer data is placed into paths specified earlier in the vector while
1264    /// older data gradually moves to paths specified later in the vector.
1265    ///
1266    /// For example, you have a flash device with 10GB allocated for the DB,
1267    /// as well as a hard drive of 2TB, you should config it to be:
1268    ///   [{"/flash_path", 10GB}, {"/hard_drive", 2TB}]
1269    ///
1270    /// The system will try to guarantee data under each path is close to but
1271    /// not larger than the target size. But current and future file sizes used
1272    /// by determining where to place a file are based on best-effort estimation,
1273    /// which means there is a chance that the actual size under the directory
1274    /// is slightly more than target size under some workloads. User should give
1275    /// some buffer room for those cases.
1276    ///
1277    /// If none of the paths has sufficient room to place a file, the file will
1278    /// be placed to the last path anyway, despite to the target size.
1279    ///
1280    /// Placing newer data to earlier paths is also best-efforts. User should
1281    /// expect user files to be placed in higher levels in some extreme cases.
1282    ///
1283    /// If left empty, only one path will be used, which is `path` passed when
1284    /// opening the DB.
1285    ///
1286    /// Default: empty
1287    pub fn set_db_paths(&mut self, paths: &[DBPath]) {
1288        let mut paths: Vec<_> = paths.iter().map(|path| path.inner.cast_const()).collect();
1289        let num_paths = paths.len();
1290        unsafe {
1291            ffi::rocksdb_options_set_db_paths(self.inner, paths.as_mut_ptr(), num_paths);
1292        }
1293    }
1294
1295    /// Use the specified object to interact with the environment,
1296    /// e.g. to read/write files, schedule background work, etc. In the near
1297    /// future, support for doing storage operations such as read/write files
1298    /// through env will be deprecated in favor of file_system.
1299    ///
1300    /// Default: Env::default()
1301    pub fn set_env(&mut self, env: &Env) {
1302        unsafe {
1303            ffi::rocksdb_options_set_env(self.inner, env.0.inner);
1304        }
1305        self.outlive.env = Some(env.clone());
1306    }
1307
1308    /// Sets the compression algorithm that will be used for compressing blocks.
1309    ///
1310    /// Default: `DBCompressionType::Lz4`, falling back to
1311    /// `DBCompressionType::Snappy` and then `DBCompressionType::None` when the
1312    /// preceding one is not compiled in. RocksDB 11.5.0 changed this from
1313    /// Snappy; it affects only column families that never set `compression`,
1314    /// and only newly written SST files. Existing data stays readable, since
1315    /// the decompressor is selected per block.
1316    ///
1317    /// # Examples
1318    ///
1319    /// ```
1320    /// use rust_rocksdb::{Options, DBCompressionType};
1321    ///
1322    /// let mut opts = Options::default();
1323    /// opts.set_compression_type(DBCompressionType::Snappy);
1324    /// ```
1325    pub fn set_compression_type(&mut self, t: DBCompressionType) {
1326        unsafe {
1327            ffi::rocksdb_options_set_compression(self.inner, t as c_int);
1328        }
1329    }
1330
1331    /// Number of threads for parallel compression.
1332    /// Parallel compression is enabled only if threads > 1.
1333    /// THE FEATURE IS STILL EXPERIMENTAL
1334    ///
1335    /// See [code](https://github.com/facebook/rocksdb/blob/v8.6.7/include/rocksdb/advanced_options.h#L116-L127)
1336    /// for more information.
1337    ///
1338    /// Default: 1
1339    ///
1340    /// Examples
1341    ///
1342    /// ```
1343    /// use rust_rocksdb::{Options, DBCompressionType};
1344    ///
1345    /// let mut opts = Options::default();
1346    /// opts.set_compression_type(DBCompressionType::Zstd);
1347    /// opts.set_compression_options_parallel_threads(3);
1348    /// ```
1349    pub fn set_compression_options_parallel_threads(&mut self, num: i32) {
1350        unsafe {
1351            ffi::rocksdb_options_set_compression_options_parallel_threads(self.inner, num);
1352        }
1353    }
1354
1355    /// Sets the compression algorithm that will be used for compressing WAL.
1356    ///
1357    /// At present, only ZSTD compression is supported!
1358    ///
1359    /// Default: `DBCompressionType::None`
1360    ///
1361    /// # Examples
1362    ///
1363    /// ```
1364    /// use rust_rocksdb::{Options, DBCompressionType};
1365    ///
1366    /// let mut opts = Options::default();
1367    /// opts.set_wal_compression_type(DBCompressionType::Zstd);
1368    /// // Or None to disable it
1369    /// opts.set_wal_compression_type(DBCompressionType::None);
1370    /// ```
1371    pub fn set_wal_compression_type(&mut self, t: DBCompressionType) {
1372        match t {
1373            DBCompressionType::None | DBCompressionType::Zstd => unsafe {
1374                ffi::rocksdb_options_set_wal_compression(self.inner, t as c_int);
1375            },
1376            other => unimplemented!("{:?} is not supported for WAL compression", other),
1377        }
1378    }
1379
1380    /// Sets the bottom-most compression algorithm that will be used for
1381    /// compressing blocks at the bottom-most level.
1382    ///
1383    /// Note that to actually enable bottom-most compression configuration after
1384    /// setting the compression type, it needs to be enabled by calling
1385    /// [`set_bottommost_compression_options`](#method.set_bottommost_compression_options) or
1386    /// [`set_bottommost_zstd_max_train_bytes`](#method.set_bottommost_zstd_max_train_bytes) method with `enabled` argument
1387    /// set to `true`.
1388    ///
1389    /// # Examples
1390    ///
1391    /// ```
1392    /// use rust_rocksdb::{Options, DBCompressionType};
1393    ///
1394    /// let mut opts = Options::default();
1395    /// opts.set_bottommost_compression_type(DBCompressionType::Zstd);
1396    /// opts.set_bottommost_zstd_max_train_bytes(0, true);
1397    /// ```
1398    pub fn set_bottommost_compression_type(&mut self, t: DBCompressionType) {
1399        unsafe {
1400            ffi::rocksdb_options_set_bottommost_compression(self.inner, t as c_int);
1401        }
1402    }
1403
1404    /// Different levels can have different compression policies. There
1405    /// are cases where most lower levels would like to use quick compression
1406    /// algorithms while the higher levels (which have more data) use
1407    /// compression algorithms that have better compression but could
1408    /// be slower. This array, if non-empty, should have an entry for
1409    /// each level of the database; these override the value specified in
1410    /// the previous field 'compression'.
1411    ///
1412    /// # Examples
1413    ///
1414    /// ```
1415    /// use rust_rocksdb::{Options, DBCompressionType};
1416    ///
1417    /// let mut opts = Options::default();
1418    /// opts.set_compression_per_level(&[
1419    ///     DBCompressionType::None,
1420    ///     DBCompressionType::None,
1421    ///     DBCompressionType::Snappy,
1422    ///     DBCompressionType::Snappy,
1423    ///     DBCompressionType::Snappy
1424    /// ]);
1425    /// ```
1426    pub fn set_compression_per_level(&mut self, level_types: &[DBCompressionType]) {
1427        unsafe {
1428            let mut level_types: Vec<_> = level_types.iter().map(|&t| t as c_int).collect();
1429            ffi::rocksdb_options_set_compression_per_level(
1430                self.inner,
1431                level_types.as_mut_ptr(),
1432                level_types.len() as size_t,
1433            );
1434        }
1435    }
1436
1437    /// Maximum size of dictionaries used to prime the compression library.
1438    /// Enabling dictionary can improve compression ratios when there are
1439    /// repetitions across data blocks.
1440    ///
1441    /// The dictionary is created by sampling the SST file data. If
1442    /// `zstd_max_train_bytes` is nonzero, the samples are passed through zstd's
1443    /// dictionary generator. Otherwise, the random samples are used directly as
1444    /// the dictionary.
1445    ///
1446    /// When compression dictionary is disabled, we compress and write each block
1447    /// before buffering data for the next one. When compression dictionary is
1448    /// enabled, we buffer all SST file data in-memory so we can sample it, as data
1449    /// can only be compressed and written after the dictionary has been finalized.
1450    /// So users of this feature may see increased memory usage.
1451    ///
1452    /// Default: `0`
1453    ///
1454    /// # Examples
1455    ///
1456    /// ```
1457    /// use rust_rocksdb::Options;
1458    ///
1459    /// let mut opts = Options::default();
1460    /// opts.set_compression_options(4, 5, 6, 7);
1461    /// ```
1462    pub fn set_compression_options(
1463        &mut self,
1464        w_bits: c_int,
1465        level: c_int,
1466        strategy: c_int,
1467        max_dict_bytes: c_int,
1468    ) {
1469        unsafe {
1470            ffi::rocksdb_options_set_compression_options(
1471                self.inner,
1472                w_bits,
1473                level,
1474                strategy,
1475                max_dict_bytes,
1476            );
1477        }
1478    }
1479
1480    /// Sets compression options for blocks at the bottom-most level.  Meaning
1481    /// of all settings is the same as in [`set_compression_options`](#method.set_compression_options) method but
1482    /// affect only the bottom-most compression which is set using
1483    /// [`set_bottommost_compression_type`](#method.set_bottommost_compression_type) method.
1484    ///
1485    /// # Examples
1486    ///
1487    /// ```
1488    /// use rust_rocksdb::{Options, DBCompressionType};
1489    ///
1490    /// let mut opts = Options::default();
1491    /// opts.set_bottommost_compression_type(DBCompressionType::Zstd);
1492    /// opts.set_bottommost_compression_options(4, 5, 6, 7, true);
1493    /// ```
1494    pub fn set_bottommost_compression_options(
1495        &mut self,
1496        w_bits: c_int,
1497        level: c_int,
1498        strategy: c_int,
1499        max_dict_bytes: c_int,
1500        enabled: bool,
1501    ) {
1502        unsafe {
1503            ffi::rocksdb_options_set_bottommost_compression_options(
1504                self.inner,
1505                w_bits,
1506                level,
1507                strategy,
1508                max_dict_bytes,
1509                c_uchar::from(enabled),
1510            );
1511        }
1512    }
1513
1514    /// Sets maximum size of training data passed to zstd's dictionary trainer. Using zstd's
1515    /// dictionary trainer can achieve even better compression ratio improvements than using
1516    /// `max_dict_bytes` alone.
1517    ///
1518    /// The training data will be used to generate a dictionary of max_dict_bytes.
1519    ///
1520    /// Default: 0.
1521    pub fn set_zstd_max_train_bytes(&mut self, value: c_int) {
1522        unsafe {
1523            ffi::rocksdb_options_set_compression_options_zstd_max_train_bytes(self.inner, value);
1524        }
1525    }
1526
1527    /// Sets maximum size of training data passed to zstd's dictionary trainer
1528    /// when compressing the bottom-most level. Using zstd's dictionary trainer
1529    /// can achieve even better compression ratio improvements than using
1530    /// `max_dict_bytes` alone.
1531    ///
1532    /// The training data will be used to generate a dictionary of
1533    /// `max_dict_bytes`.
1534    ///
1535    /// Default: 0.
1536    pub fn set_bottommost_zstd_max_train_bytes(&mut self, value: c_int, enabled: bool) {
1537        unsafe {
1538            ffi::rocksdb_options_set_bottommost_compression_options_zstd_max_train_bytes(
1539                self.inner,
1540                value,
1541                c_uchar::from(enabled),
1542            );
1543        }
1544    }
1545
1546    /// If non-zero, we perform bigger reads when doing compaction. If you're
1547    /// running RocksDB on spinning disks, you should set this to at least 2MB.
1548    /// That way RocksDB's compaction is doing sequential instead of random reads.
1549    ///
1550    /// Default: 2 * 1024 * 1024 (2 MB)
1551    pub fn set_compaction_readahead_size(&mut self, compaction_readahead_size: usize) {
1552        unsafe {
1553            ffi::rocksdb_options_compaction_readahead_size(self.inner, compaction_readahead_size);
1554        }
1555    }
1556
1557    /// Allow RocksDB to pick dynamic base of bytes for levels.
1558    /// With this feature turned on, RocksDB will automatically adjust max bytes for each level.
1559    /// The goal of this feature is to have lower bound on size amplification.
1560    ///
1561    /// Default: true.
1562    pub fn set_level_compaction_dynamic_level_bytes(&mut self, v: bool) {
1563        unsafe {
1564            ffi::rocksdb_options_set_level_compaction_dynamic_level_bytes(
1565                self.inner,
1566                c_uchar::from(v),
1567            );
1568        }
1569    }
1570
1571    /// This option has different meanings for different compaction styles:
1572    ///
1573    /// Leveled: files older than `periodic_compaction_seconds` will be picked up
1574    /// for compaction and will be re-written to the same level as they were
1575    /// before if level_compaction_dynamic_level_bytes is disabled. Otherwise,
1576    /// it will rewrite files to the next level except for the last level files
1577    /// to the same level.
1578    ///
1579    /// FIFO: not supported. Setting this option has no effect for FIFO compaction.
1580    ///
1581    /// Universal: when there are files older than `periodic_compaction_seconds`,
1582    /// rocksdb will try to do as large a compaction as possible including the
1583    /// last level. Such compaction is only skipped if only last level is to
1584    /// be compacted and no file in last level is older than
1585    /// `periodic_compaction_seconds`. See more in
1586    /// UniversalCompactionBuilder::PickPeriodicCompaction().
1587    /// For backward compatibility, the effective value of this option takes
1588    /// into account the value of option `ttl`. The logic is as follows:
1589    ///
1590    /// - both options are set to 30 days if they have the default value.
1591    /// - if both options are zero, zero is picked. Otherwise, we take the min
1592    ///   value among non-zero options values (i.e. takes the stricter limit).
1593    ///
1594    /// One main use of the feature is to make sure a file goes through compaction
1595    /// filters periodically. Users can also use the feature to clear up SST
1596    /// files using old format.
1597    ///
1598    /// A file's age is computed by looking at file_creation_time or creation_time
1599    /// table properties in order, if they have valid non-zero values; if not, the
1600    /// age is based on the file's last modified time (given by the underlying
1601    /// Env).
1602    ///
1603    /// This option only supports block based table format for any compaction
1604    /// style.
1605    ///
1606    /// unit: seconds. Ex: 7 days = 7 * 24 * 60 * 60
1607    ///
1608    /// Values:
1609    /// 0: Turn off Periodic compactions.
1610    /// UINT64_MAX - 1 (0xfffffffffffffffe) is special flag to allow RocksDB to
1611    /// pick default.
1612    ///
1613    /// Default: 30 days if using block based table format + compaction filter +
1614    /// leveled compaction or block based table format + universal compaction.
1615    /// 0 (disabled) otherwise.
1616    ///
1617    pub fn set_periodic_compaction_seconds(&mut self, secs: u64) {
1618        unsafe {
1619            ffi::rocksdb_options_set_periodic_compaction_seconds(self.inner, secs);
1620        }
1621    }
1622
1623    /// When an iterator scans this number of invisible entries (tombstones or
1624    /// hidden puts) from the active memtable during a single iterator operation,
1625    /// we will attempt to flush the memtable. Currently only forward scans are
1626    /// supported (SeekToFirst(), Seek() and Next()).
1627    /// This option helps to reduce the overhead of scanning through a
1628    /// large number of entries in memtable.
1629    /// Users should consider enable deletion-triggered-compaction (see
1630    /// CompactOnDeletionCollectorFactory) together with this option to compact
1631    /// away tombstones after the memtable is flushed.
1632    ///
1633    /// Default: 0 (disabled)
1634    /// Dynamically changeable through the SetOptions() API.
1635    pub fn set_memtable_op_scan_flush_trigger(&mut self, num: u32) {
1636        unsafe {
1637            ffi::rocksdb_options_set_memtable_op_scan_flush_trigger(self.inner, num);
1638        }
1639    }
1640
1641    /// Similar to `memtable_op_scan_flush_trigger`, but this option applies to
1642    /// Next() calls between Seeks or until iterator destruction. If the average
1643    /// of the number of invisible entries scanned from the active memtable, the
1644    /// memtable will be marked for flush.
1645    /// Note that to avoid the case where the window between Seeks is too small,
1646    /// the option only takes effect if the total number of hidden entries scanned
1647    /// within a window is at least `memtable_op_scan_flush_trigger`. So this
1648    /// option is only effective when `memtable_op_scan_flush_trigger` is set.
1649    ///
1650    /// This option should be set to a lower value than
1651    /// `memtable_op_scan_flush_trigger`. It covers the case where an iterator
1652    /// scans through an expensive key range with many invisible entries from the
1653    /// active memtable, but the number of invisible entries per operation does not
1654    /// exceed `memtable_op_scan_flush_trigger`.
1655    ///
1656    /// Default: 0 (disabled)
1657    /// Dynamically changeable through the SetOptions() API.
1658    pub fn set_memtable_avg_op_scan_flush_trigger(&mut self, num: u32) {
1659        unsafe {
1660            ffi::rocksdb_options_set_memtable_avg_op_scan_flush_trigger(self.inner, num);
1661        }
1662    }
1663
1664    /// This option has different meanings for different compaction styles:
1665    ///
1666    /// Leveled: Non-bottom-level files with all keys older than TTL will go
1667    ///    through the compaction process. This usually happens in a cascading
1668    ///    way so that those entries will be compacted to bottommost level/file.
1669    ///    The feature is used to remove stale entries that have been deleted or
1670    ///    updated from the file system.
1671    ///
1672    /// FIFO: Files with all keys older than TTL will be deleted. TTL is only
1673    ///    supported if option max_open_files is set to -1.
1674    ///
1675    /// Universal: users should only set the option `periodic_compaction_seconds`
1676    ///    instead. For backward compatibility, this option has the same
1677    ///    meaning as `periodic_compaction_seconds`. See more in comments for
1678    ///    `periodic_compaction_seconds` on the interaction between these two
1679    ///    options.
1680    ///
1681    /// This option only supports block based table format for any compaction
1682    /// style.
1683    ///
1684    /// unit: seconds. Ex: 1 day = 1 * 24 * 60 * 60
1685    /// 0 means disabling.
1686    /// UINT64_MAX - 1 (0xfffffffffffffffe) is special flag to allow RocksDB to
1687    /// pick default.
1688    ///
1689    /// Default: 30 days if using block based table. 0 (disable) otherwise.
1690    ///
1691    /// Dynamically changeable
1692    /// Note that dynamically changing this option only works for leveled and FIFO
1693    /// compaction. For universal compaction, dynamically changing this option has
1694    /// no effect, users should dynamically change `periodic_compaction_seconds`
1695    /// instead.
1696    pub fn set_ttl(&mut self, secs: u64) {
1697        unsafe {
1698            ffi::rocksdb_options_set_ttl(self.inner, secs);
1699        }
1700    }
1701
1702    pub fn set_merge_operator_associative<F: MergeFn + Clone>(
1703        &mut self,
1704        name: impl CStrLike,
1705        full_merge_fn: F,
1706    ) {
1707        let cb = Box::new(MergeOperatorCallback {
1708            name: name.into_c_string().unwrap(),
1709            full_merge_fn: full_merge_fn.clone(),
1710            partial_merge_fn: full_merge_fn,
1711        });
1712
1713        unsafe {
1714            let mo = ffi::rocksdb_mergeoperator_create(
1715                Box::into_raw(cb).cast::<c_void>(),
1716                Some(merge_operator::destructor_callback::<F, F>),
1717                Some(full_merge_callback::<F, F>),
1718                Some(partial_merge_callback::<F, F>),
1719                Some(merge_operator::delete_callback),
1720                Some(merge_operator::name_callback::<F, F>),
1721            );
1722            ffi::rocksdb_options_set_merge_operator(self.inner, mo);
1723        }
1724    }
1725
1726    pub fn set_merge_operator<F: MergeFn, PF: MergeFn>(
1727        &mut self,
1728        name: impl CStrLike,
1729        full_merge_fn: F,
1730        partial_merge_fn: PF,
1731    ) {
1732        let cb = Box::new(MergeOperatorCallback {
1733            name: name.into_c_string().unwrap(),
1734            full_merge_fn,
1735            partial_merge_fn,
1736        });
1737
1738        unsafe {
1739            let mo = ffi::rocksdb_mergeoperator_create(
1740                Box::into_raw(cb).cast::<c_void>(),
1741                Some(merge_operator::destructor_callback::<F, PF>),
1742                Some(full_merge_callback::<F, PF>),
1743                Some(partial_merge_callback::<F, PF>),
1744                Some(merge_operator::delete_callback),
1745                Some(merge_operator::name_callback::<F, PF>),
1746            );
1747            ffi::rocksdb_options_set_merge_operator(self.inner, mo);
1748        }
1749    }
1750
1751    #[deprecated(
1752        since = "0.5.0",
1753        note = "add_merge_operator has been renamed to set_merge_operator"
1754    )]
1755    pub fn add_merge_operator<F: MergeFn + Clone>(&mut self, name: &str, merge_fn: F) {
1756        self.set_merge_operator_associative(name, merge_fn);
1757    }
1758
1759    /// Sets a compaction filter used to determine if entries should be kept, changed,
1760    /// or removed during compaction.
1761    ///
1762    /// An example use case is to remove entries with an expired TTL.
1763    ///
1764    /// If you take a snapshot of the database, only values written since the last
1765    /// snapshot will be passed through the compaction filter.
1766    ///
1767    /// If multi-threaded compaction is used, `filter_fn` may be called multiple times
1768    /// simultaneously.
1769    pub fn set_compaction_filter<F>(&mut self, name: impl CStrLike, filter_fn: F)
1770    where
1771        F: CompactionFilterFn + Send + 'static,
1772    {
1773        let cb = Box::new(CompactionFilterCallback {
1774            name: name.into_c_string().unwrap(),
1775            filter_fn,
1776        });
1777
1778        let filter = unsafe {
1779            let cf = ffi::rocksdb_compactionfilter_create(
1780                Box::into_raw(cb).cast::<c_void>(),
1781                Some(compaction_filter::destructor_callback::<CompactionFilterCallback<F>>),
1782                Some(compaction_filter::filter_callback::<CompactionFilterCallback<F>>),
1783                Some(compaction_filter::name_callback::<CompactionFilterCallback<F>>),
1784            );
1785            ffi::rocksdb_options_set_compaction_filter(self.inner, cf);
1786
1787            OwnedCompactionFilter::new(NonNull::new(cf).unwrap())
1788        };
1789        self.outlive.compaction_filter = Some(Arc::new(filter));
1790    }
1791
1792    pub fn add_event_listener<L: EventListener>(&mut self, l: L) {
1793        let handle = new_event_listener(l);
1794        unsafe { ffi::rust_rocksdb_options_add_eventlistener(self.inner, handle.inner) }
1795    }
1796
1797    /// This is a factory that provides compaction filter objects which allow
1798    /// an application to modify/delete a key-value during background compaction.
1799    ///
1800    /// A new filter will be created on each compaction run.  If multithreaded
1801    /// compaction is being used, each created CompactionFilter will only be used
1802    /// from a single thread and so does not need to be thread-safe.
1803    ///
1804    /// Default: nullptr
1805    pub fn set_compaction_filter_factory<F>(&mut self, factory: F)
1806    where
1807        F: CompactionFilterFactory + 'static,
1808    {
1809        let factory = Box::new(factory);
1810
1811        unsafe {
1812            let cff = ffi::rocksdb_compactionfilterfactory_create(
1813                Box::into_raw(factory).cast::<c_void>(),
1814                Some(compaction_filter_factory::destructor_callback::<F>),
1815                Some(compaction_filter_factory::create_compaction_filter_callback::<F>),
1816                Some(compaction_filter_factory::name_callback::<F>),
1817            );
1818
1819            ffi::rocksdb_options_set_compaction_filter_factory(self.inner, cff);
1820        }
1821    }
1822
1823    /// Sets the comparator used to define the order of keys in the table.
1824    /// Default: a comparator that uses lexicographic byte-wise ordering
1825    ///
1826    /// The client must ensure that the comparator supplied here has the same
1827    /// name and orders keys *exactly* the same as the comparator provided to
1828    /// previous open calls on the same DB.
1829    pub fn set_comparator(&mut self, name: impl CStrLike, compare_fn: Box<CompareFn>) {
1830        let cb = Box::new(ComparatorCallback {
1831            name: name.into_c_string().unwrap(),
1832            compare_fn,
1833        });
1834
1835        let cmp = unsafe {
1836            let cmp = ffi::rocksdb_comparator_create(
1837                Box::into_raw(cb).cast::<c_void>(),
1838                Some(ComparatorCallback::destructor_callback),
1839                Some(ComparatorCallback::compare_callback),
1840                Some(ComparatorCallback::name_callback),
1841            );
1842            ffi::rocksdb_options_set_comparator(self.inner, cmp);
1843            OwnedComparator::new(NonNull::new(cmp).unwrap())
1844        };
1845        self.outlive.comparator = Some(Arc::new(cmp));
1846    }
1847
1848    /// Sets the comparator that are timestamp-aware, used to define the order of keys in the table,
1849    /// taking timestamp into consideration.
1850    /// Find more information on timestamp-aware comparator on [here](https://github.com/facebook/rocksdb/wiki/User-defined-Timestamp)
1851    ///
1852    /// The client must ensure that the comparator supplied here has the same
1853    /// name and orders keys *exactly* the same as the comparator provided to
1854    /// previous open calls on the same DB.
1855    pub fn set_comparator_with_ts(
1856        &mut self,
1857        name: impl CStrLike,
1858        timestamp_size: usize,
1859        compare_fn: Box<CompareFn>,
1860        compare_ts_fn: Box<CompareTsFn>,
1861        compare_without_ts_fn: Box<CompareWithoutTsFn>,
1862    ) {
1863        let cb = Box::new(ComparatorWithTsCallback {
1864            name: name.into_c_string().unwrap(),
1865            compare_fn,
1866            compare_ts_fn,
1867            compare_without_ts_fn,
1868        });
1869
1870        let cmp = unsafe {
1871            let cmp = ffi::rocksdb_comparator_with_ts_create(
1872                Box::into_raw(cb).cast::<c_void>(),
1873                Some(ComparatorWithTsCallback::destructor_callback),
1874                Some(ComparatorWithTsCallback::compare_callback),
1875                Some(ComparatorWithTsCallback::compare_ts_callback),
1876                Some(ComparatorWithTsCallback::compare_without_ts_callback),
1877                Some(ComparatorWithTsCallback::name_callback),
1878                timestamp_size,
1879            );
1880            ffi::rocksdb_options_set_comparator(self.inner, cmp);
1881            OwnedComparator::new(NonNull::new(cmp).unwrap())
1882        };
1883        self.outlive.comparator = Some(Arc::new(cmp));
1884    }
1885
1886    pub fn set_prefix_extractor(&mut self, prefix_extractor: SliceTransform) {
1887        unsafe {
1888            ffi::rocksdb_options_set_prefix_extractor(self.inner, prefix_extractor.inner);
1889        }
1890    }
1891
1892    // Use this if you don't need to keep the data sorted, i.e. you'll never use
1893    // an iterator, only Put() and Get() API calls
1894    //
1895    pub fn optimize_for_point_lookup(&mut self, block_cache_size_mb: u64) {
1896        unsafe {
1897            ffi::rocksdb_options_optimize_for_point_lookup(self.inner, block_cache_size_mb);
1898        }
1899    }
1900
1901    /// Sets the optimize_filters_for_hits flag
1902    ///
1903    /// Default: `false`
1904    ///
1905    /// # Examples
1906    ///
1907    /// ```
1908    /// use rust_rocksdb::Options;
1909    ///
1910    /// let mut opts = Options::default();
1911    /// opts.set_optimize_filters_for_hits(true);
1912    /// ```
1913    pub fn set_optimize_filters_for_hits(&mut self, optimize_for_hits: bool) {
1914        unsafe {
1915            ffi::rocksdb_options_set_optimize_filters_for_hits(
1916                self.inner,
1917                c_int::from(optimize_for_hits),
1918            );
1919        }
1920    }
1921
1922    /// Sets the periodicity when obsolete files get deleted.
1923    ///
1924    /// The files that get out of scope by compaction
1925    /// process will still get automatically delete on every compaction,
1926    /// regardless of this setting.
1927    ///
1928    /// Default: 6 hours
1929    pub fn set_delete_obsolete_files_period_micros(&mut self, micros: u64) {
1930        unsafe {
1931            ffi::rocksdb_options_set_delete_obsolete_files_period_micros(self.inner, micros);
1932        }
1933    }
1934
1935    /// Prepare the DB for bulk loading.
1936    ///
1937    /// All data will be in level 0 without any automatic compaction.
1938    /// It's recommended to manually call CompactRange(NULL, NULL) before reading
1939    /// from the database, because otherwise the read can be very slow.
1940    pub fn prepare_for_bulk_load(&mut self) {
1941        unsafe {
1942            ffi::rocksdb_options_prepare_for_bulk_load(self.inner);
1943        }
1944    }
1945
1946    /// Sets the number of open files that can be used by the DB. You may need to
1947    /// increase this if your database has a large working set. Value `-1` means
1948    /// files opened are always kept open. You can estimate number of files based
1949    /// on target_file_size_base and target_file_size_multiplier for level-based
1950    /// compaction. For universal-style compaction, you can usually set it to `-1`.
1951    ///
1952    /// Default: `-1`
1953    ///
1954    /// # Examples
1955    ///
1956    /// ```
1957    /// use rust_rocksdb::Options;
1958    ///
1959    /// let mut opts = Options::default();
1960    /// opts.set_max_open_files(10);
1961    /// ```
1962    pub fn set_max_open_files(&mut self, nfiles: c_int) {
1963        unsafe {
1964            ffi::rocksdb_options_set_max_open_files(self.inner, nfiles);
1965        }
1966    }
1967
1968    /// If max_open_files is -1, DB will open all files on DB::Open(). You can
1969    /// use this option to increase the number of threads used to open the files.
1970    /// Default: 16
1971    pub fn set_max_file_opening_threads(&mut self, nthreads: c_int) {
1972        unsafe {
1973            ffi::rocksdb_options_set_max_file_opening_threads(self.inner, nthreads);
1974        }
1975    }
1976
1977    /// By default, writes to stable storage use fdatasync (on platforms
1978    /// where this function is available). If this option is true,
1979    /// fsync is used instead.
1980    ///
1981    /// fsync and fdatasync are equally safe for our purposes and fdatasync is
1982    /// faster, so it is rarely necessary to set this option. It is provided
1983    /// as a workaround for kernel/filesystem bugs, such as one that affected
1984    /// fdatasync with ext4 in kernel versions prior to 3.7.
1985    ///
1986    /// Default: `false`
1987    ///
1988    /// # Examples
1989    ///
1990    /// ```
1991    /// use rust_rocksdb::Options;
1992    ///
1993    /// let mut opts = Options::default();
1994    /// opts.set_use_fsync(true);
1995    /// ```
1996    pub fn set_use_fsync(&mut self, useit: bool) {
1997        unsafe {
1998            ffi::rocksdb_options_set_use_fsync(self.inner, c_int::from(useit));
1999        }
2000    }
2001
2002    /// Returns the value of the `use_fsync` option.
2003    pub fn get_use_fsync(&self) -> bool {
2004        let val = unsafe { ffi::rocksdb_options_get_use_fsync(self.inner) };
2005        val != 0
2006    }
2007
2008    /// Specifies the absolute info LOG dir.
2009    ///
2010    /// If it is empty, the log files will be in the same dir as data.
2011    /// If it is non empty, the log files will be in the specified dir,
2012    /// and the db data dir's absolute path will be used as the log file
2013    /// name's prefix.
2014    ///
2015    /// Default: empty
2016    pub fn set_db_log_dir<P: AsRef<Path>>(&mut self, path: P) {
2017        let p = to_cpath(path).unwrap();
2018        unsafe {
2019            ffi::rocksdb_options_set_db_log_dir(self.inner, p.as_ptr());
2020        }
2021    }
2022
2023    /// Specifies the log level.
2024    /// Consider the `LogLevel` enum for a list of possible levels.
2025    ///
2026    /// Default: Info
2027    ///
2028    /// # Examples
2029    ///
2030    /// ```
2031    /// use rust_rocksdb::{Options, LogLevel};
2032    ///
2033    /// let mut opts = Options::default();
2034    /// opts.set_log_level(LogLevel::Warn);
2035    /// ```
2036    pub fn set_log_level(&mut self, level: LogLevel) {
2037        unsafe {
2038            ffi::rocksdb_options_set_info_log_level(self.inner, level as c_int);
2039        }
2040    }
2041
2042    /// Allows OS to incrementally sync files to disk while they are being
2043    /// written, asynchronously, in the background. This operation can be used
2044    /// to smooth out write I/Os over time. Users shouldn't rely on it for
2045    /// persistency guarantee.
2046    /// Issue one request for every bytes_per_sync written. `0` turns it off.
2047    ///
2048    /// Default: `0`
2049    ///
2050    /// You may consider using rate_limiter to regulate write rate to device.
2051    /// When rate limiter is enabled, it automatically enables bytes_per_sync
2052    /// to 1MB.
2053    ///
2054    /// This option applies to table files
2055    ///
2056    /// # Examples
2057    ///
2058    /// ```
2059    /// use rust_rocksdb::Options;
2060    ///
2061    /// let mut opts = Options::default();
2062    /// opts.set_bytes_per_sync(1024 * 1024);
2063    /// ```
2064    pub fn set_bytes_per_sync(&mut self, nbytes: u64) {
2065        unsafe {
2066            ffi::rocksdb_options_set_bytes_per_sync(self.inner, nbytes);
2067        }
2068    }
2069
2070    /// Same as bytes_per_sync, but applies to WAL files.
2071    ///
2072    /// Default: 0, turned off
2073    ///
2074    /// Dynamically changeable through SetDBOptions() API.
2075    pub fn set_wal_bytes_per_sync(&mut self, nbytes: u64) {
2076        unsafe {
2077            ffi::rocksdb_options_set_wal_bytes_per_sync(self.inner, nbytes);
2078        }
2079    }
2080
2081    /// Sets the maximum buffer size that is used by WritableFileWriter.
2082    ///
2083    /// On Windows, we need to maintain an aligned buffer for writes.
2084    /// We allow the buffer to grow until it's size hits the limit in buffered
2085    /// IO and fix the buffer size when using direct IO to ensure alignment of
2086    /// write requests if the logical sector size is unusual
2087    ///
2088    /// Default: 1024 * 1024 (1 MB)
2089    ///
2090    /// Dynamically changeable through SetDBOptions() API.
2091    pub fn set_writable_file_max_buffer_size(&mut self, nbytes: u64) {
2092        unsafe {
2093            ffi::rocksdb_options_set_writable_file_max_buffer_size(self.inner, nbytes);
2094        }
2095    }
2096
2097    /// If true, allow multi-writers to update mem tables in parallel.
2098    /// Only some memtable_factory-s support concurrent writes; currently it
2099    /// is implemented only for SkipListFactory.  Concurrent memtable writes
2100    /// are not compatible with inplace_update_support or filter_deletes.
2101    /// It is strongly recommended to set enable_write_thread_adaptive_yield
2102    /// if you are going to use this feature.
2103    ///
2104    /// Default: true
2105    ///
2106    /// # Examples
2107    ///
2108    /// ```
2109    /// use rust_rocksdb::Options;
2110    ///
2111    /// let mut opts = Options::default();
2112    /// opts.set_allow_concurrent_memtable_write(false);
2113    /// ```
2114    pub fn set_allow_concurrent_memtable_write(&mut self, allow: bool) {
2115        unsafe {
2116            ffi::rocksdb_options_set_allow_concurrent_memtable_write(
2117                self.inner,
2118                c_uchar::from(allow),
2119            );
2120        }
2121    }
2122
2123    /// If true, threads synchronizing with the write batch group leader will wait for up to
2124    /// write_thread_max_yield_usec before blocking on a mutex. This can substantially improve
2125    /// throughput for concurrent workloads, regardless of whether allow_concurrent_memtable_write
2126    /// is enabled.
2127    ///
2128    /// Default: true
2129    pub fn set_enable_write_thread_adaptive_yield(&mut self, enabled: bool) {
2130        unsafe {
2131            ffi::rocksdb_options_set_enable_write_thread_adaptive_yield(
2132                self.inner,
2133                c_uchar::from(enabled),
2134            );
2135        }
2136    }
2137
2138    /// Specifies whether an iteration->Next() sequentially skips over keys with the same user-key or not.
2139    ///
2140    /// This number specifies the number of keys (with the same userkey)
2141    /// that will be sequentially skipped before a reseek is issued.
2142    ///
2143    /// Default: 8
2144    pub fn set_max_sequential_skip_in_iterations(&mut self, num: u64) {
2145        unsafe {
2146            ffi::rocksdb_options_set_max_sequential_skip_in_iterations(self.inner, num);
2147        }
2148    }
2149
2150    /// Enable direct I/O mode for reading
2151    /// they may or may not improve performance depending on the use case
2152    ///
2153    /// Files will be opened in "direct I/O" mode
2154    /// which means that data read from the disk will not be cached or
2155    /// buffered. The hardware buffer of the devices may however still
2156    /// be used. Memory mapped files are not impacted by these parameters.
2157    ///
2158    /// Default: false
2159    ///
2160    /// # Examples
2161    ///
2162    /// ```
2163    /// use rust_rocksdb::Options;
2164    ///
2165    /// let mut opts = Options::default();
2166    /// opts.set_use_direct_reads(true);
2167    /// ```
2168    pub fn set_use_direct_reads(&mut self, enabled: bool) {
2169        unsafe {
2170            ffi::rocksdb_options_set_use_direct_reads(self.inner, c_uchar::from(enabled));
2171        }
2172    }
2173
2174    /// Enable direct I/O mode for flush and compaction
2175    ///
2176    /// Files will be opened in "direct I/O" mode
2177    /// which means that data written to the disk will not be cached or
2178    /// buffered. The hardware buffer of the devices may however still
2179    /// be used. Memory mapped files are not impacted by these parameters.
2180    /// they may or may not improve performance depending on the use case
2181    ///
2182    /// Default: false
2183    ///
2184    /// # Examples
2185    ///
2186    /// ```
2187    /// use rust_rocksdb::Options;
2188    ///
2189    /// let mut opts = Options::default();
2190    /// opts.set_use_direct_io_for_flush_and_compaction(true);
2191    /// ```
2192    pub fn set_use_direct_io_for_flush_and_compaction(&mut self, enabled: bool) {
2193        unsafe {
2194            ffi::rocksdb_options_set_use_direct_io_for_flush_and_compaction(
2195                self.inner,
2196                c_uchar::from(enabled),
2197            );
2198        }
2199    }
2200
2201    /// Enable/disable child process inherit open files.
2202    ///
2203    /// Default: true
2204    pub fn set_is_fd_close_on_exec(&mut self, enabled: bool) {
2205        unsafe {
2206            ffi::rocksdb_options_set_is_fd_close_on_exec(self.inner, c_uchar::from(enabled));
2207        }
2208    }
2209
2210    /// Hints to the OS that it should not buffer disk I/O. Enabling this
2211    /// parameter may improve performance but increases pressure on the
2212    /// system cache.
2213    ///
2214    /// The exact behavior of this parameter is platform dependent.
2215    ///
2216    /// On POSIX systems, after RocksDB reads data from disk it will
2217    /// mark the pages as "unneeded". The operating system may or may not
2218    /// evict these pages from memory, reducing pressure on the system
2219    /// cache. If the disk block is requested again this can result in
2220    /// additional disk I/O.
2221    ///
2222    /// On WINDOWS systems, files will be opened in "unbuffered I/O" mode
2223    /// which means that data read from the disk will not be cached or
2224    /// bufferized. The hardware buffer of the devices may however still
2225    /// be used. Memory mapped files are not impacted by this parameter.
2226    ///
2227    /// Default: true
2228    ///
2229    /// # Examples
2230    ///
2231    /// ```
2232    /// use rust_rocksdb::Options;
2233    ///
2234    /// let mut opts = Options::default();
2235    /// #[allow(deprecated)]
2236    /// opts.set_allow_os_buffer(false);
2237    /// ```
2238    #[deprecated(
2239        since = "0.7.0",
2240        note = "replaced with set_use_direct_reads/set_use_direct_io_for_flush_and_compaction methods"
2241    )]
2242    pub fn set_allow_os_buffer(&mut self, is_allow: bool) {
2243        self.set_use_direct_reads(!is_allow);
2244        self.set_use_direct_io_for_flush_and_compaction(!is_allow);
2245    }
2246
2247    /// Sets the number of shards used for table cache.
2248    ///
2249    /// Default: `6`
2250    ///
2251    /// # Examples
2252    ///
2253    /// ```
2254    /// use rust_rocksdb::Options;
2255    ///
2256    /// let mut opts = Options::default();
2257    /// opts.set_table_cache_num_shard_bits(4);
2258    /// ```
2259    pub fn set_table_cache_num_shard_bits(&mut self, nbits: c_int) {
2260        unsafe {
2261            ffi::rocksdb_options_set_table_cache_numshardbits(self.inner, nbits);
2262        }
2263    }
2264
2265    /// By default target_file_size_multiplier is 1, which means
2266    /// by default files in different levels will have similar size.
2267    ///
2268    /// Dynamically changeable through SetOptions() API
2269    pub fn set_target_file_size_multiplier(&mut self, multiplier: i32) {
2270        unsafe {
2271            ffi::rocksdb_options_set_target_file_size_multiplier(self.inner, multiplier as c_int);
2272        }
2273    }
2274
2275    /// Sets the minimum number of write buffers that will be merged
2276    /// before writing to storage.  If set to `1`, then
2277    /// all write buffers are flushed to L0 as individual files and this increases
2278    /// read amplification because a get request has to check in all of these
2279    /// files. Also, an in-memory merge may result in writing lesser
2280    /// data to storage if there are duplicate records in each of these
2281    /// individual write buffers.
2282    ///
2283    /// Default: `1`
2284    ///
2285    /// # Examples
2286    ///
2287    /// ```
2288    /// use rust_rocksdb::Options;
2289    ///
2290    /// let mut opts = Options::default();
2291    /// opts.set_min_write_buffer_number(2);
2292    /// ```
2293    pub fn set_min_write_buffer_number(&mut self, nbuf: c_int) {
2294        unsafe {
2295            ffi::rocksdb_options_set_min_write_buffer_number_to_merge(self.inner, nbuf);
2296        }
2297    }
2298
2299    /// Sets the maximum number of write buffers that are built up in memory.
2300    /// The default and the minimum number is 2, so that when 1 write buffer
2301    /// is being flushed to storage, new writes can continue to the other
2302    /// write buffer.
2303    /// If max_write_buffer_number > 3, writing will be slowed down to
2304    /// options.delayed_write_rate if we are writing to the last write buffer
2305    /// allowed.
2306    ///
2307    /// Default: `2`
2308    ///
2309    /// # Examples
2310    ///
2311    /// ```
2312    /// use rust_rocksdb::Options;
2313    ///
2314    /// let mut opts = Options::default();
2315    /// opts.set_max_write_buffer_number(4);
2316    /// ```
2317    pub fn set_max_write_buffer_number(&mut self, nbuf: c_int) {
2318        unsafe {
2319            ffi::rocksdb_options_set_max_write_buffer_number(self.inner, nbuf);
2320        }
2321    }
2322
2323    /// Sets the amount of data to build up in memory (backed by an unsorted log
2324    /// on disk) before converting to a sorted on-disk file.
2325    ///
2326    /// Larger values increase performance, especially during bulk loads.
2327    /// Up to max_write_buffer_number write buffers may be held in memory
2328    /// at the same time,
2329    /// so you may wish to adjust this parameter to control memory usage.
2330    /// Also, a larger write buffer will result in a longer recovery time
2331    /// the next time the database is opened.
2332    ///
2333    /// Note that write_buffer_size is enforced per column family.
2334    /// See db_write_buffer_size for sharing memory across column families.
2335    ///
2336    /// Default: `0x4000000` (64MiB)
2337    ///
2338    /// Dynamically changeable through SetOptions() API
2339    ///
2340    /// # Examples
2341    ///
2342    /// ```
2343    /// use rust_rocksdb::Options;
2344    ///
2345    /// let mut opts = Options::default();
2346    /// opts.set_write_buffer_size(128 * 1024 * 1024);
2347    /// ```
2348    pub fn set_write_buffer_size(&mut self, size: usize) {
2349        unsafe {
2350            ffi::rocksdb_options_set_write_buffer_size(self.inner, size);
2351        }
2352    }
2353
2354    /// Amount of data to build up in memtables across all column
2355    /// families before writing to disk.
2356    ///
2357    /// This is distinct from write_buffer_size, which enforces a limit
2358    /// for a single memtable.
2359    ///
2360    /// This feature is disabled by default. Specify a non-zero value
2361    /// to enable it.
2362    ///
2363    /// Default: 0 (disabled)
2364    ///
2365    /// # Examples
2366    ///
2367    /// ```
2368    /// use rust_rocksdb::Options;
2369    ///
2370    /// let mut opts = Options::default();
2371    /// opts.set_db_write_buffer_size(128 * 1024 * 1024);
2372    /// ```
2373    pub fn set_db_write_buffer_size(&mut self, size: usize) {
2374        unsafe {
2375            ffi::rocksdb_options_set_db_write_buffer_size(self.inner, size);
2376        }
2377    }
2378
2379    /// Control maximum total data size for a level.
2380    /// max_bytes_for_level_base is the max total for level-1.
2381    /// Maximum number of bytes for level L can be calculated as
2382    /// (max_bytes_for_level_base) * (max_bytes_for_level_multiplier ^ (L-1))
2383    /// For example, if max_bytes_for_level_base is 200MB, and if
2384    /// max_bytes_for_level_multiplier is 10, total data size for level-1
2385    /// will be 200MB, total file size for level-2 will be 2GB,
2386    /// and total file size for level-3 will be 20GB.
2387    ///
2388    /// Default: `0x10000000` (256MiB).
2389    ///
2390    /// Dynamically changeable through SetOptions() API
2391    ///
2392    /// # Examples
2393    ///
2394    /// ```
2395    /// use rust_rocksdb::Options;
2396    ///
2397    /// let mut opts = Options::default();
2398    /// opts.set_max_bytes_for_level_base(512 * 1024 * 1024);
2399    /// ```
2400    pub fn set_max_bytes_for_level_base(&mut self, size: u64) {
2401        unsafe {
2402            ffi::rocksdb_options_set_max_bytes_for_level_base(self.inner, size);
2403        }
2404    }
2405
2406    /// Default: `10`
2407    ///
2408    /// # Examples
2409    ///
2410    /// ```
2411    /// use rust_rocksdb::Options;
2412    ///
2413    /// let mut opts = Options::default();
2414    /// opts.set_max_bytes_for_level_multiplier(4.0);
2415    /// ```
2416    pub fn set_max_bytes_for_level_multiplier(&mut self, mul: f64) {
2417        unsafe {
2418            ffi::rocksdb_options_set_max_bytes_for_level_multiplier(self.inner, mul);
2419        }
2420    }
2421
2422    /// Sets a lower bound on the auto-tuned MANIFEST size limit. The MANIFEST
2423    /// is rolled over on reaching the limit and the older one is deleted.
2424    ///
2425    /// This used to be a hard limit. RocksDB now auto-tunes the real limit and
2426    /// treats this as a minimum, so setting it small does not keep the MANIFEST
2427    /// small. Batches written in the foreground get a 25% higher limit.
2428    ///
2429    /// Default: 1 GiB.
2430    ///
2431    /// # Examples
2432    ///
2433    /// ```
2434    /// use rust_rocksdb::Options;
2435    ///
2436    /// let mut opts = Options::default();
2437    /// opts.set_max_manifest_file_size(20 * 1024 * 1024);
2438    /// ```
2439    pub fn set_max_manifest_file_size(&mut self, size: usize) {
2440        unsafe {
2441            ffi::rocksdb_options_set_max_manifest_file_size(self.inner, size);
2442        }
2443    }
2444
2445    /// Sets the target file size for compaction.
2446    /// target_file_size_base is per-file size for level-1.
2447    /// Target file size for level L can be calculated by
2448    /// target_file_size_base * (target_file_size_multiplier ^ (L-1))
2449    /// For example, if target_file_size_base is 2MB and
2450    /// target_file_size_multiplier is 10, then each file on level-1 will
2451    /// be 2MB, and each file on level 2 will be 20MB,
2452    /// and each file on level-3 will be 200MB.
2453    ///
2454    /// Default: `0x4000000` (64MiB)
2455    ///
2456    /// Dynamically changeable through SetOptions() API
2457    ///
2458    /// # Examples
2459    ///
2460    /// ```
2461    /// use rust_rocksdb::Options;
2462    ///
2463    /// let mut opts = Options::default();
2464    /// opts.set_target_file_size_base(128 * 1024 * 1024);
2465    /// ```
2466    pub fn set_target_file_size_base(&mut self, size: u64) {
2467        unsafe {
2468            ffi::rocksdb_options_set_target_file_size_base(self.inner, size);
2469        }
2470    }
2471
2472    /// Sets the minimum number of write buffers that will be merged together
2473    /// before writing to storage.  If set to `1`, then
2474    /// all write buffers are flushed to L0 as individual files and this increases
2475    /// read amplification because a get request has to check in all of these
2476    /// files. Also, an in-memory merge may result in writing lesser
2477    /// data to storage if there are duplicate records in each of these
2478    /// individual write buffers.
2479    ///
2480    /// Default: `1`
2481    ///
2482    /// # Examples
2483    ///
2484    /// ```
2485    /// use rust_rocksdb::Options;
2486    ///
2487    /// let mut opts = Options::default();
2488    /// opts.set_min_write_buffer_number_to_merge(2);
2489    /// ```
2490    pub fn set_min_write_buffer_number_to_merge(&mut self, to_merge: c_int) {
2491        unsafe {
2492            ffi::rocksdb_options_set_min_write_buffer_number_to_merge(self.inner, to_merge);
2493        }
2494    }
2495
2496    /// Sets the number of files to trigger level-0 compaction. A value < `0` means that
2497    /// level-0 compaction will not be triggered by number of files at all.
2498    ///
2499    /// Default: `4`
2500    ///
2501    /// Dynamically changeable through SetOptions() API
2502    ///
2503    /// # Examples
2504    ///
2505    /// ```
2506    /// use rust_rocksdb::Options;
2507    ///
2508    /// let mut opts = Options::default();
2509    /// opts.set_level_zero_file_num_compaction_trigger(8);
2510    /// ```
2511    pub fn set_level_zero_file_num_compaction_trigger(&mut self, n: c_int) {
2512        unsafe {
2513            ffi::rocksdb_options_set_level0_file_num_compaction_trigger(self.inner, n);
2514        }
2515    }
2516
2517    /// Sets the soft limit on number of level-0 files. We start slowing down writes at this
2518    /// point. A value < `0` means that no writing slowdown will be triggered by
2519    /// number of files in level-0.
2520    ///
2521    /// Default: `20`
2522    ///
2523    /// Dynamically changeable through SetOptions() API
2524    ///
2525    /// # Examples
2526    ///
2527    /// ```
2528    /// use rust_rocksdb::Options;
2529    ///
2530    /// let mut opts = Options::default();
2531    /// opts.set_level_zero_slowdown_writes_trigger(10);
2532    /// ```
2533    pub fn set_level_zero_slowdown_writes_trigger(&mut self, n: c_int) {
2534        unsafe {
2535            ffi::rocksdb_options_set_level0_slowdown_writes_trigger(self.inner, n);
2536        }
2537    }
2538
2539    /// Sets the maximum number of level-0 files.  We stop writes at this point.
2540    ///
2541    /// Default: `36`
2542    ///
2543    /// Dynamically changeable through SetOptions() API
2544    ///
2545    /// # Examples
2546    ///
2547    /// ```
2548    /// use rust_rocksdb::Options;
2549    ///
2550    /// let mut opts = Options::default();
2551    /// opts.set_level_zero_stop_writes_trigger(48);
2552    /// ```
2553    pub fn set_level_zero_stop_writes_trigger(&mut self, n: c_int) {
2554        unsafe {
2555            ffi::rocksdb_options_set_level0_stop_writes_trigger(self.inner, n);
2556        }
2557    }
2558
2559    /// Sets the compaction style.
2560    ///
2561    /// Default: DBCompactionStyle::Level
2562    ///
2563    /// # Examples
2564    ///
2565    /// ```
2566    /// use rust_rocksdb::{Options, DBCompactionStyle};
2567    ///
2568    /// let mut opts = Options::default();
2569    /// opts.set_compaction_style(DBCompactionStyle::Universal);
2570    /// ```
2571    pub fn set_compaction_style(&mut self, style: DBCompactionStyle) {
2572        unsafe {
2573            ffi::rocksdb_options_set_compaction_style(self.inner, style as c_int);
2574        }
2575    }
2576
2577    /// Sets the options needed to support Universal Style compactions.
2578    pub fn set_universal_compaction_options(&mut self, uco: &UniversalCompactOptions) {
2579        unsafe {
2580            ffi::rocksdb_options_set_universal_compaction_options(self.inner, uco.inner);
2581        }
2582    }
2583
2584    /// Sets the options for FIFO compaction style.
2585    pub fn set_fifo_compaction_options(&mut self, fco: &FifoCompactOptions) {
2586        unsafe {
2587            ffi::rocksdb_options_set_fifo_compaction_options(self.inner, fco.inner);
2588        }
2589    }
2590
2591    /// Sets unordered_write to true trades higher write throughput with
2592    /// relaxing the immutability guarantee of snapshots. This violates the
2593    /// repeatability one expects from ::Get from a snapshot, as well as
2594    /// ::MultiGet and Iterator's consistent-point-in-time view property.
2595    /// If the application cannot tolerate the relaxed guarantees, it can implement
2596    /// its own mechanisms to work around that and yet benefit from the higher
2597    /// throughput. Using TransactionDB with WRITE_PREPARED write policy and
2598    /// two_write_queues=true is one way to achieve immutable snapshots despite
2599    /// unordered_write.
2600    ///
2601    /// By default, i.e., when it is false, rocksdb does not advance the sequence
2602    /// number for new snapshots unless all the writes with lower sequence numbers
2603    /// are already finished. This provides the immutability that we expect from
2604    /// snapshots. Moreover, since Iterator and MultiGet internally depend on
2605    /// snapshots, the snapshot immutability results into Iterator and MultiGet
2606    /// offering consistent-point-in-time view. If set to true, although
2607    /// Read-Your-Own-Write property is still provided, the snapshot immutability
2608    /// property is relaxed: the writes issued after the snapshot is obtained (with
2609    /// larger sequence numbers) will be still not visible to the reads from that
2610    /// snapshot, however, there still might be pending writes (with lower sequence
2611    /// number) that will change the state visible to the snapshot after they are
2612    /// landed to the memtable.
2613    ///
2614    /// Default: false
2615    pub fn set_unordered_write(&mut self, unordered: bool) {
2616        unsafe {
2617            ffi::rocksdb_options_set_unordered_write(self.inner, c_uchar::from(unordered));
2618        }
2619    }
2620
2621    /// Sets maximum number of threads that will
2622    /// concurrently perform a compaction job by breaking it into multiple,
2623    /// smaller ones that are run simultaneously.
2624    ///
2625    /// Default: 1 (i.e. no subcompactions)
2626    pub fn set_max_subcompactions(&mut self, num: u32) {
2627        unsafe {
2628            ffi::rocksdb_options_set_max_subcompactions(self.inner, num);
2629        }
2630    }
2631
2632    /// Sets maximum number of concurrent background jobs
2633    /// (compactions and flushes).
2634    ///
2635    /// Default: 2
2636    ///
2637    /// Dynamically changeable through SetDBOptions() API.
2638    pub fn set_max_background_jobs(&mut self, jobs: c_int) {
2639        unsafe {
2640            ffi::rocksdb_options_set_max_background_jobs(self.inner, jobs);
2641        }
2642    }
2643
2644    /// Sets the maximum number of concurrent background compaction jobs, submitted to
2645    /// the default LOW priority thread pool.
2646    /// We first try to schedule compactions based on
2647    /// `base_background_compactions`. If the compaction cannot catch up , we
2648    /// will increase number of compaction threads up to
2649    /// `max_background_compactions`.
2650    ///
2651    /// If you're increasing this, also consider increasing number of threads in
2652    /// LOW priority thread pool. For more information, see
2653    /// Env::SetBackgroundThreads
2654    ///
2655    /// Default: `-1`, meaning RocksDB derives it from `max_background_jobs`.
2656    /// Setting either this or `max_background_flushes` opts into the old
2657    /// behaviour, where the unset one of the pair counts as `1`.
2658    ///
2659    /// # Examples
2660    ///
2661    /// ```
2662    /// use rust_rocksdb::Options;
2663    ///
2664    /// let mut opts = Options::default();
2665    /// #[allow(deprecated)]
2666    /// opts.set_max_background_compactions(2);
2667    /// ```
2668    #[deprecated(
2669        since = "0.15.0",
2670        note = "RocksDB automatically decides this based on the value of max_background_jobs"
2671    )]
2672    pub fn set_max_background_compactions(&mut self, n: c_int) {
2673        unsafe {
2674            ffi::rocksdb_options_set_max_background_compactions(self.inner, n);
2675        }
2676    }
2677
2678    /// Sets the maximum number of concurrent background memtable flush jobs, submitted to
2679    /// the HIGH priority thread pool.
2680    ///
2681    /// By default, all background jobs (major compaction and memtable flush) go
2682    /// to the LOW priority pool. If this option is set to a positive number,
2683    /// memtable flush jobs will be submitted to the HIGH priority pool.
2684    /// It is important when the same Env is shared by multiple db instances.
2685    /// Without a separate pool, long running major compaction jobs could
2686    /// potentially block memtable flush jobs of other db instances, leading to
2687    /// unnecessary Put stalls.
2688    ///
2689    /// If you're increasing this, also consider increasing number of threads in
2690    /// HIGH priority thread pool. For more information, see
2691    /// Env::SetBackgroundThreads
2692    ///
2693    /// Default: `-1`, meaning RocksDB derives it from `max_background_jobs`.
2694    /// Setting either this or `max_background_compactions` opts into the old
2695    /// behaviour, where the unset one of the pair counts as `1`.
2696    ///
2697    /// # Examples
2698    ///
2699    /// ```
2700    /// use rust_rocksdb::Options;
2701    ///
2702    /// let mut opts = Options::default();
2703    /// #[allow(deprecated)]
2704    /// opts.set_max_background_flushes(2);
2705    /// ```
2706    #[deprecated(
2707        since = "0.15.0",
2708        note = "RocksDB automatically decides this based on the value of max_background_jobs"
2709    )]
2710    pub fn set_max_background_flushes(&mut self, n: c_int) {
2711        unsafe {
2712            ffi::rocksdb_options_set_max_background_flushes(self.inner, n);
2713        }
2714    }
2715
2716    /// Disables automatic compactions. Manual compactions can still
2717    /// be issued on this column family
2718    ///
2719    /// Default: `false`
2720    ///
2721    /// Dynamically changeable through SetOptions() API
2722    ///
2723    /// # Examples
2724    ///
2725    /// ```
2726    /// use rust_rocksdb::Options;
2727    ///
2728    /// let mut opts = Options::default();
2729    /// opts.set_disable_auto_compactions(true);
2730    /// ```
2731    pub fn set_disable_auto_compactions(&mut self, disable: bool) {
2732        unsafe {
2733            ffi::rocksdb_options_set_disable_auto_compactions(self.inner, c_int::from(disable));
2734        }
2735    }
2736
2737    /// SetMemtableHugePageSize sets the page size for huge page for
2738    /// arena used by the memtable.
2739    /// If <=0, it won't allocate from huge page but from malloc.
2740    /// Users are responsible to reserve huge pages for it to be allocated. For
2741    /// example:
2742    ///      sysctl -w vm.nr_hugepages=20
2743    /// See linux doc Documentation/vm/hugetlbpage.txt
2744    /// If there isn't enough free huge page available, it will fall back to
2745    /// malloc.
2746    ///
2747    /// Dynamically changeable through SetOptions() API
2748    pub fn set_memtable_huge_page_size(&mut self, size: size_t) {
2749        unsafe {
2750            ffi::rocksdb_options_set_memtable_huge_page_size(self.inner, size);
2751        }
2752    }
2753
2754    /// Enables the skip-list memtable's batch-lookup optimization for
2755    /// `MultiGet`.
2756    ///
2757    /// When enabled, the search path is cached between consecutive keys in a
2758    /// `MultiGet`, reducing per-key cost from `O(log N)` to `O(log d)` where
2759    /// `d` is the distance between consecutive keys. The optimization
2760    /// exploits the fact that `MultiGet` keys are sorted.
2761    ///
2762    /// Applies only to the default skip-list memtable (the one used when no
2763    /// memtable factory is set via [`Self::set_memtable_factory`]). The
2764    /// `MemtableFactory::Vector`, `HashSkipList`, and `HashLinkList` variants
2765    /// all fall back to per-key lookups regardless of this flag.
2766    ///
2767    /// This option is immutable on the C++ side: it must be set before the
2768    /// column family is opened and cannot be changed via `SetOptions`.
2769    ///
2770    /// Default: `false`
2771    pub fn set_memtable_batch_lookup_optimization(&mut self, enable: bool) {
2772        unsafe {
2773            ffi::rocksdb_options_set_memtable_batch_lookup_optimization(
2774                self.inner,
2775                c_uchar::from(enable),
2776            );
2777        }
2778    }
2779
2780    /// Returns the current value of
2781    /// [`Self::set_memtable_batch_lookup_optimization`].
2782    ///
2783    /// Provided primarily for tests that want to confirm the setter is wired
2784    /// through to the underlying C++ `AdvancedColumnFamilyOptions`.
2785    pub fn get_memtable_batch_lookup_optimization(&self) -> bool {
2786        unsafe { ffi::rocksdb_options_get_memtable_batch_lookup_optimization(self.inner) != 0 }
2787    }
2788
2789    /// Sets the maximum number of successive merge operations on a key in the memtable.
2790    ///
2791    /// When a merge operation is added to the memtable and the maximum number of
2792    /// successive merges is reached, the value of the key will be calculated and
2793    /// inserted into the memtable instead of the merge operation. This will
2794    /// ensure that there are never more than max_successive_merges merge
2795    /// operations in the memtable.
2796    ///
2797    /// Default: 0 (disabled)
2798    pub fn set_max_successive_merges(&mut self, num: usize) {
2799        unsafe {
2800            ffi::rocksdb_options_set_max_successive_merges(self.inner, num);
2801        }
2802    }
2803
2804    /// Control locality of bloom filter probes to improve cache miss rate.
2805    /// This option only applies to memtable prefix bloom and plaintable
2806    /// prefix bloom. It essentially limits the max number of cache lines each
2807    /// bloom filter check can touch.
2808    ///
2809    /// This optimization is turned off when set to 0. The number should never
2810    /// be greater than number of probes. This option can boost performance
2811    /// for in-memory workload but should use with care since it can cause
2812    /// higher false positive rate.
2813    ///
2814    /// Default: 0
2815    pub fn set_bloom_locality(&mut self, v: u32) {
2816        unsafe {
2817            ffi::rocksdb_options_set_bloom_locality(self.inner, v);
2818        }
2819    }
2820
2821    /// Enable/disable thread-safe inplace updates.
2822    ///
2823    /// Requires updates if
2824    /// * key exists in current memtable
2825    /// * new sizeof(new_value) <= sizeof(old_value)
2826    /// * old_value for that key is a put i.e. kTypeValue
2827    ///
2828    /// Default: false.
2829    pub fn set_inplace_update_support(&mut self, enabled: bool) {
2830        unsafe {
2831            ffi::rocksdb_options_set_inplace_update_support(self.inner, c_uchar::from(enabled));
2832        }
2833    }
2834
2835    /// Sets the number of locks used for inplace update.
2836    ///
2837    /// Default: 10000 when inplace_update_support = true, otherwise 0.
2838    pub fn set_inplace_update_locks(&mut self, num: usize) {
2839        unsafe {
2840            ffi::rocksdb_options_set_inplace_update_num_locks(self.inner, num);
2841        }
2842    }
2843
2844    /// Different max-size multipliers for different levels.
2845    /// These are multiplied by max_bytes_for_level_multiplier to arrive
2846    /// at the max-size of each level.
2847    ///
2848    /// Default: 1
2849    ///
2850    /// Dynamically changeable through SetOptions() API
2851    pub fn set_max_bytes_for_level_multiplier_additional(&mut self, level_values: &[i32]) {
2852        let count = level_values.len();
2853        unsafe {
2854            ffi::rocksdb_options_set_max_bytes_for_level_multiplier_additional(
2855                self.inner,
2856                level_values.as_ptr().cast_mut(),
2857                count,
2858            );
2859        }
2860    }
2861
2862    /// The total maximum size(bytes) of write buffers to maintain in memory
2863    /// including copies of buffers that have already been flushed. This parameter
2864    /// only affects trimming of flushed buffers and does not affect flushing.
2865    /// This controls the maximum amount of write history that will be available
2866    /// in memory for conflict checking when Transactions are used. The actual
2867    /// size of write history (flushed Memtables) might be higher than this limit
2868    /// if further trimming will reduce write history total size below this
2869    /// limit. For example, if max_write_buffer_size_to_maintain is set to 64MB,
2870    /// and there are three flushed Memtables, with sizes of 32MB, 20MB, 20MB.
2871    /// Because trimming the next Memtable of size 20MB will reduce total memory
2872    /// usage to 52MB which is below the limit, RocksDB will stop trimming.
2873    ///
2874    /// When using an OptimisticTransactionDB:
2875    /// If this value is too low, some transactions may fail at commit time due
2876    /// to not being able to determine whether there were any write conflicts.
2877    ///
2878    /// When using a TransactionDB:
2879    /// If Transaction::SetSnapshot is used, TransactionDB will read either
2880    /// in-memory write buffers or SST files to do write-conflict checking.
2881    /// Increasing this value can reduce the number of reads to SST files
2882    /// done for conflict detection.
2883    ///
2884    /// Setting this value to 0 will cause write buffers to be freed immediately
2885    /// after they are flushed. If this value is set to -1,
2886    /// 'max_write_buffer_number * write_buffer_size' will be used.
2887    ///
2888    /// Default:
2889    /// If using a TransactionDB/OptimisticTransactionDB, the default value will
2890    /// be set to the value of 'max_write_buffer_number * write_buffer_size'
2891    /// if it is not explicitly set by the user.  Otherwise, the default is 0.
2892    pub fn set_max_write_buffer_size_to_maintain(&mut self, size: i64) {
2893        unsafe {
2894            ffi::rocksdb_options_set_max_write_buffer_size_to_maintain(self.inner, size);
2895        }
2896    }
2897
2898    /// By default, a single write thread queue is maintained. The thread gets
2899    /// to the head of the queue becomes write batch group leader and responsible
2900    /// for writing to WAL and memtable for the batch group.
2901    ///
2902    /// If enable_pipelined_write is true, separate write thread queue is
2903    /// maintained for WAL write and memtable write. A write thread first enter WAL
2904    /// writer queue and then memtable writer queue. Pending thread on the WAL
2905    /// writer queue thus only have to wait for previous writers to finish their
2906    /// WAL writing but not the memtable writing. Enabling the feature may improve
2907    /// write throughput and reduce latency of the prepare phase of two-phase
2908    /// commit.
2909    ///
2910    /// Default: false
2911    pub fn set_enable_pipelined_write(&mut self, value: bool) {
2912        unsafe {
2913            ffi::rocksdb_options_set_enable_pipelined_write(self.inner, c_uchar::from(value));
2914        }
2915    }
2916
2917    /// Defines the underlying memtable implementation.
2918    /// See official [wiki](https://github.com/facebook/rocksdb/wiki/MemTable) for more information.
2919    /// Defaults to using a skiplist.
2920    ///
2921    /// # Examples
2922    ///
2923    /// ```
2924    /// use rust_rocksdb::{Options, MemtableFactory};
2925    /// let mut opts = Options::default();
2926    /// let factory = MemtableFactory::HashSkipList {
2927    ///     bucket_count: 1_000_000,
2928    ///     height: 4,
2929    ///     branching_factor: 4,
2930    /// };
2931    ///
2932    /// opts.set_allow_concurrent_memtable_write(false);
2933    /// opts.set_memtable_factory(factory);
2934    /// ```
2935    pub fn set_memtable_factory(&mut self, factory: MemtableFactory) {
2936        match factory {
2937            MemtableFactory::Vector => unsafe {
2938                ffi::rocksdb_options_set_memtable_vector_rep(self.inner);
2939            },
2940            MemtableFactory::HashSkipList {
2941                bucket_count,
2942                height,
2943                branching_factor,
2944            } => unsafe {
2945                ffi::rocksdb_options_set_hash_skip_list_rep(
2946                    self.inner,
2947                    bucket_count,
2948                    height,
2949                    branching_factor,
2950                );
2951            },
2952            MemtableFactory::HashLinkList { bucket_count } => unsafe {
2953                ffi::rocksdb_options_set_hash_link_list_rep(self.inner, bucket_count);
2954            },
2955        }
2956    }
2957
2958    pub fn set_block_based_table_factory(&mut self, factory: &BlockBasedOptions) {
2959        unsafe {
2960            ffi::rocksdb_options_set_block_based_table_factory(self.inner, factory.inner);
2961        }
2962        self.outlive.block_based = Some(factory.outlive.clone());
2963    }
2964
2965    /// Sets the table factory to a CuckooTableFactory (the default table
2966    /// factory is a block-based table factory that provides a default
2967    /// implementation of TableBuilder and TableReader with default
2968    /// BlockBasedTableOptions).
2969    /// See official [wiki](https://github.com/facebook/rocksdb/wiki/CuckooTable-Format) for more information on this table format.
2970    /// # Examples
2971    ///
2972    /// ```
2973    /// use rust_rocksdb::{Options, CuckooTableOptions};
2974    ///
2975    /// let mut opts = Options::default();
2976    /// let mut factory_opts = CuckooTableOptions::default();
2977    /// factory_opts.set_hash_ratio(0.8);
2978    /// factory_opts.set_max_search_depth(20);
2979    /// factory_opts.set_cuckoo_block_size(10);
2980    /// factory_opts.set_identity_as_first_hash(true);
2981    /// factory_opts.set_use_module_hash(false);
2982    ///
2983    /// opts.set_cuckoo_table_factory(&factory_opts);
2984    /// ```
2985    pub fn set_cuckoo_table_factory(&mut self, factory: &CuckooTableOptions) {
2986        unsafe {
2987            ffi::rocksdb_options_set_cuckoo_table_factory(self.inner, factory.inner);
2988        }
2989    }
2990
2991    // This is a factory that provides TableFactory objects.
2992    // Default: a block-based table factory that provides a default
2993    // implementation of TableBuilder and TableReader with default
2994    // BlockBasedTableOptions.
2995    /// Sets the factory as plain table.
2996    /// See official [wiki](https://github.com/facebook/rocksdb/wiki/PlainTable-Format) for more
2997    /// information.
2998    ///
2999    /// # Examples
3000    ///
3001    /// ```
3002    /// use rust_rocksdb::{KeyEncodingType, Options, PlainTableFactoryOptions};
3003    ///
3004    /// let mut opts = Options::default();
3005    /// let factory_opts = PlainTableFactoryOptions {
3006    ///   user_key_length: 0,
3007    ///   bloom_bits_per_key: 20,
3008    ///   hash_table_ratio: 0.75,
3009    ///   index_sparseness: 16,
3010    ///   huge_page_tlb_size: 0,
3011    ///   encoding_type: KeyEncodingType::Plain,
3012    ///   full_scan_mode: false,
3013    ///   store_index_in_file: false,
3014    /// };
3015    ///
3016    /// opts.set_plain_table_factory(&factory_opts);
3017    /// ```
3018    pub fn set_plain_table_factory(&mut self, options: &PlainTableFactoryOptions) {
3019        unsafe {
3020            ffi::rocksdb_options_set_plain_table_factory(
3021                self.inner,
3022                options.user_key_length,
3023                options.bloom_bits_per_key,
3024                options.hash_table_ratio,
3025                options.index_sparseness,
3026                options.huge_page_tlb_size,
3027                options.encoding_type as c_char,
3028                c_uchar::from(options.full_scan_mode),
3029                c_uchar::from(options.store_index_in_file),
3030            );
3031        }
3032    }
3033
3034    /// Sets the start level to use compression.
3035    pub fn set_min_level_to_compress(&mut self, lvl: c_int) {
3036        unsafe {
3037            ffi::rocksdb_options_set_min_level_to_compress(self.inner, lvl);
3038        }
3039    }
3040
3041    /// Measure IO stats in compactions and flushes, if `true`.
3042    ///
3043    /// Default: `false`
3044    ///
3045    /// # Examples
3046    ///
3047    /// ```
3048    /// use rust_rocksdb::Options;
3049    ///
3050    /// let mut opts = Options::default();
3051    /// opts.set_report_bg_io_stats(true);
3052    /// ```
3053    pub fn set_report_bg_io_stats(&mut self, enable: bool) {
3054        unsafe {
3055            ffi::rocksdb_options_set_report_bg_io_stats(self.inner, c_int::from(enable));
3056        }
3057    }
3058
3059    /// Once write-ahead logs exceed this size, we will start forcing the flush of
3060    /// column families whose memtables are backed by the oldest live WAL file
3061    /// (i.e. the ones that are causing all the space amplification).
3062    ///
3063    /// Default: `0`
3064    ///
3065    /// # Examples
3066    ///
3067    /// ```
3068    /// use rust_rocksdb::Options;
3069    ///
3070    /// let mut opts = Options::default();
3071    /// // Set max total wal size to 1G.
3072    /// opts.set_max_total_wal_size(1 << 30);
3073    /// ```
3074    pub fn set_max_total_wal_size(&mut self, size: u64) {
3075        unsafe {
3076            ffi::rocksdb_options_set_max_total_wal_size(self.inner, size);
3077        }
3078    }
3079
3080    /// Recovery mode to control the consistency while replaying WAL.
3081    ///
3082    /// Default: DBRecoveryMode::PointInTime
3083    ///
3084    /// # Examples
3085    ///
3086    /// ```
3087    /// use rust_rocksdb::{Options, DBRecoveryMode};
3088    ///
3089    /// let mut opts = Options::default();
3090    /// opts.set_wal_recovery_mode(DBRecoveryMode::AbsoluteConsistency);
3091    /// ```
3092    pub fn set_wal_recovery_mode(&mut self, mode: DBRecoveryMode) {
3093        unsafe {
3094            ffi::rocksdb_options_set_wal_recovery_mode(self.inner, mode as c_int);
3095        }
3096    }
3097
3098    /// Enables recording RocksDB statistics.
3099    ///
3100    /// The statistics in this Options object are shared between all DB instances.
3101    /// See [`get_statistics`](Self::get_statistics), [`get_ticker_count`](Self::get_ticker_count),
3102    /// and [`get_histogram_data`](Self::get_histogram_data).
3103    pub fn enable_statistics(&mut self) {
3104        unsafe {
3105            ffi::rocksdb_options_enable_statistics(self.inner);
3106        }
3107    }
3108
3109    /// Returns a string containing RocksDB statistics if enabled using
3110    /// [`enable_statistics`](Self::enable_statistics).
3111    pub fn get_statistics(&self) -> Option<String> {
3112        unsafe {
3113            let value = ffi::rocksdb_options_statistics_get_string(self.inner);
3114            if value.is_null() {
3115                return None;
3116            }
3117
3118            // Must have valid UTF-8 format.
3119            Some(from_cstr_and_free(value))
3120        }
3121    }
3122
3123    /// StatsLevel can be used to reduce statistics overhead by skipping certain
3124    /// types of stats in the stats collection process.
3125    ///
3126    /// Only takes effect if stats are enabled first using
3127    /// [`enable_statistics`](Self::enable_statistics).
3128    pub fn set_statistics_level(&self, level: StatsLevel) {
3129        unsafe { ffi::rocksdb_options_set_statistics_level(self.inner, level as c_int) }
3130    }
3131
3132    /// Returns a counter if statistics are enabled using
3133    /// [`enable_statistics`](Self::enable_statistics).
3134    pub fn get_ticker_count(&self, ticker: Ticker) -> u64 {
3135        unsafe { ffi::rocksdb_options_statistics_get_ticker_count(self.inner, ticker as u32) }
3136    }
3137
3138    /// Returns a histogram if statistics are enabled using
3139    /// [`enable_statistics`](Self::enable_statistics).
3140    pub fn get_histogram_data(&self, histogram: Histogram) -> HistogramData {
3141        unsafe {
3142            let data = HistogramData::default();
3143            ffi::rocksdb_options_statistics_get_histogram_data(
3144                self.inner,
3145                histogram as u32,
3146                data.inner,
3147            );
3148            data
3149        }
3150    }
3151
3152    /// If not zero, dump `rocksdb.stats` to LOG every `stats_dump_period_sec`.
3153    ///
3154    /// Default: `600` (10 mins)
3155    ///
3156    /// # Examples
3157    ///
3158    /// ```
3159    /// use rust_rocksdb::Options;
3160    ///
3161    /// let mut opts = Options::default();
3162    /// opts.set_stats_dump_period_sec(300);
3163    /// ```
3164    pub fn set_stats_dump_period_sec(&mut self, period: c_uint) {
3165        unsafe {
3166            ffi::rocksdb_options_set_stats_dump_period_sec(self.inner, period);
3167        }
3168    }
3169
3170    /// If not zero, dump rocksdb.stats to RocksDB to LOG every `stats_persist_period_sec`.
3171    ///
3172    /// Default: `600` (10 mins)
3173    ///
3174    /// # Examples
3175    ///
3176    /// ```
3177    /// use rust_rocksdb::Options;
3178    ///
3179    /// let mut opts = Options::default();
3180    /// opts.set_stats_persist_period_sec(5);
3181    /// ```
3182    pub fn set_stats_persist_period_sec(&mut self, period: c_uint) {
3183        unsafe {
3184            ffi::rocksdb_options_set_stats_persist_period_sec(self.inner, period);
3185        }
3186    }
3187
3188    /// When set to true, reading SST files will opt out of the filesystem's
3189    /// readahead. Setting this to false may improve sequential iteration
3190    /// performance.
3191    ///
3192    /// Default: `true`
3193    pub fn set_advise_random_on_open(&mut self, advise: bool) {
3194        unsafe {
3195            ffi::rocksdb_options_set_advise_random_on_open(self.inner, c_uchar::from(advise));
3196        }
3197    }
3198
3199    /// Enable/disable adaptive mutex, which spins in the user space before resorting to kernel.
3200    ///
3201    /// This could reduce context switch when the mutex is not
3202    /// heavily contended. However, if the mutex is hot, we could end up
3203    /// wasting spin time.
3204    ///
3205    /// Default: false
3206    pub fn set_use_adaptive_mutex(&mut self, enabled: bool) {
3207        unsafe {
3208            ffi::rocksdb_options_set_use_adaptive_mutex(self.inner, c_uchar::from(enabled));
3209        }
3210    }
3211
3212    /// Sets the number of levels for this database.
3213    pub fn set_num_levels(&mut self, n: c_int) {
3214        unsafe {
3215            ffi::rocksdb_options_set_num_levels(self.inner, n);
3216        }
3217    }
3218
3219    /// When a `prefix_extractor` is defined through `opts.set_prefix_extractor` this
3220    /// creates a prefix bloom filter for each memtable with the size of
3221    /// `write_buffer_size * memtable_prefix_bloom_ratio` (capped at 0.25).
3222    ///
3223    /// Default: `0`
3224    ///
3225    /// # Examples
3226    ///
3227    /// ```
3228    /// use rust_rocksdb::{Options, SliceTransform};
3229    ///
3230    /// let mut opts = Options::default();
3231    /// let transform = SliceTransform::create_fixed_prefix(10);
3232    /// opts.set_prefix_extractor(transform);
3233    /// opts.set_memtable_prefix_bloom_ratio(0.2);
3234    /// ```
3235    pub fn set_memtable_prefix_bloom_ratio(&mut self, ratio: f64) {
3236        unsafe {
3237            ffi::rocksdb_options_set_memtable_prefix_bloom_size_ratio(self.inner, ratio);
3238        }
3239    }
3240
3241    /// Sets the maximum number of bytes in all compacted files.
3242    /// We try to limit number of bytes in one compaction to be lower than this
3243    /// threshold. But it's not guaranteed.
3244    ///
3245    /// Value 0 will be sanitized.
3246    ///
3247    /// Default: target_file_size_base * 25
3248    pub fn set_max_compaction_bytes(&mut self, nbytes: u64) {
3249        unsafe {
3250            ffi::rocksdb_options_set_max_compaction_bytes(self.inner, nbytes);
3251        }
3252    }
3253
3254    /// Specifies the absolute path of the directory the
3255    /// write-ahead log (WAL) should be written to.
3256    ///
3257    /// Default: same directory as the database
3258    ///
3259    /// # Examples
3260    ///
3261    /// ```
3262    /// use rust_rocksdb::Options;
3263    ///
3264    /// let mut opts = Options::default();
3265    /// opts.set_wal_dir("/path/to/dir");
3266    /// ```
3267    pub fn set_wal_dir<P: AsRef<Path>>(&mut self, path: P) {
3268        let p = to_cpath(path).unwrap();
3269        unsafe {
3270            ffi::rocksdb_options_set_wal_dir(self.inner, p.as_ptr());
3271        }
3272    }
3273
3274    /// Sets the WAL ttl in seconds.
3275    ///
3276    /// The following two options affect how archived logs will be deleted.
3277    /// 1. If both set to 0, logs will be deleted asap and will not get into
3278    ///    the archive.
3279    /// 2. If wal_ttl_seconds is 0 and wal_size_limit_mb is not 0,
3280    ///    WAL files will be checked every 10 min and if total size is greater
3281    ///    then wal_size_limit_mb, they will be deleted starting with the
3282    ///    earliest until size_limit is met. All empty files will be deleted.
3283    /// 3. If wal_ttl_seconds is not 0 and wall_size_limit_mb is 0, then
3284    ///    WAL files will be checked every wal_ttl_seconds / 2 and those that
3285    ///    are older than wal_ttl_seconds will be deleted.
3286    /// 4. If both are not 0, WAL files will be checked every 10 min and both
3287    ///    checks will be performed with ttl being first.
3288    ///
3289    /// Default: 0
3290    pub fn set_wal_ttl_seconds(&mut self, secs: u64) {
3291        unsafe {
3292            ffi::rocksdb_options_set_WAL_ttl_seconds(self.inner, secs);
3293        }
3294    }
3295
3296    /// Sets the WAL size limit in MB.
3297    ///
3298    /// If total size of WAL files is greater then wal_size_limit_mb,
3299    /// they will be deleted starting with the earliest until size_limit is met.
3300    ///
3301    /// Default: 0
3302    pub fn set_wal_size_limit_mb(&mut self, size: u64) {
3303        unsafe {
3304            ffi::rocksdb_options_set_WAL_size_limit_MB(self.inner, size);
3305        }
3306    }
3307
3308    /// Sets the number of bytes to preallocate (via fallocate) the manifest files.
3309    ///
3310    /// Default is 4MB, which is reasonable to reduce random IO
3311    /// as well as prevent overallocation for mounts that preallocate
3312    /// large amounts of data (such as xfs's allocsize option).
3313    pub fn set_manifest_preallocation_size(&mut self, size: usize) {
3314        unsafe {
3315            ffi::rocksdb_options_set_manifest_preallocation_size(self.inner, size);
3316        }
3317    }
3318
3319    /// If true, then DB::Open() will not update the statistics used to optimize
3320    /// compaction decision by loading table properties from many files.
3321    /// Turning off this feature will improve DBOpen time especially in disk environment.
3322    ///
3323    /// Default: false
3324    pub fn set_skip_stats_update_on_db_open(&mut self, skip: bool) {
3325        unsafe {
3326            ffi::rocksdb_options_set_skip_stats_update_on_db_open(self.inner, c_uchar::from(skip));
3327        }
3328    }
3329
3330    /// Controls whether RocksDB opens and validates SST files in the background after open.
3331    ///
3332    /// Enabling this can reduce open latency for databases with many SST files
3333    /// or high latency storage. It is mostly useful with
3334    /// [`Options::set_max_open_files`] set to `-1`.
3335    ///
3336    /// This option is not compatible with FIFO compaction and requires
3337    /// [`Options::set_skip_stats_update_on_db_open`] to be `true`. SST open
3338    /// errors are no longer returned by `DB::open`; they can instead surface as
3339    /// background errors or from operations that access the affected file.
3340    ///
3341    /// Default: `false`
3342    pub fn set_open_files_async(&mut self, enabled: bool) -> Result<(), Error> {
3343        let supported = unsafe {
3344            ffi::rust_rocksdb_options_set_open_files_async(self.inner, c_uchar::from(enabled)) != 0
3345        };
3346        if !supported {
3347            return Err(Error::new(
3348                "open_files_async requires RocksDB 11.1 or newer".to_owned(),
3349            ));
3350        }
3351        Ok(())
3352    }
3353
3354    /// Returns whether SST files are opened and validated in the background after open.
3355    pub fn get_open_files_async(&self) -> bool {
3356        unsafe { ffi::rust_rocksdb_options_get_open_files_async(self.inner) != 0 }
3357    }
3358
3359    /// Returns whether the linked RocksDB supports `open_files_async`.
3360    pub fn supports_open_files_async() -> bool {
3361        unsafe { ffi::rust_rocksdb_options_open_files_async_supported() != 0 }
3362    }
3363
3364    /// Specify the maximal number of info log files to be kept.
3365    ///
3366    /// Default: 1000
3367    ///
3368    /// # Examples
3369    ///
3370    /// ```
3371    /// use rust_rocksdb::Options;
3372    ///
3373    /// let mut options = Options::default();
3374    /// options.set_keep_log_file_num(100);
3375    /// ```
3376    pub fn set_keep_log_file_num(&mut self, nfiles: usize) {
3377        unsafe {
3378            ffi::rocksdb_options_set_keep_log_file_num(self.inner, nfiles);
3379        }
3380    }
3381
3382    /// Allow the OS to mmap file for writing.
3383    ///
3384    /// Default: false
3385    ///
3386    /// # Examples
3387    ///
3388    /// ```
3389    /// use rust_rocksdb::Options;
3390    ///
3391    /// let mut options = Options::default();
3392    /// options.set_allow_mmap_writes(true);
3393    /// ```
3394    pub fn set_allow_mmap_writes(&mut self, is_enabled: bool) {
3395        unsafe {
3396            ffi::rocksdb_options_set_allow_mmap_writes(self.inner, c_uchar::from(is_enabled));
3397        }
3398    }
3399
3400    /// Allow the OS to mmap file for reading sst tables.
3401    ///
3402    /// Default: false
3403    ///
3404    /// # Examples
3405    ///
3406    /// ```
3407    /// use rust_rocksdb::Options;
3408    ///
3409    /// let mut options = Options::default();
3410    /// options.set_allow_mmap_reads(true);
3411    /// ```
3412    pub fn set_allow_mmap_reads(&mut self, is_enabled: bool) {
3413        unsafe {
3414            ffi::rocksdb_options_set_allow_mmap_reads(self.inner, c_uchar::from(is_enabled));
3415        }
3416    }
3417
3418    /// If enabled, WAL is not flushed automatically after each write. Instead it
3419    /// relies on manual invocation of `DB::flush_wal()` to write the WAL buffer
3420    /// to its file.
3421    ///
3422    /// Default: false
3423    ///
3424    /// # Examples
3425    ///
3426    /// ```
3427    /// use rust_rocksdb::Options;
3428    ///
3429    /// let mut options = Options::default();
3430    /// options.set_manual_wal_flush(true);
3431    /// ```
3432    pub fn set_manual_wal_flush(&mut self, is_enabled: bool) {
3433        unsafe {
3434            ffi::rocksdb_options_set_manual_wal_flush(self.inner, c_uchar::from(is_enabled));
3435        }
3436    }
3437
3438    /// Guarantee that all column families are flushed together atomically.
3439    /// This option applies to both manual flushes (`db.flush()`) and automatic
3440    /// background flushes caused when memtables are filled.
3441    ///
3442    /// Note that this is only useful when the WAL is disabled. When using the
3443    /// WAL, writes are always consistent across column families.
3444    ///
3445    /// Default: false
3446    ///
3447    /// # Examples
3448    ///
3449    /// ```
3450    /// use rust_rocksdb::Options;
3451    ///
3452    /// let mut options = Options::default();
3453    /// options.set_atomic_flush(true);
3454    /// ```
3455    pub fn set_atomic_flush(&mut self, atomic_flush: bool) {
3456        unsafe {
3457            ffi::rocksdb_options_set_atomic_flush(self.inner, c_uchar::from(atomic_flush));
3458        }
3459    }
3460
3461    /// Sets global cache for table-level rows.
3462    ///
3463    /// Default: null (disabled)
3464    /// Not supported in ROCKSDB_LITE mode!
3465    pub fn set_row_cache(&mut self, cache: &Cache) {
3466        unsafe {
3467            ffi::rocksdb_options_set_row_cache(self.inner, cache.0.inner.as_ptr());
3468        }
3469        self.outlive.row_cache = Some(cache.clone());
3470    }
3471
3472    /// Use to control write rate of flush and compaction. Flush has higher
3473    /// priority than compaction.
3474    /// If rate limiter is enabled, bytes_per_sync is set to 1MB by default.
3475    ///
3476    /// Default: disable
3477    ///
3478    /// # Examples
3479    ///
3480    /// ```
3481    /// use rust_rocksdb::Options;
3482    ///
3483    /// let mut options = Options::default();
3484    /// options.set_ratelimiter(1024 * 1024, 100 * 1000, 10);
3485    /// ```
3486    pub fn set_ratelimiter(
3487        &mut self,
3488        rate_bytes_per_sec: i64,
3489        refill_period_us: i64,
3490        fairness: i32,
3491    ) {
3492        unsafe {
3493            let ratelimiter =
3494                ffi::rocksdb_ratelimiter_create(rate_bytes_per_sec, refill_period_us, fairness);
3495            ffi::rocksdb_options_set_ratelimiter(self.inner, ratelimiter);
3496            ffi::rocksdb_ratelimiter_destroy(ratelimiter);
3497        }
3498    }
3499
3500    /// Use to control write rate of flush and compaction. Flush has higher
3501    /// priority than compaction.
3502    /// If rate limiter is enabled, bytes_per_sync is set to 1MB by default.
3503    ///
3504    /// Default: disable
3505    pub fn set_auto_tuned_ratelimiter(
3506        &mut self,
3507        rate_bytes_per_sec: i64,
3508        refill_period_us: i64,
3509        fairness: i32,
3510    ) {
3511        unsafe {
3512            let ratelimiter = ffi::rocksdb_ratelimiter_create_auto_tuned(
3513                rate_bytes_per_sec,
3514                refill_period_us,
3515                fairness,
3516            );
3517            ffi::rocksdb_options_set_ratelimiter(self.inner, ratelimiter);
3518            ffi::rocksdb_ratelimiter_destroy(ratelimiter);
3519        }
3520    }
3521
3522    /// Create a RateLimiter object, which can be shared among RocksDB instances to
3523    /// control write rate of flush and compaction.
3524    ///
3525    /// rate_bytes_per_sec: this is the only parameter you want to set most of the
3526    /// time. It controls the total write rate of compaction and flush in bytes per
3527    /// second. Currently, RocksDB does not enforce rate limit for anything other
3528    /// than flush and compaction, e.g. write to WAL.
3529    ///
3530    /// refill_period_us: this controls how often tokens are refilled. For example,
3531    /// when rate_bytes_per_sec is set to 10MB/s and refill_period_us is set to
3532    /// 100ms, then 1MB is refilled every 100ms internally. Larger value can lead to
3533    /// burstier writes while smaller value introduces more CPU overhead.
3534    /// The default should work for most cases.
3535    ///
3536    /// fairness: RateLimiter accepts high-pri requests and low-pri requests.
3537    /// A low-pri request is usually blocked in favor of hi-pri request. Currently,
3538    /// RocksDB assigns low-pri to request from compaction and high-pri to request
3539    /// from flush. Low-pri requests can get blocked if flush requests come in
3540    /// continuously. This fairness parameter grants low-pri requests permission by
3541    /// 1/fairness chance even though high-pri requests exist to avoid starvation.
3542    /// You should be good by leaving it at default 10.
3543    ///
3544    /// mode: Mode indicates which types of operations count against the limit.
3545    ///
3546    /// auto_tuned: Enables dynamic adjustment of rate limit within the range
3547    ///              `[rate_bytes_per_sec / 20, rate_bytes_per_sec]`, according to
3548    ///              the recent demand for background I/O.
3549    pub fn set_ratelimiter_with_mode(
3550        &mut self,
3551        rate_bytes_per_sec: i64,
3552        refill_period_us: i64,
3553        fairness: i32,
3554        mode: RateLimiterMode,
3555        auto_tuned: bool,
3556    ) {
3557        unsafe {
3558            let ratelimiter = ffi::rocksdb_ratelimiter_create_with_mode(
3559                rate_bytes_per_sec,
3560                refill_period_us,
3561                fairness,
3562                mode as c_int,
3563                auto_tuned,
3564            );
3565            ffi::rocksdb_options_set_ratelimiter(self.inner, ratelimiter);
3566            ffi::rocksdb_ratelimiter_destroy(ratelimiter);
3567        }
3568    }
3569
3570    /// Sets the maximal size of the info log file.
3571    ///
3572    /// If the log file is larger than `max_log_file_size`, a new info log file
3573    /// will be created. If `max_log_file_size` is equal to zero, all logs will
3574    /// be written to one log file.
3575    ///
3576    /// Default: 0
3577    ///
3578    /// # Examples
3579    ///
3580    /// ```
3581    /// use rust_rocksdb::Options;
3582    ///
3583    /// let mut options = Options::default();
3584    /// options.set_max_log_file_size(0);
3585    /// ```
3586    pub fn set_max_log_file_size(&mut self, size: usize) {
3587        unsafe {
3588            ffi::rocksdb_options_set_max_log_file_size(self.inner, size);
3589        }
3590    }
3591
3592    /// Sets the time for the info log file to roll (in seconds).
3593    ///
3594    /// If specified with non-zero value, log file will be rolled
3595    /// if it has been active longer than `log_file_time_to_roll`.
3596    /// Default: 0 (disabled)
3597    pub fn set_log_file_time_to_roll(&mut self, secs: usize) {
3598        unsafe {
3599            ffi::rocksdb_options_set_log_file_time_to_roll(self.inner, secs);
3600        }
3601    }
3602
3603    /// Controls the recycling of log files.
3604    ///
3605    /// If non-zero, previously written log files will be reused for new logs,
3606    /// overwriting the old data. The value indicates how many such files we will
3607    /// keep around at any point in time for later use. This is more efficient
3608    /// because the blocks are already allocated and fdatasync does not need to
3609    /// update the inode after each write.
3610    ///
3611    /// Default: 0
3612    ///
3613    /// # Examples
3614    ///
3615    /// ```
3616    /// use rust_rocksdb::Options;
3617    ///
3618    /// let mut options = Options::default();
3619    /// options.set_recycle_log_file_num(5);
3620    /// ```
3621    pub fn set_recycle_log_file_num(&mut self, num: usize) {
3622        unsafe {
3623            ffi::rocksdb_options_set_recycle_log_file_num(self.inner, num);
3624        }
3625    }
3626
3627    /// Prints logs to stderr for faster debugging
3628    /// See official [wiki](https://github.com/facebook/rocksdb/wiki/Logger) for more information.
3629    pub fn set_stderr_logger(&mut self, log_level: LogLevel, prefix: impl CStrLike) {
3630        let p = prefix.into_c_string().unwrap();
3631
3632        unsafe {
3633            let logger = ffi::rocksdb_logger_create_stderr_logger(log_level as c_int, p.as_ptr());
3634            ffi::rocksdb_options_set_info_log(self.inner, logger);
3635            ffi::rocksdb_logger_destroy(logger);
3636        }
3637    }
3638
3639    /// Invokes `callback` with RocksDB log messages with level >= `log_level`.
3640    ///
3641    /// The callback can be called concurrently by multiple RocksDB threads.
3642    ///
3643    /// # Examples
3644    /// ```
3645    /// use rust_rocksdb::{LogLevel, Options};
3646    ///
3647    /// let mut options = Options::default();
3648    /// options.set_callback_logger(LogLevel::Debug, move |level, msg| println!("{level:?} {msg}"));
3649    /// ```
3650    pub fn set_callback_logger(
3651        &mut self,
3652        log_level: LogLevel,
3653        callback: impl Fn(LogLevel, &str) + 'static + Send + Sync,
3654    ) {
3655        // store the closure in an Arc so it can be shared across multiple Option/DBs
3656        let holder = Arc::new(LogCallback {
3657            callback: Box::new(callback),
3658        });
3659        let holder_ptr = std::ptr::from_ref::<LogCallback>(holder.as_ref());
3660        let holder_cvoid = holder_ptr.cast::<c_void>().cast_mut();
3661
3662        unsafe {
3663            let logger = ffi::rocksdb_logger_create_callback_logger(
3664                log_level as c_int,
3665                Some(Self::logger_callback),
3666                holder_cvoid,
3667            );
3668            ffi::rocksdb_options_set_info_log(self.inner, logger);
3669            ffi::rocksdb_logger_destroy(logger);
3670        }
3671
3672        self.outlive.log_callback = Some(holder);
3673    }
3674
3675    extern "C" fn logger_callback(func: *mut c_void, level: u32, msg: *mut c_char, len: usize) {
3676        use std::process;
3677
3678        // Neither argument can be trusted:
3679        //
3680        //  * `LogLevel` is `#[repr(i32)]`, and `level` is whatever
3681        //    `InfoLogLevel` the C layer cast to an unsigned, so transmuting it
3682        //    could materialise an invalid discriminant.
3683        //  * `msg` is raw `vsnprintf` output. Log lines routinely embed
3684        //    filesystem paths and `Status::ToString()` text, and paths reach
3685        //    RocksDB via `OsStr::as_bytes()`, which is not UTF-8 validated, so
3686        //    `from_utf8_unchecked` was unsound.
3687        //
3688        // `from_utf8_lossy` returns `Cow::Borrowed` for valid UTF-8, so the
3689        // common path still does not allocate.
3690        let level = LogLevel::try_from_raw(level as i32).unwrap_or(LogLevel::Info);
3691        let slice = if len == 0 {
3692            &[][..]
3693        } else {
3694            unsafe { slice::from_raw_parts(msg.cast_const().cast::<u8>(), len) }
3695        };
3696        let msg = String::from_utf8_lossy(slice);
3697
3698        // Shared reference, not `&mut`: RocksDB logs from several background
3699        // threads at once, so a `&mut` here would alias. `LogCallbackFn` is a
3700        // `dyn Fn`, so a shared reference is all it needs.
3701        let holder = unsafe { &*func.cast::<LogCallback>() };
3702        let callback_in_catch_unwind = AssertUnwindSafe(&holder.callback);
3703        if catch_unwind(move || callback_in_catch_unwind(level, &msg)).is_err() {
3704            process::abort();
3705        }
3706    }
3707
3708    /// Sets the threshold at which all writes will be slowed down to at least delayed_write_rate if estimated
3709    /// bytes needed to be compaction exceed this threshold.
3710    ///
3711    /// Default: 64GB
3712    pub fn set_soft_pending_compaction_bytes_limit(&mut self, limit: usize) {
3713        unsafe {
3714            ffi::rocksdb_options_set_soft_pending_compaction_bytes_limit(self.inner, limit);
3715        }
3716    }
3717
3718    /// Sets the bytes threshold at which all writes are stopped if estimated bytes needed to be compaction exceed
3719    /// this threshold.
3720    ///
3721    /// Default: 256GB
3722    pub fn set_hard_pending_compaction_bytes_limit(&mut self, limit: usize) {
3723        unsafe {
3724            ffi::rocksdb_options_set_hard_pending_compaction_bytes_limit(self.inner, limit);
3725        }
3726    }
3727
3728    /// Sets the size of one block in arena memory allocation.
3729    ///
3730    /// If <= 0, a proper value is automatically calculated (usually 1/10 of
3731    /// writer_buffer_size).
3732    ///
3733    /// Default: 0
3734    pub fn set_arena_block_size(&mut self, size: usize) {
3735        unsafe {
3736            ffi::rocksdb_options_set_arena_block_size(self.inner, size);
3737        }
3738    }
3739
3740    /// If true, then print malloc stats together with rocksdb.stats when printing to LOG.
3741    ///
3742    /// Default: false
3743    pub fn set_dump_malloc_stats(&mut self, enabled: bool) {
3744        unsafe {
3745            ffi::rocksdb_options_set_dump_malloc_stats(self.inner, c_uchar::from(enabled));
3746        }
3747    }
3748
3749    /// Enable whole key bloom filter in memtable. Note this will only take effect
3750    /// if memtable_prefix_bloom_size_ratio is not 0. Enabling whole key filtering
3751    /// can potentially reduce CPU usage for point-look-ups.
3752    ///
3753    /// Default: false (disable)
3754    ///
3755    /// Dynamically changeable through SetOptions() API
3756    pub fn set_memtable_whole_key_filtering(&mut self, whole_key_filter: bool) {
3757        unsafe {
3758            ffi::rocksdb_options_set_memtable_whole_key_filtering(
3759                self.inner,
3760                c_uchar::from(whole_key_filter),
3761            );
3762        }
3763    }
3764
3765    /// Enable the use of key-value separation.
3766    ///
3767    /// More details can be found here: [Integrated BlobDB](http://rocksdb.org/blog/2021/05/26/integrated-blob-db.html).
3768    ///
3769    /// Default: false (disable)
3770    ///
3771    /// Dynamically changeable through SetOptions() API
3772    pub fn set_enable_blob_files(&mut self, val: bool) {
3773        unsafe {
3774            ffi::rocksdb_options_set_enable_blob_files(self.inner, u8::from(val));
3775        }
3776    }
3777
3778    /// Sets the minimum threshold value at or above which will be written
3779    /// to blob files during flush or compaction.
3780    ///
3781    /// Dynamically changeable through SetOptions() API
3782    pub fn set_min_blob_size(&mut self, val: u64) {
3783        unsafe {
3784            ffi::rocksdb_options_set_min_blob_size(self.inner, val);
3785        }
3786    }
3787
3788    /// Sets the size limit for blob files.
3789    ///
3790    /// Dynamically changeable through SetOptions() API
3791    pub fn set_blob_file_size(&mut self, val: u64) {
3792        unsafe {
3793            ffi::rocksdb_options_set_blob_file_size(self.inner, val);
3794        }
3795    }
3796
3797    /// Sets the blob compression type. All blob files use the same
3798    /// compression type.
3799    ///
3800    /// Dynamically changeable through SetOptions() API
3801    pub fn set_blob_compression_type(&mut self, val: DBCompressionType) {
3802        unsafe {
3803            ffi::rocksdb_options_set_blob_compression_type(self.inner, val as _);
3804        }
3805    }
3806
3807    /// If this is set to true RocksDB will actively relocate valid blobs from the oldest blob files
3808    /// as they are encountered during compaction.
3809    ///
3810    /// Dynamically changeable through SetOptions() API
3811    pub fn set_enable_blob_gc(&mut self, val: bool) {
3812        unsafe {
3813            ffi::rocksdb_options_set_enable_blob_gc(self.inner, u8::from(val));
3814        }
3815    }
3816
3817    /// Sets the threshold that the GC logic uses to determine which blob files should be considered “old.”
3818    ///
3819    /// For example, the default value of 0.25 signals to RocksDB that blobs residing in the
3820    /// oldest 25% of blob files should be relocated by GC. This parameter can be tuned to adjust
3821    /// the trade-off between write amplification and space amplification.
3822    ///
3823    /// Dynamically changeable through SetOptions() API
3824    pub fn set_blob_gc_age_cutoff(&mut self, val: c_double) {
3825        unsafe {
3826            ffi::rocksdb_options_set_blob_gc_age_cutoff(self.inner, val);
3827        }
3828    }
3829
3830    /// Sets the blob GC force threshold.
3831    ///
3832    /// Dynamically changeable through SetOptions() API
3833    pub fn set_blob_gc_force_threshold(&mut self, val: c_double) {
3834        unsafe {
3835            ffi::rocksdb_options_set_blob_gc_force_threshold(self.inner, val);
3836        }
3837    }
3838
3839    /// Sets the blob compaction read ahead size.
3840    ///
3841    /// Dynamically changeable through SetOptions() API
3842    pub fn set_blob_compaction_readahead_size(&mut self, val: u64) {
3843        unsafe {
3844            ffi::rocksdb_options_set_blob_compaction_readahead_size(self.inner, val);
3845        }
3846    }
3847
3848    /// Sets the blob cache.
3849    ///
3850    /// Using a dedicated object for blobs and using the same object for the block and blob caches
3851    /// are both supported. In the latter case, note that blobs are less valuable from a caching
3852    /// perspective than SST blocks, and some cache implementations have configuration options that
3853    /// can be used to prioritize items accordingly (see Cache::Priority and
3854    /// LRUCacheOptions::{high,low}_pri_pool_ratio).
3855    ///
3856    /// Default: disabled
3857    pub fn set_blob_cache(&mut self, cache: &Cache) {
3858        unsafe {
3859            ffi::rocksdb_options_set_blob_cache(self.inner, cache.0.inner.as_ptr());
3860        }
3861        self.outlive.blob_cache = Some(cache.clone());
3862    }
3863
3864    /// Set this option to true during creation of database if you want
3865    /// to be able to ingest behind (call IngestExternalFile() skipping keys
3866    /// that already exist, rather than overwriting matching keys).
3867    /// Setting this option to true has the following effects:
3868    ///
3869    /// 1. Disable some internal optimizations around SST file compression.
3870    /// 2. Reserve the last level for ingested files only.
3871    /// 3. Compaction will not include any file from the last level.
3872    ///
3873    /// Note that only Universal Compaction supports allow_ingest_behind.
3874    /// `num_levels` should be >= 3 if this option is turned on.
3875    ///
3876    /// DEFAULT: false
3877    /// Immutable.
3878    pub fn set_allow_ingest_behind(&mut self, val: bool) {
3879        unsafe {
3880            ffi::rocksdb_options_set_allow_ingest_behind(self.inner, c_uchar::from(val));
3881        }
3882    }
3883
3884    // A factory of a table property collector that marks an SST
3885    // file as need-compaction when it observe at least "D" deletion
3886    // entries in any "N" consecutive entries, or the ratio of tombstone
3887    // entries >= deletion_ratio.
3888    //
3889    // `window_size`: is the sliding window size "N"
3890    // `num_dels_trigger`: is the deletion trigger "D"
3891    // `deletion_ratio`: if <= 0 or > 1, disable triggering compaction based on
3892    // deletion ratio.
3893    pub fn add_compact_on_deletion_collector_factory(
3894        &mut self,
3895        window_size: size_t,
3896        num_dels_trigger: size_t,
3897        deletion_ratio: f64,
3898    ) {
3899        unsafe {
3900            ffi::rocksdb_options_add_compact_on_deletion_collector_factory_del_ratio(
3901                self.inner,
3902                window_size,
3903                num_dels_trigger,
3904                deletion_ratio,
3905            );
3906        }
3907    }
3908
3909    /// Like [`Self::add_compact_on_deletion_collector_factory`], but only triggers
3910    /// compaction if the SST file size is at least `min_file_size` bytes.
3911    pub fn add_compact_on_deletion_collector_factory_min_file_size(
3912        &mut self,
3913        window_size: size_t,
3914        num_dels_trigger: size_t,
3915        deletion_ratio: f64,
3916        min_file_size: u64,
3917    ) {
3918        unsafe {
3919            ffi::rocksdb_options_add_compact_on_deletion_collector_factory_min_file_size(
3920                self.inner,
3921                window_size,
3922                num_dels_trigger,
3923                deletion_ratio,
3924                min_file_size,
3925            );
3926        }
3927    }
3928
3929    /// <https://github.com/facebook/rocksdb/wiki/Write-Buffer-Manager>
3930    /// Write buffer manager helps users control the total memory used by memtables across multiple column families and/or DB instances.
3931    /// Users can enable this control by 2 ways:
3932    ///
3933    /// 1- Limit the total memtable usage across multiple column families and DBs under a threshold.
3934    /// 2- Cost the memtable memory usage to block cache so that memory of RocksDB can be capped by the single limit.
3935    /// The usage of a write buffer manager is similar to rate_limiter and sst_file_manager.
3936    /// Users can create one write buffer manager object and pass it to all the options of column families or DBs whose memtable size they want to be controlled by this object.
3937    pub fn set_write_buffer_manager(&mut self, write_buffer_manager: &WriteBufferManager) {
3938        unsafe {
3939            ffi::rocksdb_options_set_write_buffer_manager(
3940                self.inner,
3941                write_buffer_manager.0.inner.as_ptr(),
3942            );
3943        }
3944        self.outlive.write_buffer_manager = Some(write_buffer_manager.clone());
3945    }
3946
3947    /// Sets an `SstFileManager` for this `Options`.
3948    ///
3949    /// SstFileManager tracks and controls total SST file space usage, enabling
3950    /// applications to cap disk utilization and throttle deletions.
3951    pub fn set_sst_file_manager(&mut self, sst_file_manager: &SstFileManager) {
3952        unsafe {
3953            ffi::rocksdb_options_set_sst_file_manager(
3954                self.inner,
3955                sst_file_manager.0.inner.as_ptr(),
3956            );
3957        }
3958        self.outlive.sst_file_manager = Some(sst_file_manager.clone());
3959    }
3960
3961    /// If true, working thread may avoid doing unnecessary and long-latency
3962    /// operation (such as deleting obsolete files directly or deleting memtable)
3963    /// and will instead schedule a background job to do it.
3964    ///
3965    /// Use it if you're latency-sensitive.
3966    ///
3967    /// Default: false (disabled)
3968    pub fn set_avoid_unnecessary_blocking_io(&mut self, val: bool) {
3969        unsafe {
3970            ffi::rocksdb_options_set_avoid_unnecessary_blocking_io(self.inner, u8::from(val));
3971        }
3972    }
3973
3974    /// Activates the experimental Mempurge memtable garbage collection feature.
3975    ///
3976    /// See the upstream RocksDB option documentation:
3977    /// <https://github.com/facebook/rocksdb/blob/v10.7.5/include/rocksdb/advanced_options.h#L259-L274>
3978    ///
3979    /// At every flush, RocksDB estimates the useful payload ratio of the memtable
3980    /// and compares it with this threshold. If the ratio is below the threshold,
3981    /// RocksDB replaces the regular flush with a mempurge operation.
3982    ///
3983    /// Threshold values:
3984    ///
3985    /// * `0.0`: mempurge deactivated.
3986    /// * `1.0`: recommended threshold value.
3987    /// * `> 1.0`: aggressive mempurge.
3988    /// * `0.0 < threshold < 1.0`: mempurge only for very low useful payload ratios.
3989    ///
3990    /// Default: 0.0
3991    pub fn set_experimental_mempurge_threshold(&mut self, threshold: f64) {
3992        unsafe {
3993            ffi::rocksdb_options_set_experimental_mempurge_threshold(self.inner, threshold);
3994        }
3995    }
3996
3997    /// Sets the compaction priority.
3998    ///
3999    /// If level compaction_style =
4000    /// kCompactionStyleLevel, for each level, which files are prioritized to be
4001    /// picked to compact.
4002    ///
4003    /// Default: `DBCompactionPri::MinOverlappingRatio`
4004    ///
4005    /// # Examples
4006    ///
4007    /// ```
4008    /// use rust_rocksdb::{Options, DBCompactionPri};
4009    ///
4010    /// let mut opts = Options::default();
4011    /// opts.set_compaction_pri(DBCompactionPri::RoundRobin);
4012    /// ```
4013    pub fn set_compaction_pri(&mut self, pri: DBCompactionPri) {
4014        unsafe {
4015            ffi::rocksdb_options_set_compaction_pri(self.inner, pri as c_int);
4016        }
4017    }
4018
4019    /// If true, the log numbers and sizes of the synced WALs are tracked
4020    /// in MANIFEST. During DB recovery, if a synced WAL is missing
4021    /// from disk, or the WAL's size does not match the recorded size in
4022    /// MANIFEST, an error will be reported and the recovery will be aborted.
4023    ///
4024    /// This is one additional protection against WAL corruption besides the
4025    /// per-WAL-entry checksum.
4026    ///
4027    /// Note that this option does not work with secondary instance.
4028    /// Currently, only syncing closed WALs are tracked. Calling `DB::SyncWAL()`,
4029    /// etc. or writing with `WriteOptions::sync=true` to sync the live WAL is not
4030    /// tracked for performance/efficiency reasons.
4031    ///
4032    /// See: <https://github.com/facebook/rocksdb/wiki/Track-WAL-in-MANIFEST>
4033    ///
4034    /// Default: false (disabled)
4035    pub fn set_track_and_verify_wals_in_manifest(&mut self, val: bool) {
4036        unsafe {
4037            ffi::rocksdb_options_set_track_and_verify_wals_in_manifest(self.inner, u8::from(val));
4038        }
4039    }
4040
4041    /// Returns the value of the `track_and_verify_wals_in_manifest` option.
4042    pub fn get_track_and_verify_wals_in_manifest(&self) -> bool {
4043        let val_u8 =
4044            unsafe { ffi::rocksdb_options_get_track_and_verify_wals_in_manifest(self.inner) };
4045        val_u8 != 0
4046    }
4047
4048    /// The DB unique ID can be saved in the DB manifest (preferred, this option)
4049    /// or an IDENTITY file (historical, deprecated), or both. If this option is
4050    /// set to false (old behavior), then `write_identity_file` must be set to true.
4051    /// The manifest is preferred because
4052    ///
4053    /// 1. The IDENTITY file is not checksummed, so it is not as safe against
4054    ///    corruption.
4055    /// 2. The IDENTITY file may or may not be copied with the DB (e.g. not
4056    ///    copied by BackupEngine), so is not reliable for the provenance of a DB.
4057    ///
4058    /// This option might eventually be obsolete and removed as Identity files
4059    /// are phased out.
4060    ///
4061    /// Default: true (enabled)
4062    pub fn set_write_dbid_to_manifest(&mut self, val: bool) {
4063        unsafe {
4064            ffi::rocksdb_options_set_write_dbid_to_manifest(self.inner, u8::from(val));
4065        }
4066    }
4067
4068    /// Returns the value of the `write_dbid_to_manifest` option.
4069    pub fn get_write_dbid_to_manifest(&self) -> bool {
4070        let val_u8 = unsafe { ffi::rocksdb_options_get_write_dbid_to_manifest(self.inner) };
4071        val_u8 != 0
4072    }
4073
4074    /// Sets the logger to use.
4075    ///
4076    /// By default `rocksdb` writes its internal logs to a file in the database
4077    /// directory; this can be changed to a custom callback with the
4078    /// [`InfoLogger::new_callback_logger`] constructor.
4079    pub fn set_info_logger(&mut self, mut logger: InfoLogger) {
4080        // Move the callback so it can be shared across database instances
4081        self.outlive.logger_callback = logger.callback.take();
4082        unsafe {
4083            ffi::rocksdb_options_set_info_log(self.inner, logger.inner);
4084        }
4085    }
4086
4087    /// Returns a reference to the currently configured logger.
4088    pub fn get_info_logger(&self) -> InfoLogger {
4089        let raw = unsafe { ffi::rocksdb_options_get_info_log(self.inner) };
4090        InfoLogger {
4091            inner: raw,
4092            callback: self.outlive.logger_callback.clone(),
4093        }
4094    }
4095}
4096
4097impl Default for Options {
4098    fn default() -> Self {
4099        unsafe {
4100            let opts = ffi::rocksdb_options_create();
4101            assert!(!opts.is_null(), "Could not create RocksDB options");
4102
4103            Self {
4104                inner: opts,
4105                outlive: OptionsMustOutliveDB::default(),
4106            }
4107        }
4108    }
4109}
4110
4111impl FlushOptions {
4112    pub fn new() -> FlushOptions {
4113        FlushOptions::default()
4114    }
4115
4116    /// Waits until the flush is done.
4117    ///
4118    /// Default: true
4119    ///
4120    /// # Examples
4121    ///
4122    /// ```
4123    /// use rust_rocksdb::FlushOptions;
4124    ///
4125    /// let mut options = FlushOptions::default();
4126    /// options.set_wait(false);
4127    /// ```
4128    pub fn set_wait(&mut self, wait: bool) {
4129        unsafe {
4130            ffi::rocksdb_flushoptions_set_wait(self.inner, c_uchar::from(wait));
4131        }
4132    }
4133}
4134
4135impl Default for FlushOptions {
4136    fn default() -> Self {
4137        let flush_opts = unsafe { ffi::rocksdb_flushoptions_create() };
4138        assert!(
4139            !flush_opts.is_null(),
4140            "Could not create RocksDB flush options"
4141        );
4142
4143        Self { inner: flush_opts }
4144    }
4145}
4146
4147impl WriteOptions {
4148    pub fn new() -> WriteOptions {
4149        WriteOptions::default()
4150    }
4151
4152    /// Sets the sync mode. If true, the write will be flushed
4153    /// from the operating system buffer cache before the write is considered complete.
4154    /// If this flag is true, writes will be slower.
4155    ///
4156    /// Default: false
4157    pub fn set_sync(&mut self, sync: bool) {
4158        unsafe {
4159            ffi::rocksdb_writeoptions_set_sync(self.inner, c_uchar::from(sync));
4160        }
4161    }
4162
4163    /// Sets whether WAL should be active or not.
4164    /// If true, writes will not first go to the write ahead log,
4165    /// and the write may got lost after a crash.
4166    ///
4167    /// Default: false
4168    pub fn disable_wal(&mut self, disable: bool) {
4169        unsafe {
4170            ffi::rocksdb_writeoptions_disable_WAL(self.inner, c_int::from(disable));
4171        }
4172    }
4173
4174    /// If true and if user is trying to write to column families that don't exist (they were dropped),
4175    /// ignore the write (don't return an error). If there are multiple writes in a WriteBatch,
4176    /// other writes will succeed.
4177    ///
4178    /// Default: false
4179    pub fn set_ignore_missing_column_families(&mut self, ignore: bool) {
4180        unsafe {
4181            ffi::rocksdb_writeoptions_set_ignore_missing_column_families(
4182                self.inner,
4183                c_uchar::from(ignore),
4184            );
4185        }
4186    }
4187
4188    /// If true and we need to wait or sleep for the write request, fails
4189    /// immediately with Status::Incomplete().
4190    ///
4191    /// Default: false
4192    pub fn set_no_slowdown(&mut self, no_slowdown: bool) {
4193        unsafe {
4194            ffi::rocksdb_writeoptions_set_no_slowdown(self.inner, c_uchar::from(no_slowdown));
4195        }
4196    }
4197
4198    /// If true, this write request is of lower priority if compaction is
4199    /// behind. In this case, no_slowdown = true, the request will be cancelled
4200    /// immediately with Status::Incomplete() returned. Otherwise, it will be
4201    /// slowed down. The slowdown value is determined by RocksDB to guarantee
4202    /// it introduces minimum impacts to high priority writes.
4203    ///
4204    /// Default: false
4205    pub fn set_low_pri(&mut self, v: bool) {
4206        unsafe {
4207            ffi::rocksdb_writeoptions_set_low_pri(self.inner, c_uchar::from(v));
4208        }
4209    }
4210
4211    /// If true, writebatch will maintain the last insert positions of each
4212    /// memtable as hints in concurrent write. It can improve write performance
4213    /// in concurrent writes if keys in one writebatch are sequential. In
4214    /// non-concurrent writes (when concurrent_memtable_writes is false) this
4215    /// option will be ignored.
4216    ///
4217    /// Default: false
4218    pub fn set_memtable_insert_hint_per_batch(&mut self, v: bool) {
4219        unsafe {
4220            ffi::rocksdb_writeoptions_set_memtable_insert_hint_per_batch(
4221                self.inner,
4222                c_uchar::from(v),
4223            );
4224        }
4225    }
4226}
4227
4228impl Default for WriteOptions {
4229    fn default() -> Self {
4230        let write_opts = unsafe { ffi::rocksdb_writeoptions_create() };
4231        assert!(
4232            !write_opts.is_null(),
4233            "Could not create RocksDB write options"
4234        );
4235
4236        Self { inner: write_opts }
4237    }
4238}
4239
4240impl LruCacheOptions {
4241    /// Capacity of the cache, in the same units as the `charge` of each entry.
4242    /// This is typically measured in bytes, but can be a different unit if using
4243    /// kDontChargeCacheMetadata.
4244    pub fn set_capacity(&mut self, cap: usize) {
4245        unsafe {
4246            ffi::rocksdb_lru_cache_options_set_capacity(self.inner, cap);
4247        }
4248    }
4249
4250    /// Cache is sharded into 2^num_shard_bits shards, by hash of key.
4251    /// If < 0, a good default is chosen based on the capacity and the
4252    /// implementation. (Mutex-based implementations are much more reliant
4253    /// on many shards for parallel scalability.)
4254    pub fn set_num_shard_bits(&mut self, val: c_int) {
4255        unsafe {
4256            ffi::rocksdb_lru_cache_options_set_num_shard_bits(self.inner, val);
4257        }
4258    }
4259}
4260
4261impl Default for LruCacheOptions {
4262    fn default() -> Self {
4263        let inner = unsafe { ffi::rocksdb_lru_cache_options_create() };
4264        assert!(
4265            !inner.is_null(),
4266            "Could not create RocksDB LRU cache options"
4267        );
4268
4269        Self { inner }
4270    }
4271}
4272
4273#[derive(Debug, Copy, Clone, PartialEq, Eq)]
4274#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
4275#[repr(i32)]
4276pub enum ReadTier {
4277    /// Reads data in memtable, block cache, OS cache or storage.
4278    All = 0,
4279    /// Reads data in memtable or block cache.
4280    BlockCache,
4281    /// Reads persisted data. When WAL is disabled, this option will skip data in memtable.
4282    Persisted,
4283    /// Reads data in memtable. Used for memtable only iterators.
4284    Memtable,
4285}
4286
4287impl ReadOptions {
4288    // TODO add snapshot setting here
4289    // TODO add snapshot wrapper structs with proper destructors;
4290    // that struct needs an "iterator" impl too.
4291
4292    /// Specify whether the "data block"/"index block"/"filter block"
4293    /// read for this iteration should be cached in memory?
4294    /// Callers may wish to set this field to false for bulk scans.
4295    ///
4296    /// Default: true
4297    pub fn fill_cache(&mut self, v: bool) {
4298        unsafe {
4299            ffi::rocksdb_readoptions_set_fill_cache(self.inner, c_uchar::from(v));
4300        }
4301    }
4302
4303    /// Sets the snapshot which should be used for the read.
4304    /// The snapshot must belong to the DB that is being read and must
4305    /// not have been released.
4306    pub fn set_snapshot<D: DBAccess>(&mut self, snapshot: &SnapshotWithThreadMode<D>) {
4307        unsafe {
4308            ffi::rocksdb_readoptions_set_snapshot(self.inner, snapshot.inner);
4309        }
4310    }
4311
4312    /// Sets the lower bound for an iterator.
4313    pub fn set_iterate_lower_bound<K: Into<Vec<u8>>>(&mut self, key: K) {
4314        self.set_lower_bound_impl(Some(key.into()));
4315    }
4316
4317    /// Sets the upper bound for an iterator.
4318    /// The upper bound itself is not included on the iteration result.
4319    pub fn set_iterate_upper_bound<K: Into<Vec<u8>>>(&mut self, key: K) {
4320        self.set_upper_bound_impl(Some(key.into()));
4321    }
4322
4323    /// Sets lower and upper bounds based on the provided range.  This is
4324    /// similar to setting lower and upper bounds separately except that it also
4325    /// allows either bound to be reset.
4326    ///
4327    /// The argument can be a regular Rust range, e.g. `lower..upper`.  However,
4328    /// since RocksDB upper bound is always excluded (i.e. range can never be
4329    /// fully closed) inclusive ranges (`lower..=upper` and `..=upper`) are not
4330    /// supported.  For example:
4331    ///
4332    /// ```
4333    /// let mut options = rust_rocksdb::ReadOptions::default();
4334    /// options.set_iterate_range("xy".as_bytes().."xz".as_bytes());
4335    /// ```
4336    ///
4337    /// In addition, [`crate::PrefixRange`] can be used to specify a range of
4338    /// keys with a given prefix.  In particular, the above example is
4339    /// equivalent to:
4340    ///
4341    /// ```
4342    /// let mut options = rust_rocksdb::ReadOptions::default();
4343    /// options.set_iterate_range(rust_rocksdb::PrefixRange("xy".as_bytes()));
4344    /// ```
4345    ///
4346    /// Note that setting range using this method is separate to using prefix
4347    /// iterators.  Prefix iterators use prefix extractor configured for
4348    /// a column family.  Setting bounds via [`crate::PrefixRange`] is more akin
4349    /// to using manual prefix.
4350    ///
4351    /// Using this method clears any previously set bounds.  In other words, the
4352    /// bounds can be reset by setting the range to `..` as in:
4353    ///
4354    /// ```
4355    /// let mut options = rust_rocksdb::ReadOptions::default();
4356    /// options.set_iterate_range(..);
4357    /// ```
4358    pub fn set_iterate_range(&mut self, range: impl crate::IterateBounds) {
4359        let (lower, upper) = range.into_bounds();
4360        self.set_lower_bound_impl(lower);
4361        self.set_upper_bound_impl(upper);
4362    }
4363
4364    /// Equivalent to `set_iterate_range(PrefixRange(prefix))`, but writes into
4365    /// the already-allocated bound buffers instead of building two fresh
4366    /// `Vec<u8>`s and dropping the old ones.
4367    ///
4368    /// `set_iterate_range` has to allocate because `IterateBounds::into_bounds`
4369    /// hands back owned `Vec`s. That is fine for one-off configuration, but the
4370    /// hot prefix-probe path reuses a cached `ReadOptions` specifically to avoid
4371    /// per-call allocation, and then threw that away by reallocating both bounds
4372    /// on every call. Reusing the buffers makes the steady state allocation-free.
4373    pub(crate) fn set_prefix_range_in_place(&mut self, prefix: &[u8]) {
4374        // An empty prefix covers the full keyspace, i.e. no bounds at all.
4375        if prefix.is_empty() {
4376            self.set_lower_bound_impl(None);
4377            self.set_upper_bound_impl(None);
4378            return;
4379        }
4380
4381        // Lower bound is the prefix itself. The buffer can be reallocated by
4382        // `extend_from_slice`, so the pointer has to be handed to RocksDB again
4383        // even when the bound was already set.
4384        let (ptr, len) = {
4385            let lower = self.iterate_lower_bound.get_or_insert_with(Vec::new);
4386            lower.clear();
4387            lower.extend_from_slice(prefix);
4388            (lower.as_ptr() as *const c_char, lower.len())
4389        };
4390        unsafe {
4391            ffi::rocksdb_readoptions_set_iterate_lower_bound(self.inner, ptr, len);
4392        }
4393
4394        // Upper bound is the successor of the prefix: strip trailing 0xff bytes,
4395        // then increment the last remaining one. A prefix that is entirely 0xff
4396        // has no successor, so it is an unbounded scan. This mirrors
4397        // `iter_range::next_prefix`.
4398        let ffs = prefix
4399            .iter()
4400            .rev()
4401            .take_while(|&&byte| byte == u8::MAX)
4402            .count();
4403        let head = &prefix[..prefix.len() - ffs];
4404        if head.is_empty() {
4405            self.set_upper_bound_impl(None);
4406            return;
4407        }
4408        let (ptr, len) = {
4409            let upper = self.iterate_upper_bound.get_or_insert_with(Vec::new);
4410            upper.clear();
4411            upper.extend_from_slice(head);
4412            // `head` is non-empty and its last byte is not 0xff, so this cannot
4413            // overflow.
4414            *upper.last_mut().unwrap() += 1;
4415            (upper.as_ptr() as *const c_char, upper.len())
4416        };
4417        unsafe {
4418            ffi::rocksdb_readoptions_set_iterate_upper_bound(self.inner, ptr, len);
4419        }
4420    }
4421
4422    fn set_lower_bound_impl(&mut self, bound: Option<Vec<u8>>) {
4423        let (ptr, len) = if let Some(ref bound) = bound {
4424            (bound.as_ptr() as *const c_char, bound.len())
4425        } else if self.iterate_lower_bound.is_some() {
4426            (std::ptr::null(), 0)
4427        } else {
4428            return;
4429        };
4430        self.iterate_lower_bound = bound;
4431        unsafe {
4432            ffi::rocksdb_readoptions_set_iterate_lower_bound(self.inner, ptr, len);
4433        }
4434    }
4435
4436    fn set_upper_bound_impl(&mut self, bound: Option<Vec<u8>>) {
4437        let (ptr, len) = if let Some(ref bound) = bound {
4438            (bound.as_ptr() as *const c_char, bound.len())
4439        } else if self.iterate_upper_bound.is_some() {
4440            (std::ptr::null(), 0)
4441        } else {
4442            return;
4443        };
4444        self.iterate_upper_bound = bound;
4445        unsafe {
4446            ffi::rocksdb_readoptions_set_iterate_upper_bound(self.inner, ptr, len);
4447        }
4448    }
4449
4450    /// Specify if this read request should process data that ALREADY
4451    /// resides on a particular cache. If the required data is not
4452    /// found at the specified cache, then Status::Incomplete is returned.
4453    ///
4454    /// Default: ::All
4455    pub fn set_read_tier(&mut self, tier: ReadTier) {
4456        unsafe {
4457            ffi::rocksdb_readoptions_set_read_tier(self.inner, tier as c_int);
4458        }
4459    }
4460
4461    /// Enforce that the iterator only iterates over the same
4462    /// prefix as the seek.
4463    /// This option is effective only for prefix seeks, i.e. prefix_extractor is
4464    /// non-null for the column family and total_order_seek is false.  Unlike
4465    /// iterate_upper_bound, prefix_same_as_start only works within a prefix
4466    /// but in both directions.
4467    ///
4468    /// Default: false
4469    pub fn set_prefix_same_as_start(&mut self, v: bool) {
4470        unsafe {
4471            ffi::rocksdb_readoptions_set_prefix_same_as_start(self.inner, c_uchar::from(v));
4472        }
4473    }
4474
4475    /// Enable a total order seek regardless of index format (e.g. hash index)
4476    /// used in the table. Some table format (e.g. plain table) may not support
4477    /// this option.
4478    ///
4479    /// If true when calling Get(), we also skip prefix bloom when reading from
4480    /// block based table. It provides a way to read existing data after
4481    /// changing implementation of prefix extractor.
4482    pub fn set_total_order_seek(&mut self, v: bool) {
4483        unsafe {
4484            ffi::rocksdb_readoptions_set_total_order_seek(self.inner, c_uchar::from(v));
4485        }
4486    }
4487
4488    /// Sets a threshold for the number of keys that can be skipped
4489    /// before failing an iterator seek as incomplete. The default value of 0 should be used to
4490    /// never fail a request as incomplete, even on skipping too many keys.
4491    ///
4492    /// Default: 0
4493    pub fn set_max_skippable_internal_keys(&mut self, num: u64) {
4494        unsafe {
4495            ffi::rocksdb_readoptions_set_max_skippable_internal_keys(self.inner, num);
4496        }
4497    }
4498
4499    /// If true, when PurgeObsoleteFile is called in CleanupIteratorState, we schedule a background job
4500    /// in the flush job queue and delete obsolete files in background.
4501    ///
4502    /// Default: false
4503    pub fn set_background_purge_on_iterator_cleanup(&mut self, v: bool) {
4504        unsafe {
4505            ffi::rocksdb_readoptions_set_background_purge_on_iterator_cleanup(
4506                self.inner,
4507                c_uchar::from(v),
4508            );
4509        }
4510    }
4511
4512    /// If true, keys deleted using the DeleteRange() API will be visible to
4513    /// readers until they are naturally deleted during compaction.
4514    ///
4515    /// Default: false
4516    #[deprecated(
4517        note = "deprecated in RocksDB 10.2.1: no performance impact if DeleteRange is not used"
4518    )]
4519    pub fn set_ignore_range_deletions(&mut self, v: bool) {
4520        unsafe {
4521            ffi::rocksdb_readoptions_set_ignore_range_deletions(self.inner, c_uchar::from(v));
4522        }
4523    }
4524
4525    /// If true, all data read from underlying storage will be
4526    /// verified against corresponding checksums.
4527    ///
4528    /// Default: true
4529    pub fn set_verify_checksums(&mut self, v: bool) {
4530        unsafe {
4531            ffi::rocksdb_readoptions_set_verify_checksums(self.inner, c_uchar::from(v));
4532        }
4533    }
4534
4535    /// If non-zero, an iterator will create a new table reader which
4536    /// performs reads of the given size. Using a large size (> 2MB) can
4537    /// improve the performance of forward iteration on spinning disks.
4538    /// Default: 0
4539    ///
4540    /// ```
4541    /// use rust_rocksdb::{ReadOptions};
4542    ///
4543    /// let mut opts = ReadOptions::default();
4544    /// opts.set_readahead_size(4_194_304); // 4mb
4545    /// ```
4546    pub fn set_readahead_size(&mut self, v: usize) {
4547        unsafe {
4548            ffi::rocksdb_readoptions_set_readahead_size(self.inner, v as size_t);
4549        }
4550    }
4551
4552    /// If auto_readahead_size is set to true, it will auto tune the readahead_size
4553    /// during scans internally.
4554    /// For this feature to be enabled, iterate_upper_bound must also be specified.
4555    ///
4556    /// NOTE: - Recommended for forward Scans only.
4557    ///       - If there is a backward scans, this option will be
4558    ///         disabled internally and won't be enabled again if the forward scan
4559    ///         is issued again.
4560    ///
4561    /// Default: true
4562    pub fn set_auto_readahead_size(&mut self, v: bool) {
4563        unsafe {
4564            ffi::rocksdb_readoptions_set_auto_readahead_size(self.inner, c_uchar::from(v));
4565        }
4566    }
4567
4568    /// If true, create a tailing iterator. Note that tailing iterators
4569    /// only support moving in the forward direction. Iterating in reverse
4570    /// or seek_to_last are not supported.
4571    pub fn set_tailing(&mut self, v: bool) {
4572        unsafe {
4573            ffi::rocksdb_readoptions_set_tailing(self.inner, c_uchar::from(v));
4574        }
4575    }
4576
4577    /// Specifies the value of "pin_data". If true, it keeps the blocks
4578    /// loaded by the iterator pinned in memory as long as the iterator is not deleted,
4579    /// If used when reading from tables created with
4580    /// BlockBasedTableOptions::use_delta_encoding = false,
4581    /// Iterator's property "rocksdb.iterator.is-key-pinned" is guaranteed to
4582    /// return 1.
4583    ///
4584    /// Default: false
4585    pub fn set_pin_data(&mut self, v: bool) {
4586        unsafe {
4587            ffi::rocksdb_readoptions_set_pin_data(self.inner, c_uchar::from(v));
4588        }
4589    }
4590
4591    /// Asynchronously prefetch some data.
4592    ///
4593    /// Used for sequential reads and internal automatic prefetching.
4594    ///
4595    /// Default: `false`
4596    pub fn set_async_io(&mut self, v: bool) {
4597        unsafe {
4598            ffi::rocksdb_readoptions_set_async_io(self.inner, c_uchar::from(v));
4599        }
4600    }
4601
4602    /// Selects the multi-level vs single-level parallel `MultiGet` path when
4603    /// the library is built with `USE_COROUTINES` (the `coroutines` cargo
4604    /// feature) and `set_async_io(true)` has been called.
4605    ///
4606    /// When `true` (the C++ default), `MultiGet` parallelises reads across
4607    /// LSM levels, giving the lowest latency at the cost of higher CPU and
4608    /// coroutine scheduling overhead. When `false`, parallelism is limited
4609    /// to within a single level, trading some latency for CPU savings.
4610    ///
4611    /// Has no effect outside of `USE_COROUTINES` builds with `async_io=true`.
4612    /// With either condition unmet, both code paths in `db/version_set.cc`
4613    /// fall through to the synchronous per-file lookup regardless of this
4614    /// flag's value.
4615    ///
4616    /// See the RocksDB ["Asynchronous IO in RocksDB" blog
4617    /// post](https://rocksdb.org/blog/2022/10/07/asynchronous-io-in-rocksdb.html)
4618    /// for the qualitative tradeoff: `optimize_multiget_for_io=true`
4619    /// (multi-level) is the lowest-latency configuration but costs the most
4620    /// CPU; `optimize_multiget_for_io=false` (single-level, with `async_io`
4621    /// still on) retains most of the latency win at meaningfully lower CPU.
4622    ///
4623    /// Default: `true`
4624    pub fn set_optimize_multiget_for_io(&mut self, v: bool) {
4625        unsafe {
4626            ffi::rocksdb_readoptions_set_optimize_multiget_for_io(self.inner, c_uchar::from(v));
4627        }
4628    }
4629
4630    /// Returns the current value of [`Self::set_optimize_multiget_for_io`].
4631    ///
4632    /// Provided primarily for tests that want to confirm the setter is wired
4633    /// through to the underlying C++ `ReadOptions`. Reads through to the C
4634    /// API getter without exposing the underlying `c_uchar` representation.
4635    pub fn get_optimize_multiget_for_io(&self) -> bool {
4636        unsafe { ffi::rocksdb_readoptions_get_optimize_multiget_for_io(self.inner) != 0 }
4637    }
4638
4639    /// Deadline for completing an API call (Get/MultiGet/Seek/Next for now)
4640    /// in microseconds.
4641    /// It should be set to microseconds since epoch, i.e, gettimeofday or
4642    /// equivalent plus allowed duration in microseconds.
4643    /// This is best effort. The call may exceed the deadline if there is IO
4644    /// involved and the file system doesn't support deadlines, or due to
4645    /// checking for deadline periodically rather than for every key if
4646    /// processing a batch
4647    pub fn set_deadline(&mut self, microseconds: u64) {
4648        unsafe {
4649            ffi::rocksdb_readoptions_set_deadline(self.inner, microseconds);
4650        }
4651    }
4652
4653    /// A timeout in microseconds to be passed to the underlying FileSystem for
4654    /// reads. As opposed to deadline, this determines the timeout for each
4655    /// individual file read request. If a MultiGet/Get/Seek/Next etc call
4656    /// results in multiple reads, each read can last up to io_timeout us.
4657    pub fn set_io_timeout(&mut self, microseconds: u64) {
4658        unsafe {
4659            ffi::rocksdb_readoptions_set_io_timeout(self.inner, microseconds);
4660        }
4661    }
4662
4663    /// Timestamp of operation. Read should return the latest data visible to the
4664    /// specified timestamp. All timestamps of the same database must be of the
4665    /// same length and format. The user is responsible for providing a customized
4666    /// compare function via Comparator to order <key, timestamp> tuples.
4667    /// For iterator, iter_start_ts is the lower bound (older) and timestamp
4668    /// serves as the upper bound. Versions of the same record that fall in
4669    /// the timestamp range will be returned. If iter_start_ts is nullptr,
4670    /// only the most recent version visible to timestamp is returned.
4671    /// The user-specified timestamp feature is still under active development,
4672    /// and the API is subject to change.
4673    pub fn set_timestamp<S: Into<Vec<u8>>>(&mut self, ts: S) {
4674        self.set_timestamp_impl(Some(ts.into()));
4675    }
4676
4677    fn set_timestamp_impl(&mut self, ts: Option<Vec<u8>>) {
4678        let (ptr, len) = if let Some(ref ts) = ts {
4679            (ts.as_ptr() as *const c_char, ts.len())
4680        } else if self.timestamp.is_some() {
4681            // The stored timestamp is a `Some` but we're updating it to a `None`.
4682            // This means to cancel a previously set timestamp.
4683            // To do this, use a null pointer and zero length.
4684            (std::ptr::null(), 0)
4685        } else {
4686            return;
4687        };
4688        self.timestamp = ts;
4689        unsafe {
4690            ffi::rocksdb_readoptions_set_timestamp(self.inner, ptr, len);
4691        }
4692    }
4693
4694    /// See `set_timestamp`
4695    pub fn set_iter_start_ts<S: Into<Vec<u8>>>(&mut self, ts: S) {
4696        self.set_iter_start_ts_impl(Some(ts.into()));
4697    }
4698
4699    fn set_iter_start_ts_impl(&mut self, ts: Option<Vec<u8>>) {
4700        let (ptr, len) = if let Some(ref ts) = ts {
4701            (ts.as_ptr() as *const c_char, ts.len())
4702        } else if self.timestamp.is_some() {
4703            (std::ptr::null(), 0)
4704        } else {
4705            return;
4706        };
4707        self.iter_start_ts = ts;
4708        unsafe {
4709            ffi::rocksdb_readoptions_set_iter_start_ts(self.inner, ptr, len);
4710        }
4711    }
4712}
4713
4714impl Default for ReadOptions {
4715    fn default() -> Self {
4716        unsafe {
4717            Self {
4718                inner: ffi::rocksdb_readoptions_create(),
4719                timestamp: None,
4720                iter_start_ts: None,
4721                iterate_upper_bound: None,
4722                iterate_lower_bound: None,
4723            }
4724        }
4725    }
4726}
4727
4728impl IngestExternalFileOptions {
4729    /// Can be set to true to move the files instead of copying them.
4730    pub fn set_move_files(&mut self, v: bool) {
4731        unsafe {
4732            ffi::rocksdb_ingestexternalfileoptions_set_move_files(self.inner, c_uchar::from(v));
4733        }
4734    }
4735
4736    /// If set to false, an ingested file keys could appear in existing snapshots
4737    /// that where created before the file was ingested.
4738    pub fn set_snapshot_consistency(&mut self, v: bool) {
4739        unsafe {
4740            ffi::rocksdb_ingestexternalfileoptions_set_snapshot_consistency(
4741                self.inner,
4742                c_uchar::from(v),
4743            );
4744        }
4745    }
4746
4747    /// If set to false, IngestExternalFile() will fail if the file key range
4748    /// overlaps with existing keys or tombstones in the DB.
4749    pub fn set_allow_global_seqno(&mut self, v: bool) {
4750        unsafe {
4751            ffi::rocksdb_ingestexternalfileoptions_set_allow_global_seqno(
4752                self.inner,
4753                c_uchar::from(v),
4754            );
4755        }
4756    }
4757
4758    /// If set to false and the file key range overlaps with the memtable key range
4759    /// (memtable flush required), IngestExternalFile will fail.
4760    pub fn set_allow_blocking_flush(&mut self, v: bool) {
4761        unsafe {
4762            ffi::rocksdb_ingestexternalfileoptions_set_allow_blocking_flush(
4763                self.inner,
4764                c_uchar::from(v),
4765            );
4766        }
4767    }
4768
4769    /// Set to true if you would like duplicate keys in the file being ingested
4770    /// to be skipped rather than overwriting existing data under that key.
4771    /// Usecase: back-fill of some historical data in the database without
4772    /// over-writing existing newer version of data.
4773    /// This option could only be used if the DB has been running
4774    /// with allow_ingest_behind=true since the dawn of time.
4775    /// All files will be ingested at the bottommost level with seqno=0.
4776    pub fn set_ingest_behind(&mut self, v: bool) {
4777        unsafe {
4778            ffi::rocksdb_ingestexternalfileoptions_set_ingest_behind(self.inner, c_uchar::from(v));
4779        }
4780    }
4781}
4782
4783impl Default for IngestExternalFileOptions {
4784    fn default() -> Self {
4785        unsafe {
4786            Self {
4787                inner: ffi::rocksdb_ingestexternalfileoptions_create(),
4788            }
4789        }
4790    }
4791}
4792
4793/// Used by BlockBasedOptions::set_index_type.
4794pub enum BlockBasedIndexType {
4795    /// A space efficient index block that is optimized for
4796    /// binary-search-based index.
4797    BinarySearch,
4798
4799    /// The hash index, if enabled, will perform a hash lookup if
4800    /// a prefix extractor has been provided through Options::set_prefix_extractor.
4801    HashSearch,
4802
4803    /// A two-level index implementation. Both levels are binary search indexes.
4804    TwoLevelIndexSearch,
4805}
4806
4807/// Used by BlockBasedOptions::set_data_block_index_type.
4808#[repr(C)]
4809pub enum DataBlockIndexType {
4810    /// Use binary search when performing point lookup for keys in data blocks.
4811    /// This is the default.
4812    BinarySearch = 0,
4813
4814    /// Appends a compact hash table to the end of the data block for efficient indexing. Backwards
4815    /// compatible with databases created without this feature. Once turned on, existing data will
4816    /// be gradually converted to the hash index format.
4817    BinaryAndHash = 1,
4818}
4819
4820/// Defines the underlying memtable implementation.
4821/// See official [wiki](https://github.com/facebook/rocksdb/wiki/MemTable) for more information.
4822pub enum MemtableFactory {
4823    Vector,
4824    HashSkipList {
4825        bucket_count: usize,
4826        height: i32,
4827        branching_factor: i32,
4828    },
4829    HashLinkList {
4830        bucket_count: usize,
4831    },
4832}
4833
4834/// Used by BlockBasedOptions::set_checksum_type.
4835pub enum ChecksumType {
4836    NoChecksum = 0,
4837    CRC32c = 1,
4838    XXHash = 2,
4839    XXHash64 = 3,
4840    XXH3 = 4, // Supported since RocksDB 6.27
4841}
4842
4843/// Used in [`PlainTableFactoryOptions`].
4844#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)]
4845pub enum KeyEncodingType {
4846    /// Always write full keys.
4847    #[default]
4848    Plain = 0,
4849    /// Find opportunities to write the same prefix for multiple rows.
4850    Prefix = 1,
4851}
4852
4853/// Used with DBOptions::set_plain_table_factory.
4854/// See official [wiki](https://github.com/facebook/rocksdb/wiki/PlainTable-Format) for more
4855/// information.
4856///
4857/// Defaults:
4858///  user_key_length: 0 (variable length)
4859///  bloom_bits_per_key: 10
4860///  hash_table_ratio: 0.75
4861///  index_sparseness: 16
4862///  huge_page_tlb_size: 0
4863///  encoding_type: KeyEncodingType::Plain
4864///  full_scan_mode: false
4865///  store_index_in_file: false
4866pub struct PlainTableFactoryOptions {
4867    pub user_key_length: u32,
4868    pub bloom_bits_per_key: i32,
4869    pub hash_table_ratio: f64,
4870    pub index_sparseness: usize,
4871    pub huge_page_tlb_size: usize,
4872    pub encoding_type: KeyEncodingType,
4873    pub full_scan_mode: bool,
4874    pub store_index_in_file: bool,
4875}
4876
4877#[derive(Debug, Copy, Clone, PartialEq, Eq)]
4878#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
4879pub enum DBCompressionType {
4880    None = ffi::rocksdb_no_compression as isize,
4881    Snappy = ffi::rocksdb_snappy_compression as isize,
4882    Zlib = ffi::rocksdb_zlib_compression as isize,
4883    Bz2 = ffi::rocksdb_bz2_compression as isize,
4884    Lz4 = ffi::rocksdb_lz4_compression as isize,
4885    Lz4hc = ffi::rocksdb_lz4hc_compression as isize,
4886    Zstd = ffi::rocksdb_zstd_compression as isize,
4887}
4888
4889#[derive(Debug, Copy, Clone, PartialEq, Eq)]
4890#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
4891pub enum DBCompactionStyle {
4892    Level = ffi::rocksdb_level_compaction as isize,
4893    Universal = ffi::rocksdb_universal_compaction as isize,
4894    Fifo = ffi::rocksdb_fifo_compaction as isize,
4895}
4896
4897#[derive(Debug, Copy, Clone, PartialEq, Eq)]
4898#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
4899pub enum DBRecoveryMode {
4900    TolerateCorruptedTailRecords = ffi::rocksdb_tolerate_corrupted_tail_records_recovery as isize,
4901    AbsoluteConsistency = ffi::rocksdb_absolute_consistency_recovery as isize,
4902    PointInTime = ffi::rocksdb_point_in_time_recovery as isize,
4903    SkipAnyCorruptedRecord = ffi::rocksdb_skip_any_corrupted_records_recovery as isize,
4904}
4905
4906#[derive(Debug, Copy, Clone, PartialEq, Eq)]
4907#[repr(i32)]
4908pub enum RateLimiterMode {
4909    KReadsOnly = 0,
4910    KWritesOnly = 1,
4911    KAllIo = 2,
4912}
4913
4914#[derive(Debug, Copy, Clone, PartialEq, Eq)]
4915#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
4916pub enum DBCompactionPri {
4917    ByCompensatedSize = ffi::rocksdb_k_by_compensated_size_compaction_pri as isize,
4918    OldestLargestSeqFirst = ffi::rocksdb_k_oldest_largest_seq_first_compaction_pri as isize,
4919    OldestSmallestSeqFirst = ffi::rocksdb_k_oldest_smallest_seq_first_compaction_pri as isize,
4920    MinOverlappingRatio = ffi::rocksdb_k_min_overlapping_ratio_compaction_pri as isize,
4921    RoundRobin = ffi::rocksdb_k_round_robin_compaction_pri as isize,
4922}
4923
4924#[derive(Debug, Copy, Clone, PartialEq, Eq)]
4925#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
4926pub enum BlockBasedPinningTier {
4927    Fallback = ffi::rocksdb_block_based_k_fallback_pinning_tier as isize,
4928    None = ffi::rocksdb_block_based_k_none_pinning_tier as isize,
4929    FlushAndSimilar = ffi::rocksdb_block_based_k_flush_and_similar_pinning_tier as isize,
4930    All = ffi::rocksdb_block_based_k_all_pinning_tier as isize,
4931}
4932
4933/// Index-block search algorithm selected by
4934/// [`BlockBasedOptions::set_index_block_search_type`].
4935///
4936/// `Auto` is only meaningful in combination with
4937/// [`BlockBasedOptions::set_uniform_cv_threshold`]: the threshold gates whether
4938/// the per-block "is_uniform" footer bit is set on the write path, and `Auto`
4939/// reads that bit at lookup time to choose between binary and interpolation
4940/// search per index block. Without setting the threshold to a non-negative
4941/// value, `Auto` degenerates to binary search.
4942#[derive(Debug, Copy, Clone, PartialEq, Eq)]
4943#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
4944pub enum IndexBlockSearchType {
4945    /// Standard binary search. The default and safest choice.
4946    Binary = ffi::rocksdb_block_based_table_index_block_search_type_binary as isize,
4947    /// Interpolation search. Faster than binary search for index blocks whose
4948    /// keys are uniformly distributed; significantly slower when they are not.
4949    ///
4950    /// Only applicable when the byte-wise comparator is in use; with any
4951    /// other comparator the C++ code falls back to binary search regardless.
4952    ///
4953    /// Performance is significantly degraded when
4954    /// `IndexShorteningMode::kShortenSeparatorsAndSuccessor` is also set,
4955    /// because the shortened successor skews end-keys away from the uniform
4956    /// distribution that interpolation search relies on. Avoid combining the
4957    /// two.
4958    Interpolation = ffi::rocksdb_block_based_table_index_block_search_type_interpolation as isize,
4959    /// Per-block adaptive selection between binary and interpolation search,
4960    /// based on the per-block "is_uniform" footer bit. Requires
4961    /// `uniform_cv_threshold >= 0` on the write path; see
4962    /// [`BlockBasedOptions::set_uniform_cv_threshold`].
4963    Auto = ffi::rocksdb_block_based_table_index_block_search_type_auto as isize,
4964}
4965
4966pub struct FifoCompactOptions {
4967    pub(crate) inner: *mut ffi::rocksdb_fifo_compaction_options_t,
4968}
4969
4970impl Default for FifoCompactOptions {
4971    fn default() -> Self {
4972        let opts = unsafe { ffi::rocksdb_fifo_compaction_options_create() };
4973        assert!(
4974            !opts.is_null(),
4975            "Could not create RocksDB Fifo Compaction Options"
4976        );
4977
4978        Self { inner: opts }
4979    }
4980}
4981
4982impl Drop for FifoCompactOptions {
4983    fn drop(&mut self) {
4984        unsafe {
4985            ffi::rocksdb_fifo_compaction_options_destroy(self.inner);
4986        }
4987    }
4988}
4989
4990impl FifoCompactOptions {
4991    /// Sets the max table file size.
4992    ///
4993    /// Once the total sum of table files reaches this, we will delete the oldest
4994    /// table file
4995    ///
4996    /// Default: 1GB
4997    pub fn set_max_table_files_size(&mut self, nbytes: u64) {
4998        unsafe {
4999            ffi::rocksdb_fifo_compaction_options_set_max_table_files_size(self.inner, nbytes);
5000        }
5001    }
5002}
5003
5004#[derive(Debug, Copy, Clone, PartialEq, Eq)]
5005#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
5006pub enum UniversalCompactionStopStyle {
5007    Similar = ffi::rocksdb_similar_size_compaction_stop_style as isize,
5008    Total = ffi::rocksdb_total_size_compaction_stop_style as isize,
5009}
5010
5011pub struct UniversalCompactOptions {
5012    pub(crate) inner: *mut ffi::rocksdb_universal_compaction_options_t,
5013}
5014
5015impl Default for UniversalCompactOptions {
5016    fn default() -> Self {
5017        let opts = unsafe { ffi::rocksdb_universal_compaction_options_create() };
5018        assert!(
5019            !opts.is_null(),
5020            "Could not create RocksDB Universal Compaction Options"
5021        );
5022
5023        Self { inner: opts }
5024    }
5025}
5026
5027impl Drop for UniversalCompactOptions {
5028    fn drop(&mut self) {
5029        unsafe {
5030            ffi::rocksdb_universal_compaction_options_destroy(self.inner);
5031        }
5032    }
5033}
5034
5035impl UniversalCompactOptions {
5036    /// Sets the percentage flexibility while comparing file size.
5037    /// If the candidate file(s) size is 1% smaller than the next file's size,
5038    /// then include next file into this candidate set.
5039    ///
5040    /// Default: 1
5041    pub fn set_size_ratio(&mut self, ratio: c_int) {
5042        unsafe {
5043            ffi::rocksdb_universal_compaction_options_set_size_ratio(self.inner, ratio);
5044        }
5045    }
5046
5047    /// Sets the minimum number of files in a single compaction run.
5048    ///
5049    /// Default: 2
5050    pub fn set_min_merge_width(&mut self, num: c_int) {
5051        unsafe {
5052            ffi::rocksdb_universal_compaction_options_set_min_merge_width(self.inner, num);
5053        }
5054    }
5055
5056    /// Sets the maximum number of files in a single compaction run.
5057    ///
5058    /// Default: UINT_MAX
5059    pub fn set_max_merge_width(&mut self, num: c_int) {
5060        unsafe {
5061            ffi::rocksdb_universal_compaction_options_set_max_merge_width(self.inner, num);
5062        }
5063    }
5064
5065    /// sets the size amplification.
5066    ///
5067    /// It is defined as the amount (in percentage) of
5068    /// additional storage needed to store a single byte of data in the database.
5069    /// For example, a size amplification of 2% means that a database that
5070    /// contains 100 bytes of user-data may occupy upto 102 bytes of
5071    /// physical storage. By this definition, a fully compacted database has
5072    /// a size amplification of 0%. Rocksdb uses the following heuristic
5073    /// to calculate size amplification: it assumes that all files excluding
5074    /// the earliest file contribute to the size amplification.
5075    ///
5076    /// Default: 200, which means that a 100 byte database could require upto 300 bytes of storage.
5077    pub fn set_max_size_amplification_percent(&mut self, v: c_int) {
5078        unsafe {
5079            ffi::rocksdb_universal_compaction_options_set_max_size_amplification_percent(
5080                self.inner, v,
5081            );
5082        }
5083    }
5084
5085    /// Sets the percentage of compression size.
5086    ///
5087    /// If this option is set to be -1, all the output files
5088    /// will follow compression type specified.
5089    ///
5090    /// If this option is not negative, we will try to make sure compressed
5091    /// size is just above this value. In normal cases, at least this percentage
5092    /// of data will be compressed.
5093    /// When we are compacting to a new file, here is the criteria whether
5094    /// it needs to be compressed: assuming here are the list of files sorted
5095    /// by generation time:
5096    ///    A1...An B1...Bm C1...Ct
5097    /// where A1 is the newest and Ct is the oldest, and we are going to compact
5098    /// B1...Bm, we calculate the total size of all the files as total_size, as
5099    /// well as  the total size of C1...Ct as total_C, the compaction output file
5100    /// will be compressed iff
5101    ///   total_C / total_size < this percentage
5102    ///
5103    /// Default: -1
5104    pub fn set_compression_size_percent(&mut self, v: c_int) {
5105        unsafe {
5106            ffi::rocksdb_universal_compaction_options_set_compression_size_percent(self.inner, v);
5107        }
5108    }
5109
5110    /// Sets the algorithm used to stop picking files into a single compaction run.
5111    ///
5112    /// Default: ::Total
5113    pub fn set_stop_style(&mut self, style: UniversalCompactionStopStyle) {
5114        unsafe {
5115            ffi::rocksdb_universal_compaction_options_set_stop_style(self.inner, style as c_int);
5116        }
5117    }
5118}
5119
5120#[derive(Debug, Copy, Clone, PartialEq, Eq)]
5121#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
5122#[repr(u8)]
5123pub enum BottommostLevelCompaction {
5124    /// Skip bottommost level compaction
5125    Skip = 0,
5126    /// Only compact bottommost level if there is a compaction filter
5127    /// This is the default option
5128    IfHaveCompactionFilter,
5129    /// Always compact bottommost level
5130    Force,
5131    /// Always compact bottommost level but in bottommost level avoid
5132    /// double-compacting files created in the same compaction
5133    ForceOptimized,
5134}
5135
5136pub struct CompactOptions {
5137    pub(crate) inner: *mut ffi::rocksdb_compactoptions_t,
5138    full_history_ts_low: Option<Vec<u8>>,
5139}
5140
5141impl Default for CompactOptions {
5142    fn default() -> Self {
5143        let opts = unsafe { ffi::rocksdb_compactoptions_create() };
5144        assert!(!opts.is_null(), "Could not create RocksDB Compact Options");
5145
5146        Self {
5147            inner: opts,
5148            full_history_ts_low: None,
5149        }
5150    }
5151}
5152
5153impl Drop for CompactOptions {
5154    fn drop(&mut self) {
5155        unsafe {
5156            ffi::rocksdb_compactoptions_destroy(self.inner);
5157        }
5158    }
5159}
5160
5161impl CompactOptions {
5162    /// If more than one thread calls manual compaction,
5163    /// only one will actually schedule it while the other threads will simply wait
5164    /// for the scheduled manual compaction to complete. If exclusive_manual_compaction
5165    /// is set to true, the call will disable scheduling of automatic compaction jobs
5166    /// and wait for existing automatic compaction jobs to finish.
5167    pub fn set_exclusive_manual_compaction(&mut self, v: bool) {
5168        unsafe {
5169            ffi::rocksdb_compactoptions_set_exclusive_manual_compaction(
5170                self.inner,
5171                c_uchar::from(v),
5172            );
5173        }
5174    }
5175
5176    /// Sets bottommost level compaction.
5177    pub fn set_bottommost_level_compaction(&mut self, lvl: BottommostLevelCompaction) {
5178        unsafe {
5179            ffi::rocksdb_compactoptions_set_bottommost_level_compaction(self.inner, lvl as c_uchar);
5180        }
5181    }
5182
5183    /// If true, compacted files will be moved to the minimum level capable
5184    /// of holding the data or given level (specified non-negative target_level).
5185    pub fn set_change_level(&mut self, v: bool) {
5186        unsafe {
5187            ffi::rocksdb_compactoptions_set_change_level(self.inner, c_uchar::from(v));
5188        }
5189    }
5190
5191    /// If change_level is true and target_level have non-negative value, compacted
5192    /// files will be moved to target_level.
5193    pub fn set_target_level(&mut self, lvl: c_int) {
5194        unsafe {
5195            ffi::rocksdb_compactoptions_set_target_level(self.inner, lvl);
5196        }
5197    }
5198
5199    /// Set user-defined timestamp low bound, the data with older timestamp than
5200    /// low bound maybe GCed by compaction. Default: nullptr
5201    pub fn set_full_history_ts_low<S: Into<Vec<u8>>>(&mut self, ts: S) {
5202        self.set_full_history_ts_low_impl(Some(ts.into()));
5203    }
5204
5205    fn set_full_history_ts_low_impl(&mut self, ts: Option<Vec<u8>>) {
5206        let (ptr, len) = if let Some(ref ts) = ts {
5207            (ts.as_ptr().cast_mut().cast::<c_char>(), ts.len())
5208        } else if self.full_history_ts_low.is_some() {
5209            (std::ptr::null::<Vec<u8>>() as *mut c_char, 0)
5210        } else {
5211            return;
5212        };
5213        self.full_history_ts_low = ts;
5214        unsafe {
5215            ffi::rocksdb_compactoptions_set_full_history_ts_low(self.inner, ptr, len);
5216        }
5217    }
5218
5219    /// Override `CompactRangeOptions::blob_garbage_collection_age_cutoff` for a
5220    /// single manual compaction.
5221    ///
5222    /// If set to `< 0` or `> 1`, RocksDB leaves the
5223    /// `blob_garbage_collection_age_cutoff` from `ColumnFamilyOptions` in
5224    /// effect (this is the default, `-1`). Otherwise, it overrides the
5225    /// user-provided setting for the duration of this compaction. This
5226    /// enables callers to selectively override the age cutoff per
5227    /// `compact_range` call.
5228    ///
5229    /// See [`Options::set_blob_gc_age_cutoff`] for the CF-level setter that
5230    /// this value overrides.
5231    pub fn set_blob_garbage_collection_age_cutoff(&mut self, v: c_double) {
5232        unsafe {
5233            ffi::rocksdb_compactoptions_set_blob_garbage_collection_age_cutoff(self.inner, v);
5234        }
5235    }
5236}
5237
5238pub struct WaitForCompactOptions {
5239    pub(crate) inner: *mut ffi::rocksdb_wait_for_compact_options_t,
5240}
5241
5242impl Default for WaitForCompactOptions {
5243    fn default() -> Self {
5244        let opts = unsafe { ffi::rocksdb_wait_for_compact_options_create() };
5245        assert!(
5246            !opts.is_null(),
5247            "Could not create RocksDB Wait For Compact Options"
5248        );
5249
5250        Self { inner: opts }
5251    }
5252}
5253
5254impl Drop for WaitForCompactOptions {
5255    fn drop(&mut self) {
5256        unsafe {
5257            ffi::rocksdb_wait_for_compact_options_destroy(self.inner);
5258        }
5259    }
5260}
5261
5262impl WaitForCompactOptions {
5263    /// If true, abort waiting if background jobs are paused. If false,
5264    /// ContinueBackgroundWork() must be called to resume the background jobs.
5265    /// Otherwise, jobs that were queued, but not scheduled yet may never finish
5266    /// and WaitForCompact() may wait indefinitely (if timeout is set, it will
5267    /// abort after the timeout).
5268    ///
5269    /// Default: false
5270    pub fn set_abort_on_pause(&mut self, v: bool) {
5271        unsafe {
5272            ffi::rocksdb_wait_for_compact_options_set_abort_on_pause(self.inner, c_uchar::from(v));
5273        }
5274    }
5275
5276    /// If true, flush all column families before starting to wait.
5277    ///
5278    /// Default: false
5279    pub fn set_flush(&mut self, v: bool) {
5280        unsafe {
5281            ffi::rocksdb_wait_for_compact_options_set_flush(self.inner, c_uchar::from(v));
5282        }
5283    }
5284
5285    /// Timeout in microseconds for waiting for compaction to complete.
5286    /// when timeout == 0, WaitForCompact() will wait as long as there's background
5287    /// work to finish.
5288    ///
5289    /// Default: 0
5290    pub fn set_timeout(&mut self, microseconds: u64) {
5291        unsafe {
5292            ffi::rocksdb_wait_for_compact_options_set_timeout(self.inner, microseconds);
5293        }
5294    }
5295}
5296
5297/// Represents a path where sst files can be put into
5298pub struct DBPath {
5299    pub(crate) inner: *mut ffi::rocksdb_dbpath_t,
5300}
5301
5302impl DBPath {
5303    /// Create a new path
5304    pub fn new<P: AsRef<Path>>(path: P, target_size: u64) -> Result<Self, Error> {
5305        let p = to_cpath(path.as_ref()).unwrap();
5306        let dbpath = unsafe { ffi::rocksdb_dbpath_create(p.as_ptr(), target_size) };
5307        if dbpath.is_null() {
5308            Err(Error::new(format!(
5309                "Could not create path for storing sst files at location: {}",
5310                path.as_ref().display()
5311            )))
5312        } else {
5313            Ok(DBPath { inner: dbpath })
5314        }
5315    }
5316}
5317
5318impl Drop for DBPath {
5319    fn drop(&mut self) {
5320        unsafe {
5321            ffi::rocksdb_dbpath_destroy(self.inner);
5322        }
5323    }
5324}
5325
5326pub struct InfoLogger {
5327    pub(crate) inner: *mut ffi::rocksdb_logger_t,
5328    callback: Option<Arc<LoggerCallback>>,
5329}
5330
5331impl InfoLogger {
5332    /// Creates a new logger that redirects logs to `STDERR` with an optional
5333    /// prefix.
5334    pub fn new_stderr_logger<S: AsRef<str>>(log_level: LogLevel, prefix: Option<S>) -> Self {
5335        let prefix = prefix.map(|s| {
5336            s.as_ref()
5337                .into_c_string()
5338                .expect("cannot have NULL in prefix")
5339        });
5340        let prefix_ptr = match prefix.as_ref() {
5341            Some(s) => s.as_ptr(),
5342            None => std::ptr::null(),
5343        };
5344        let inner =
5345            unsafe { ffi::rocksdb_logger_create_stderr_logger(log_level as i32, prefix_ptr) };
5346        Self {
5347            inner,
5348            // no Rust callback: RocksDB implements this
5349            callback: None,
5350        }
5351    }
5352
5353    /// Creates a new logger that redirects logs to a custom callback.
5354    pub fn new_callback_logger<F: Fn(LogLevel, &str) + Sync + Send + 'static>(
5355        level: LogLevel,
5356        cb: F,
5357    ) -> Self {
5358        // use an Arc<Box<...>> so we can reference count, and still pass a thin pointer to C
5359        let arc_cb: Arc<LoggerCallback> = Arc::new(Box::new(cb));
5360        let raw_cb: LoggerCallbackPtr = Arc::as_ptr(&arc_cb);
5361        let inner = unsafe {
5362            ffi::rocksdb_logger_create_callback_logger(
5363                level as i32,
5364                Some(logger_callback),
5365                raw_cb as *mut c_void,
5366            )
5367        };
5368        Self {
5369            inner,
5370            callback: Some(arc_cb),
5371        }
5372    }
5373}
5374
5375impl Drop for InfoLogger {
5376    fn drop(&mut self) {
5377        unsafe {
5378            ffi::rocksdb_logger_destroy(self.inner);
5379        }
5380    }
5381}
5382
5383/// Options for importing column families. See
5384/// [DB::create_column_family_with_import](crate::DB::create_column_family_with_import).
5385pub struct ImportColumnFamilyOptions {
5386    pub(crate) inner: *mut ffi::rocksdb_import_column_family_options_t,
5387}
5388
5389impl ImportColumnFamilyOptions {
5390    pub fn new() -> Self {
5391        let inner = unsafe { ffi::rocksdb_import_column_family_options_create() };
5392        ImportColumnFamilyOptions { inner }
5393    }
5394
5395    /// Determines whether to move the provided set of files on import. The default
5396    /// behavior is to copy the external files on import. Setting `move_files` to `true`
5397    /// will move the files instead of copying them. See
5398    /// [DB::create_column_family_with_import](crate::DB::create_column_family_with_import)
5399    /// for more information.
5400    pub fn set_move_files(&mut self, move_files: bool) {
5401        unsafe {
5402            ffi::rocksdb_import_column_family_options_set_move_files(
5403                self.inner,
5404                c_uchar::from(move_files),
5405            );
5406        }
5407    }
5408}
5409
5410impl Default for ImportColumnFamilyOptions {
5411    fn default() -> Self {
5412        Self::new()
5413    }
5414}
5415
5416impl Drop for ImportColumnFamilyOptions {
5417    fn drop(&mut self) {
5418        unsafe { ffi::rocksdb_import_column_family_options_destroy(self.inner) }
5419    }
5420}
5421
5422/// Ensures the unsafe casts use the same type.
5423type LoggerCallbackPtr = *const LoggerCallback;
5424
5425unsafe extern "C" fn logger_callback(
5426    raw_cb: *mut c_void,
5427    level: c_uint,
5428    msg: *mut c_char,
5429    len: size_t,
5430) {
5431    let rust_callback: &LoggerCallback = unsafe { &*(raw_cb as LoggerCallbackPtr) };
5432    let raw_msg = if len == 0 {
5433        &[][..]
5434    } else {
5435        unsafe { std::slice::from_raw_parts(msg.cast_const().cast::<u8>(), len) }
5436    };
5437    let msg = String::from_utf8_lossy(raw_msg);
5438    // Don't panic on an unexpected level: this runs in an `extern "C"` frame,
5439    // where unwinding aborts the process. Losing the exact level of one log
5440    // line is not worth taking the process down for.
5441    let level = LogLevel::try_from_raw(level as i32).unwrap_or(LogLevel::Info);
5442    (rust_callback)(level, &msg);
5443}
5444
5445#[cfg(test)]
5446mod tests {
5447    use crate::cache::Cache;
5448    use crate::db_options::{DBCompactionPri, InfoLogger, WriteBufferManager};
5449    use crate::{MemtableFactory, Options};
5450
5451    /// `set_prefix_range_in_place` is an allocation-free reimplementation of
5452    /// `set_iterate_range(PrefixRange(..))`. It has to produce byte-identical
5453    /// bounds, including for the awkward cases: empty prefixes, trailing 0xff
5454    /// bytes, and all-0xff prefixes (which have no successor).
5455    #[test]
5456    fn prefix_range_in_place_matches_prefix_range() {
5457        let cases: &[&[u8]] = &[
5458            b"",
5459            b"a",
5460            b"foo",
5461            b"\x00",
5462            b"\xff",
5463            b"\xff\xff",
5464            b"a\xff",
5465            b"a\xff\xff",
5466            b"\xfe\xff",
5467            b"prefix\x00\xff",
5468        ];
5469
5470        for prefix in cases {
5471            let mut expected = crate::ReadOptions::default();
5472            expected.set_iterate_range(crate::PrefixRange(*prefix));
5473
5474            let mut actual = crate::ReadOptions::default();
5475            actual.set_prefix_range_in_place(prefix);
5476
5477            assert_eq!(
5478                actual.iterate_lower_bound, expected.iterate_lower_bound,
5479                "lower bound mismatch for prefix {prefix:?}"
5480            );
5481            assert_eq!(
5482                actual.iterate_upper_bound, expected.iterate_upper_bound,
5483                "upper bound mismatch for prefix {prefix:?}"
5484            );
5485        }
5486    }
5487
5488    /// The whole point of the in-place setter is that a reused `ReadOptions`
5489    /// stops reallocating, so overwriting the bounds repeatedly must keep the
5490    /// results correct rather than leaving stale bytes behind.
5491    #[test]
5492    fn prefix_range_in_place_is_reusable() {
5493        let mut opts = crate::ReadOptions::default();
5494
5495        opts.set_prefix_range_in_place(b"aaaa");
5496        assert_eq!(opts.iterate_lower_bound.as_deref(), Some(&b"aaaa"[..]));
5497        assert_eq!(opts.iterate_upper_bound.as_deref(), Some(&b"aaab"[..]));
5498
5499        // Shorter prefix must truncate, not leave the tail of the previous one.
5500        opts.set_prefix_range_in_place(b"b");
5501        assert_eq!(opts.iterate_lower_bound.as_deref(), Some(&b"b"[..]));
5502        assert_eq!(opts.iterate_upper_bound.as_deref(), Some(&b"c"[..]));
5503
5504        // An all-0xff prefix has no successor: the upper bound must be cleared.
5505        opts.set_prefix_range_in_place(b"\xff");
5506        assert_eq!(opts.iterate_lower_bound.as_deref(), Some(&b"\xff"[..]));
5507        assert_eq!(opts.iterate_upper_bound, None);
5508
5509        // An empty prefix is the full range: both bounds cleared.
5510        opts.set_prefix_range_in_place(b"");
5511        assert_eq!(opts.iterate_lower_bound, None);
5512        assert_eq!(opts.iterate_upper_bound, None);
5513    }
5514
5515    #[test]
5516    fn test_enable_statistics() {
5517        let mut opts = Options::default();
5518        assert_eq!(None, opts.get_statistics());
5519        opts.enable_statistics();
5520        opts.set_stats_dump_period_sec(60);
5521        assert!(opts.get_statistics().is_some());
5522
5523        let opts = Options::default();
5524        assert!(opts.get_statistics().is_none());
5525    }
5526
5527    #[test]
5528    fn test_set_memtable_factory() {
5529        let mut opts = Options::default();
5530        opts.set_memtable_factory(MemtableFactory::Vector);
5531        opts.set_memtable_factory(MemtableFactory::HashLinkList { bucket_count: 100 });
5532        opts.set_memtable_factory(MemtableFactory::HashSkipList {
5533            bucket_count: 100,
5534            height: 4,
5535            branching_factor: 4,
5536        });
5537    }
5538
5539    #[test]
5540    fn test_use_fsync() {
5541        let mut opts = Options::default();
5542        assert!(!opts.get_use_fsync());
5543        opts.set_use_fsync(true);
5544        assert!(opts.get_use_fsync());
5545    }
5546
5547    #[test]
5548    fn test_set_stats_persist_period_sec() {
5549        let mut opts = Options::default();
5550        opts.enable_statistics();
5551        opts.set_stats_persist_period_sec(5);
5552        assert!(opts.get_statistics().is_some());
5553
5554        let opts = Options::default();
5555        assert!(opts.get_statistics().is_none());
5556    }
5557
5558    #[test]
5559    fn test_set_write_buffer_manager() {
5560        let mut opts = Options::default();
5561        let lrucache = Cache::new_lru_cache(100);
5562        let write_buffer_manager =
5563            WriteBufferManager::new_write_buffer_manager_with_cache(100, false, lrucache);
5564        assert_eq!(write_buffer_manager.get_buffer_size(), 100);
5565        assert_eq!(write_buffer_manager.get_usage(), 0);
5566        assert!(write_buffer_manager.enabled());
5567
5568        opts.set_write_buffer_manager(&write_buffer_manager);
5569        drop(opts);
5570
5571        // WriteBufferManager outlives options
5572        assert!(write_buffer_manager.enabled());
5573    }
5574
5575    #[test]
5576    fn compaction_pri() {
5577        let mut opts = Options::default();
5578        opts.set_compaction_pri(DBCompactionPri::RoundRobin);
5579        opts.create_if_missing(true);
5580        let tmp = tempfile::tempdir().unwrap();
5581        let _db = crate::DB::open(&opts, tmp.path()).unwrap();
5582
5583        let options = std::fs::read_dir(tmp.path())
5584            .unwrap()
5585            .find_map(|x| {
5586                let x = x.ok()?;
5587                x.file_name()
5588                    .into_string()
5589                    .unwrap()
5590                    .contains("OPTIONS")
5591                    .then_some(x.path())
5592            })
5593            .map(std::fs::read_to_string)
5594            .unwrap()
5595            .unwrap();
5596
5597        assert!(options.contains("compaction_pri=kRoundRobin"));
5598    }
5599
5600    #[test]
5601    fn test_callback_logger() {
5602        let (log_snd, log_rcv) = std::sync::mpsc::channel();
5603        let callback = move |level, msg: &str| {
5604            log_snd.send((level, msg.to_string())).ok();
5605        };
5606
5607        let mut opts = Options::default();
5608        opts.create_if_missing(true);
5609        opts.set_info_logger(InfoLogger::new_callback_logger(
5610            super::LogLevel::Debug,
5611            callback,
5612        ));
5613
5614        // create 2 DBs with the options then drop the options to ensure it is reference counted
5615        let tmp = tempfile::tempdir().unwrap();
5616        let db = crate::DB::open(&opts, tmp.path()).unwrap();
5617        db.put(b"testkey", b"testvalue").unwrap();
5618        db.flush().unwrap();
5619        db.delete(b"testkey").unwrap();
5620        db.flush().unwrap();
5621        db.compact_range(Some(b"a"), Some(b"z"));
5622        assert!(log_rcv.try_recv().is_ok());
5623        drop(db);
5624
5625        let tmp2 = tempfile::tempdir().unwrap();
5626        let db2 = crate::DB::open(&opts, tmp2.path()).unwrap();
5627
5628        // get the configured logger before dropping the options
5629        let logger = opts.get_info_logger();
5630        drop(opts);
5631
5632        // clear the logs and make sure the callback is called by db2
5633        while log_rcv.try_recv().is_ok() {}
5634        assert!(log_rcv.try_recv().is_err());
5635
5636        db2.put(b"testkey2", b"testvalue2").unwrap();
5637        db2.flush().unwrap();
5638        db2.delete(b"testkey2").unwrap();
5639        db2.flush().unwrap();
5640        db2.compact_range(Some(b"a"), Some(b"z"));
5641
5642        drop(db2);
5643        assert!(log_rcv.try_recv().is_ok());
5644
5645        // clear the logs
5646        while log_rcv.try_recv().is_ok() {}
5647        assert!(log_rcv.try_recv().is_err());
5648
5649        // create a db with the copied logger to check lifetimes
5650        let tmp3 = tempfile::tempdir().unwrap();
5651        let mut opts2 = Options::default();
5652        opts2.create_if_missing(true);
5653        opts2.set_info_logger(logger);
5654        let db3 = crate::DB::open(&opts2, tmp3.path()).unwrap();
5655        drop(opts2);
5656        db3.put(b"testkey3", b"testvalue3").unwrap();
5657        db3.flush().unwrap();
5658        db3.delete(b"testkey3").unwrap();
5659        db3.flush().unwrap();
5660        db3.compact_range(Some(b"a"), Some(b"z"));
5661        assert!(log_rcv.try_recv().is_ok());
5662        drop(db3);
5663    }
5664}