Skip to main content

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