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