Skip to main content

rust_rocksdb/
lib.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//
15
16//! Rust wrapper for RocksDB.
17//!
18//! # Examples
19//!
20//! ```
21//! use rust_rocksdb::{DB, Options};
22//! // NB: db is automatically closed at end of lifetime
23//! let tempdir = tempfile::Builder::new()
24//!     .prefix("_path_for_rocksdb_storage")
25//!     .tempdir()
26//!     .expect("Failed to create temporary path for the _path_for_rocksdb_storage");
27//! let path = tempdir.path();
28//! {
29//!    let db = DB::open_default(path).unwrap();
30//!    db.put(b"my key", b"my value").unwrap();
31//!    match db.get(b"my key") {
32//!        Ok(Some(value)) => println!("retrieved value {}", String::from_utf8(value).unwrap()),
33//!        Ok(None) => println!("value not found"),
34//!        Err(e) => println!("operational problem encountered: {}", e),
35//!    }
36//!    db.delete(b"my key").unwrap();
37//! }
38//! let _ = DB::destroy(&Options::default(), path);
39//! ```
40//!
41//! Opening a database and a single column family with custom options:
42//!
43//! ```
44//! use rust_rocksdb::{DB, ColumnFamilyDescriptor, Options};
45//!
46//! let tempdir = tempfile::Builder::new()
47//!     .prefix("_path_for_rocksdb_storage_with_cfs")
48//!     .tempdir()
49//!     .expect("Failed to create temporary path for the _path_for_rocksdb_storage_with_cfs.");
50//! let path = tempdir.path();
51//! let mut cf_opts = Options::default();
52//! cf_opts.set_max_write_buffer_number(16);
53//! let cf = ColumnFamilyDescriptor::new("cf1", cf_opts);
54//!
55//! let mut db_opts = Options::default();
56//! db_opts.create_missing_column_families(true);
57//! db_opts.create_if_missing(true);
58//! {
59//!     let db = DB::open_cf_descriptors(&db_opts, path, vec![cf]).unwrap();
60//! }
61//! let _ = DB::destroy(&db_opts, path);
62//! ```
63//!
64
65// Only docs.rs passes `--cfg docsrs`, and only it builds on nightly, so this
66// is inert everywhere else. It is what puts the "available on crate feature X
67// only" badges on gated items. `auto_cfg` is on by default under this gate, so
68// individual items do not need annotating.
69#![cfg_attr(docsrs, feature(doc_cfg))]
70#![warn(clippy::pedantic)]
71#![allow(
72    // Next `cast_*` lints don't give alternatives.
73    clippy::cast_possible_wrap, clippy::cast_possible_truncation, clippy::cast_sign_loss,
74    // Next lints produce too much noise/false positives.
75    clippy::module_name_repetitions, clippy::similar_names, clippy::must_use_candidate,
76    // '... may panic' lints.
77    // Too much work to fix.
78    clippy::missing_errors_doc,
79    clippy::should_panic_without_expect,
80    // False positive: WebSocket
81    clippy::doc_markdown,
82    clippy::missing_safety_doc,
83    clippy::needless_pass_by_value,
84    clippy::ptr_as_ptr,
85    clippy::missing_panics_doc,
86    clippy::from_over_into,
87)]
88
89#[macro_use]
90mod ffi_util;
91
92pub mod backup;
93mod cache;
94pub mod checkpoint;
95mod column_family;
96pub mod compaction_filter;
97pub mod compaction_filter_factory;
98mod comparator;
99mod db;
100mod db_iterator;
101mod db_options;
102mod db_pinnable_batch;
103mod db_pinnable_slice;
104mod env;
105pub mod event_listener;
106mod iter_range;
107pub mod merge_operator;
108pub mod perf;
109mod prop_name;
110pub mod properties;
111mod slice_transform;
112mod snapshot;
113pub mod sst_file_manager;
114mod sst_file_writer;
115pub mod statistics;
116mod transactions;
117mod write_batch;
118mod write_batch_with_index;
119mod write_buffer_manager;
120
121pub use crate::{
122    cache::Cache,
123    column_family::{
124        AsColumnFamilyRef, BoundColumnFamily, ColumnFamily, ColumnFamilyDescriptor,
125        ColumnFamilyRef, ColumnFamilyTtl, DEFAULT_COLUMN_FAMILY_NAME,
126    },
127    compaction_filter::Decision as CompactionDecision,
128    db::{
129        ColumnFamilyMetaData, DB, DBAccess, DBCommon, DBWithThreadMode, ExportImportFilesMetaData,
130        GetIntoBufferResult, LiveFile, MultiThreaded, PrefixProber, Range, SingleThreaded,
131        ThreadMode,
132    },
133    db_iterator::{
134        DBIterator, DBIteratorWithThreadMode, DBRawIterator, DBRawIteratorWithThreadMode,
135        DBWALIterator, Direction, IteratorMode,
136    },
137    db_options::{
138        BlockBasedIndexType, BlockBasedOptions, BlockBasedPinningTier, BottommostLevelCompaction,
139        ChecksumType, CompactOptions, CuckooTableOptions, DBCompactionPri, DBCompactionStyle,
140        DBCompressionType, DBPath, DBRecoveryMode, DataBlockIndexType, FifoCompactOptions,
141        FlushOptions, ImportColumnFamilyOptions, IndexBlockSearchType, InfoLogger,
142        IngestExternalFileOptions, KeyEncodingType, LogLevel, LruCacheOptions, MemtableFactory,
143        Options, PlainTableFactoryOptions, RateLimiterMode, ReadOptions, ReadTier,
144        UniversalCompactOptions, UniversalCompactionStopStyle, WaitForCompactOptions, WriteOptions,
145    },
146    db_pinnable_batch::{DBPinnableBatch, DBPinnableBatchIter},
147    db_pinnable_slice::DBPinnableSlice,
148    env::Env,
149    ffi_util::{CSlice, CStrLike},
150    iter_range::{IterateBounds, PrefixRange},
151    merge_operator::MergeOperands,
152    perf::{PerfContext, PerfMetric, PerfStatsLevel, with_thread_local},
153    slice_transform::SliceTransform,
154    snapshot::{Snapshot, SnapshotReadOptions, SnapshotWithThreadMode},
155    sst_file_manager::SstFileManager,
156    sst_file_writer::SstFileWriter,
157    transactions::{
158        OptimisticTransactionDB, OptimisticTransactionOptions, Transaction, TransactionDB,
159        TransactionDBOptions, TransactionOptions,
160    },
161    write_batch::{
162        WriteBatch, WriteBatchIterator, WriteBatchIteratorCf, WriteBatchWithTransaction,
163    },
164    write_batch_with_index::WriteBatchWithIndex,
165    write_buffer_manager::WriteBufferManager,
166};
167
168use rust_librocksdb_sys as ffi;
169
170/// The raw `librocksdb` bindings this crate is built against.
171///
172/// [`AsRawPtr`] hands out pointers to types from this crate, such as
173/// `ffi::rocksdb_t`, so callers need a way to name them. Reaching them through
174/// a separate `rust-librocksdb-sys` dependency would mean keeping that
175/// version in lockstep with this crate's by hand, and every sys bump would
176/// break it. Going through this re-export keeps the two tied together.
177///
178/// Everything here is generated by bindgen and is not covered by this crate's
179/// semver guarantee. It changes whenever the vendored RocksDB does.
180#[cfg(feature = "raw-ptr")]
181pub use rust_librocksdb_sys as ffi_raw;
182
183/// Returns `true` if this crate was built with the `coroutines` feature, in
184/// which case librocksdb was compiled with `USE_COROUTINES` and linked
185/// against folly.
186///
187/// When `true`, calling [`ReadOptions::set_async_io(true)`][async-io] on a
188/// `MultiGet` activates the multi-level parallel-read path described in the
189/// RocksDB [Asynchronous IO blog post]. When `false`, `MultiGet` with
190/// `async_io=true` can only parallelize reads within a single LSM level.
191///
192/// Note: this reflects how this crate was configured, not what is in the
193/// linked `librocksdb`. If you used `ROCKSDB_LIB_DIR` to link against an
194/// externally-built `librocksdb.a`, the answer here may not match what that
195/// library was actually compiled with.
196///
197/// [async-io]: ReadOptions::set_async_io
198/// [Asynchronous IO blog post]: https://rocksdb.org/blog/2022/10/07/asynchronous-io-in-rocksdb.html
199#[must_use]
200pub fn built_with_coroutines() -> bool {
201    cfg!(feature = "coroutines")
202}
203
204#[cfg(feature = "raw-ptr")]
205mod raw_ptr;
206
207#[cfg(feature = "raw-ptr")]
208pub use crate::raw_ptr::AsRawPtr;
209
210use std::error;
211use std::fmt;
212
213/// RocksDB error kind.
214#[derive(Debug, Clone, PartialEq, Eq)]
215pub enum ErrorKind {
216    NotFound,
217    Corruption,
218    NotSupported,
219    InvalidArgument,
220    IOError,
221    MergeInProgress,
222    Incomplete,
223    ShutdownInProgress,
224    TimedOut,
225    Aborted,
226    Busy,
227    Expired,
228    TryAgain,
229    CompactionTooLarge,
230    ColumnFamilyDropped,
231    Unknown,
232}
233
234/// A simple wrapper round a string, used for errors reported from
235/// ffi calls.
236#[derive(Debug, Clone, PartialEq, Eq)]
237pub struct Error {
238    message: String,
239}
240
241impl Error {
242    fn new(message: String) -> Error {
243        Error { message }
244    }
245
246    pub fn into_string(self) -> String {
247        self.into()
248    }
249
250    /// Parse corresponding [`ErrorKind`] from error message.
251    pub fn kind(&self) -> ErrorKind {
252        match self.message.split(':').next().unwrap_or("") {
253            "NotFound" => ErrorKind::NotFound,
254            "Corruption" => ErrorKind::Corruption,
255            "Not implemented" => ErrorKind::NotSupported,
256            "Invalid argument" => ErrorKind::InvalidArgument,
257            "IO error" => ErrorKind::IOError,
258            "Merge in progress" => ErrorKind::MergeInProgress,
259            "Result incomplete" => ErrorKind::Incomplete,
260            "Shutdown in progress" => ErrorKind::ShutdownInProgress,
261            "Operation timed out" => ErrorKind::TimedOut,
262            "Operation aborted" => ErrorKind::Aborted,
263            "Resource busy" => ErrorKind::Busy,
264            "Operation expired" => ErrorKind::Expired,
265            "Operation failed. Try again." => ErrorKind::TryAgain,
266            "Compaction too large" => ErrorKind::CompactionTooLarge,
267            "Column family dropped" => ErrorKind::ColumnFamilyDropped,
268            _ => ErrorKind::Unknown,
269        }
270    }
271}
272
273impl AsRef<str> for Error {
274    fn as_ref(&self) -> &str {
275        &self.message
276    }
277}
278
279impl From<Error> for String {
280    fn from(e: Error) -> String {
281        e.message
282    }
283}
284
285impl error::Error for Error {
286    fn description(&self) -> &str {
287        &self.message
288    }
289}
290
291impl fmt::Display for Error {
292    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
293        self.message.fmt(formatter)
294    }
295}
296
297#[cfg(test)]
298mod test {
299    use crate::{
300        OptimisticTransactionDB, OptimisticTransactionOptions, Transaction, TransactionDB,
301        TransactionDBOptions, TransactionOptions,
302        cache::{Cache, CacheWrapper},
303        write_buffer_manager::{WriteBufferManager, WriteBufferManagerWrapper},
304    };
305
306    use super::{
307        BlockBasedOptions, BoundColumnFamily, ColumnFamily, ColumnFamilyDescriptor, DB, DBIterator,
308        DBRawIterator, IngestExternalFileOptions, Options, PlainTableFactoryOptions, ReadOptions,
309        Snapshot, SstFileWriter, WriteBatch, WriteOptions,
310        column_family::UnboundColumnFamily,
311        env::{Env, EnvWrapper},
312    };
313
314    #[test]
315    fn is_send() {
316        // test (at compile time) that certain types implement the auto-trait Send, either directly for
317        // pointer-wrapping types or transitively for types with all Send fields
318
319        fn is_send<T: Send>() {
320            // dummy function just used for its parameterized type bound
321        }
322
323        is_send::<DB>();
324        is_send::<DBIterator<'_>>();
325        is_send::<DBRawIterator<'_>>();
326        is_send::<Snapshot>();
327        is_send::<Options>();
328        is_send::<ReadOptions>();
329        is_send::<WriteOptions>();
330        is_send::<IngestExternalFileOptions>();
331        is_send::<BlockBasedOptions>();
332        is_send::<PlainTableFactoryOptions>();
333        is_send::<ColumnFamilyDescriptor>();
334        is_send::<ColumnFamily>();
335        is_send::<BoundColumnFamily<'_>>();
336        is_send::<UnboundColumnFamily>();
337        is_send::<SstFileWriter>();
338        is_send::<WriteBatch>();
339        is_send::<Cache>();
340        is_send::<CacheWrapper>();
341        is_send::<Env>();
342        is_send::<EnvWrapper>();
343        is_send::<TransactionDB>();
344        is_send::<OptimisticTransactionDB>();
345        is_send::<Transaction<'_, TransactionDB>>();
346        is_send::<TransactionDBOptions>();
347        is_send::<OptimisticTransactionOptions>();
348        is_send::<TransactionOptions>();
349        is_send::<WriteBufferManager>();
350        is_send::<WriteBufferManagerWrapper>();
351    }
352
353    #[test]
354    fn is_sync() {
355        // test (at compile time) that certain types implement the auto-trait Sync
356
357        fn is_sync<T: Sync>() {
358            // dummy function just used for its parameterized type bound
359        }
360
361        is_sync::<DB>();
362        is_sync::<Snapshot>();
363        is_sync::<Options>();
364        is_sync::<ReadOptions>();
365        is_sync::<WriteOptions>();
366        is_sync::<IngestExternalFileOptions>();
367        is_sync::<BlockBasedOptions>();
368        is_sync::<PlainTableFactoryOptions>();
369        is_sync::<UnboundColumnFamily>();
370        is_sync::<ColumnFamilyDescriptor>();
371        is_sync::<ColumnFamily>();
372        is_sync::<SstFileWriter>();
373        is_sync::<Cache>();
374        is_sync::<CacheWrapper>();
375        is_sync::<Env>();
376        is_sync::<EnvWrapper>();
377        is_sync::<TransactionDB>();
378        is_sync::<OptimisticTransactionDB>();
379        is_sync::<TransactionDBOptions>();
380        is_sync::<OptimisticTransactionOptions>();
381        is_sync::<TransactionOptions>();
382        is_sync::<WriteBufferManager>();
383        is_sync::<WriteBufferManagerWrapper>();
384    }
385}