Skip to main content

rust_rocksdb/
snapshot.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 crate::{
16    AsColumnFamilyRef, DB, DBIteratorWithThreadMode, DBPinnableSlice, DBRawIteratorWithThreadMode,
17    Error, IteratorMode, ReadOptions, db::DBAccess, ffi,
18};
19
20/// A type alias to keep compatibility. See [`SnapshotWithThreadMode`] for details
21pub type Snapshot<'a> = SnapshotWithThreadMode<'a, DB>;
22
23/// Reusable read state bound to a [`SnapshotWithThreadMode`].
24///
25/// Creating this value configures one native [`ReadOptions`] with the snapshot.
26/// Its get and multi-get methods reuse those options until the session is
27/// dropped. Custom options can be supplied with
28/// [`SnapshotWithThreadMode::read_options_opt`].
29///
30/// The session cannot outlive its snapshot:
31///
32/// ```compile_fail,E0597
33/// use rust_rocksdb::DB;
34///
35/// let db = DB::open_default("foo").unwrap();
36/// let _read_options = {
37///     let snapshot = db.snapshot();
38///     snapshot.read_options()
39/// };
40/// ```
41pub struct SnapshotReadOptions<'snapshot, 'db, D: DBAccess = DB> {
42    snapshot: &'snapshot SnapshotWithThreadMode<'db, D>,
43    readopts: ReadOptions,
44}
45
46/// A consistent view of the database at the point of creation.
47///
48/// # Examples
49///
50/// ```
51/// use rust_rocksdb::{DB, IteratorMode, Options};
52///
53/// let tempdir = tempfile::Builder::new()
54///     .prefix("_path_for_rocksdb_storage3")
55///     .tempdir()
56///     .expect("Failed to create temporary path for the _path_for_rocksdb_storage3");
57/// let path = tempdir.path();
58/// {
59///     let db = DB::open_default(path).unwrap();
60///     let snapshot = db.snapshot(); // Creates a longer-term snapshot of the DB, but closed when goes out of scope
61///     let mut iter = snapshot.iterator(IteratorMode::Start); // Make as many iterators as you'd like from one snapshot
62/// }
63/// let _ = DB::destroy(&Options::default(), path);
64/// ```
65///
66/// A `Snapshot` must not outlive the `DB` it was created from:
67///
68/// ```compile_fail,E0597
69/// use rust_rocksdb::DB;
70///
71/// let _snapshot = {
72///     let db = DB::open_default("foo").unwrap();
73///     db.snapshot()
74/// };
75/// ```
76///
77/// An iterator created from a snapshot must not outlive the snapshot either:
78/// RocksDB keeps the `const Snapshot*` inside the iterator's `ReadOptions`.
79///
80/// ```compile_fail,E0597
81/// use rust_rocksdb::{DB, IteratorMode};
82///
83/// let db = DB::open_default("foo").unwrap();
84/// let mut iter = {
85///     let snapshot = db.snapshot();
86///     snapshot.iterator(IteratorMode::Start)
87/// };
88/// let _ = iter.next();
89/// ```
90pub struct SnapshotWithThreadMode<'a, D: DBAccess> {
91    db: &'a D,
92    pub(crate) inner: *const ffi::rocksdb_snapshot_t,
93}
94
95impl<'a, D: DBAccess> SnapshotWithThreadMode<'a, D> {
96    /// Creates a new `SnapshotWithThreadMode` of the database `db`.
97    pub fn new(db: &'a D) -> Self {
98        let snapshot = unsafe { db.create_snapshot() };
99        Self {
100            db,
101            inner: snapshot,
102        }
103    }
104
105    /// Returns the sequence number of the snapshot, or `None` if there is no
106    /// underlying snapshot.
107    ///
108    /// A [`Transaction`](crate::Transaction) that was not started with
109    /// [`TransactionOptions::set_snapshot(true)`](crate::TransactionOptions::set_snapshot)
110    /// still hands out a snapshot handle, but that handle wraps a null
111    /// `rocksdb::Snapshot*`. Upstream's
112    /// `rocksdb_snapshot_get_sequence_number` dereferences it unconditionally,
113    /// so this used to be a null dereference reachable from safe code:
114    ///
115    /// ```no_run
116    /// # use rust_rocksdb::{SingleThreaded, TransactionDB};
117    /// # let db = TransactionDB::<SingleThreaded>::open_default("foo").unwrap();
118    /// let txn = db.transaction(); // no set_snapshot(true)
119    /// assert_eq!(txn.snapshot().sequence_number(), None);
120    /// ```
121    pub fn sequence_number(&self) -> Option<u64> {
122        let mut seqno: u64 = 0;
123        let present = unsafe {
124            ffi::rust_rocksdb_snapshot_try_get_sequence_number(self.inner, &raw mut seqno)
125        };
126        (present != 0).then_some(seqno)
127    }
128
129    /// Creates reusable default read options bound to this snapshot.
130    pub fn read_options(&'_ self) -> SnapshotReadOptions<'_, 'a, D> {
131        self.read_options_opt(ReadOptions::default())
132    }
133
134    /// Creates reusable custom read options bound to this snapshot.
135    ///
136    /// Any snapshot already configured on `readopts` is replaced with this
137    /// snapshot.
138    pub fn read_options_opt(&'_ self, mut readopts: ReadOptions) -> SnapshotReadOptions<'_, 'a, D> {
139        readopts.set_snapshot(self);
140        SnapshotReadOptions {
141            snapshot: self,
142            readopts,
143        }
144    }
145
146    /// Creates an iterator over the data in this snapshot, using the default read options.
147    ///
148    /// The iterator borrows the snapshot: the `ReadOptions` handed to RocksDB
149    /// carry a `const Snapshot*` that the iterator keeps, so it must not outlive
150    /// the snapshot.
151    pub fn iterator(&'_ self, mode: IteratorMode) -> DBIteratorWithThreadMode<'_, D> {
152        let readopts = ReadOptions::default();
153        self.iterator_opt(mode, readopts)
154    }
155
156    /// Creates an iterator over the data in this snapshot under the given column family, using
157    /// the default read options.
158    pub fn iterator_cf(
159        &'_ self,
160        cf_handle: &impl AsColumnFamilyRef,
161        mode: IteratorMode,
162    ) -> DBIteratorWithThreadMode<'_, D> {
163        let readopts = ReadOptions::default();
164        self.iterator_cf_opt(cf_handle, readopts, mode)
165    }
166
167    /// Creates an iterator over the data in this snapshot, using the given read options.
168    ///
169    /// The iterator borrows the snapshot; see [`Self::iterator`].
170    pub fn iterator_opt(
171        &'_ self,
172        mode: IteratorMode,
173        mut readopts: ReadOptions,
174    ) -> DBIteratorWithThreadMode<'_, D> {
175        readopts.set_snapshot(self);
176        DBIteratorWithThreadMode::<D>::new(self.db, readopts, mode)
177    }
178
179    /// Creates an iterator over the data in this snapshot under the given column family, using
180    /// the given read options.
181    pub fn iterator_cf_opt(
182        &'_ self,
183        cf_handle: &impl AsColumnFamilyRef,
184        mut readopts: ReadOptions,
185        mode: IteratorMode,
186    ) -> DBIteratorWithThreadMode<'_, D> {
187        readopts.set_snapshot(self);
188        DBIteratorWithThreadMode::new_cf(self.db, cf_handle.inner(), readopts, mode)
189    }
190
191    /// Creates a raw iterator over the data in this snapshot, using the default read options.
192    pub fn raw_iterator(&'_ self) -> DBRawIteratorWithThreadMode<'_, D> {
193        let readopts = ReadOptions::default();
194        self.raw_iterator_opt(readopts)
195    }
196
197    /// Creates a raw iterator over the data in this snapshot under the given column family, using
198    /// the default read options.
199    pub fn raw_iterator_cf(
200        &'_ self,
201        cf_handle: &impl AsColumnFamilyRef,
202    ) -> DBRawIteratorWithThreadMode<'_, D> {
203        let readopts = ReadOptions::default();
204        self.raw_iterator_cf_opt(cf_handle, readopts)
205    }
206
207    /// Creates a raw iterator over the data in this snapshot, using the given read options.
208    pub fn raw_iterator_opt(
209        &'_ self,
210        mut readopts: ReadOptions,
211    ) -> DBRawIteratorWithThreadMode<'_, D> {
212        readopts.set_snapshot(self);
213        DBRawIteratorWithThreadMode::new(self.db, readopts)
214    }
215
216    /// Creates a raw iterator over the data in this snapshot under the given column family, using
217    /// the given read options.
218    pub fn raw_iterator_cf_opt(
219        &'_ self,
220        cf_handle: &impl AsColumnFamilyRef,
221        mut readopts: ReadOptions,
222    ) -> DBRawIteratorWithThreadMode<'_, D> {
223        readopts.set_snapshot(self);
224        DBRawIteratorWithThreadMode::new_cf(self.db, cf_handle.inner(), readopts)
225    }
226
227    /// Returns the bytes associated with a key value with default read options.
228    pub fn get<K: AsRef<[u8]>>(&self, key: K) -> Result<Option<Vec<u8>>, Error> {
229        self.read_options().get(key)
230    }
231
232    /// Returns the bytes associated with a key value and given column family with default read
233    /// options.
234    pub fn get_cf<K: AsRef<[u8]>>(
235        &self,
236        cf: &impl AsColumnFamilyRef,
237        key: K,
238    ) -> Result<Option<Vec<u8>>, Error> {
239        self.read_options().get_cf(cf, key)
240    }
241
242    /// Returns the bytes associated with a key value and given read options.
243    pub fn get_opt<K: AsRef<[u8]>>(
244        &self,
245        key: K,
246        readopts: ReadOptions,
247    ) -> Result<Option<Vec<u8>>, Error> {
248        self.read_options_opt(readopts).get(key)
249    }
250
251    /// Returns the bytes associated with a key value, given column family and read options.
252    pub fn get_cf_opt<K: AsRef<[u8]>>(
253        &self,
254        cf: &impl AsColumnFamilyRef,
255        key: K,
256        readopts: ReadOptions,
257    ) -> Result<Option<Vec<u8>>, Error> {
258        self.read_options_opt(readopts).get_cf(cf, key)
259    }
260
261    /// Return the value associated with a key using RocksDB's PinnableSlice
262    /// so as to avoid unnecessary memory copy. Similar to get_pinned_opt but
263    /// leverages default options.
264    pub fn get_pinned<K: AsRef<[u8]>>(
265        &'_ self,
266        key: K,
267    ) -> Result<Option<DBPinnableSlice<'_>>, Error> {
268        self.read_options().get_pinned(key)
269    }
270
271    /// Return the value associated with a key using RocksDB's PinnableSlice
272    /// so as to avoid unnecessary memory copy. Similar to get_pinned_cf_opt but
273    /// leverages default options.
274    pub fn get_pinned_cf<K: AsRef<[u8]>>(
275        &'_ self,
276        cf: &impl AsColumnFamilyRef,
277        key: K,
278    ) -> Result<Option<DBPinnableSlice<'_>>, Error> {
279        self.read_options().get_pinned_cf(cf, key)
280    }
281
282    /// Return the value associated with a key using RocksDB's PinnableSlice
283    /// so as to avoid unnecessary memory copy.
284    pub fn get_pinned_opt<K: AsRef<[u8]>>(
285        &'_ self,
286        key: K,
287        readopts: ReadOptions,
288    ) -> Result<Option<DBPinnableSlice<'_>>, Error> {
289        self.read_options_opt(readopts).get_pinned(key)
290    }
291
292    /// Return the value associated with a key using RocksDB's PinnableSlice
293    /// so as to avoid unnecessary memory copy. Similar to get_pinned_opt but
294    /// allows specifying ColumnFamily.
295    pub fn get_pinned_cf_opt<K: AsRef<[u8]>>(
296        &'_ self,
297        cf: &impl AsColumnFamilyRef,
298        key: K,
299        readopts: ReadOptions,
300    ) -> Result<Option<DBPinnableSlice<'_>>, Error> {
301        self.read_options_opt(readopts).get_pinned_cf(cf, key)
302    }
303
304    /// Returns the bytes associated with the given key values and default read options.
305    pub fn multi_get<K: AsRef<[u8]>, I>(&self, keys: I) -> Vec<Result<Option<Vec<u8>>, Error>>
306    where
307        I: IntoIterator<Item = K>,
308    {
309        self.read_options().multi_get(keys)
310    }
311
312    /// Returns the bytes associated with the given key values and default read options.
313    pub fn multi_get_cf<'b, K, I, W>(&self, keys_cf: I) -> Vec<Result<Option<Vec<u8>>, Error>>
314    where
315        K: AsRef<[u8]>,
316        I: IntoIterator<Item = (&'b W, K)>,
317        W: AsColumnFamilyRef + 'b,
318    {
319        self.read_options().multi_get_cf(keys_cf)
320    }
321
322    /// Returns the bytes associated with the given key values and given read options.
323    pub fn multi_get_opt<K, I>(
324        &self,
325        keys: I,
326        readopts: ReadOptions,
327    ) -> Vec<Result<Option<Vec<u8>>, Error>>
328    where
329        K: AsRef<[u8]>,
330        I: IntoIterator<Item = K>,
331    {
332        self.read_options_opt(readopts).multi_get(keys)
333    }
334
335    /// Returns the bytes associated with the given key values, given column family and read options.
336    pub fn multi_get_cf_opt<'b, K, I, W>(
337        &self,
338        keys_cf: I,
339        readopts: ReadOptions,
340    ) -> Vec<Result<Option<Vec<u8>>, Error>>
341    where
342        K: AsRef<[u8]>,
343        I: IntoIterator<Item = (&'b W, K)>,
344        W: AsColumnFamilyRef + 'b,
345    {
346        self.read_options_opt(readopts).multi_get_cf(keys_cf)
347    }
348}
349
350impl<'db, D: DBAccess> SnapshotReadOptions<'_, 'db, D> {
351    /// Returns the bytes associated with a key.
352    pub fn get<K: AsRef<[u8]>>(&self, key: K) -> Result<Option<Vec<u8>>, Error> {
353        self.snapshot.db.get_opt(key, &self.readopts)
354    }
355
356    /// Returns the bytes associated with a key in a column family.
357    pub fn get_cf<K: AsRef<[u8]>>(
358        &self,
359        cf: &impl AsColumnFamilyRef,
360        key: K,
361    ) -> Result<Option<Vec<u8>>, Error> {
362        self.snapshot.db.get_cf_opt(cf, key, &self.readopts)
363    }
364
365    /// Returns a pinned value associated with a key.
366    pub fn get_pinned<K: AsRef<[u8]>>(
367        &self,
368        key: K,
369    ) -> Result<Option<DBPinnableSlice<'db>>, Error> {
370        let db: &'db D = self.snapshot.db;
371        db.get_pinned_opt(key, &self.readopts)
372    }
373
374    /// Returns a pinned value associated with a key in a column family.
375    pub fn get_pinned_cf<K: AsRef<[u8]>>(
376        &self,
377        cf: &impl AsColumnFamilyRef,
378        key: K,
379    ) -> Result<Option<DBPinnableSlice<'db>>, Error> {
380        let db: &'db D = self.snapshot.db;
381        db.get_pinned_cf_opt(cf, key, &self.readopts)
382    }
383
384    /// Returns the values associated with the given keys.
385    pub fn multi_get<K, I>(&self, keys: I) -> Vec<Result<Option<Vec<u8>>, Error>>
386    where
387        K: AsRef<[u8]>,
388        I: IntoIterator<Item = K>,
389    {
390        self.snapshot.db.multi_get_opt(keys, &self.readopts)
391    }
392
393    /// Returns the values associated with the given keys and column families.
394    pub fn multi_get_cf<'b, K, I, W>(&self, keys_cf: I) -> Vec<Result<Option<Vec<u8>>, Error>>
395    where
396        K: AsRef<[u8]>,
397        I: IntoIterator<Item = (&'b W, K)>,
398        W: AsColumnFamilyRef + 'b,
399    {
400        self.snapshot.db.multi_get_cf_opt(keys_cf, &self.readopts)
401    }
402}
403
404impl<D: DBAccess> Drop for SnapshotWithThreadMode<'_, D> {
405    fn drop(&mut self) {
406        unsafe {
407            self.db.release_snapshot(self.inner);
408        }
409    }
410}
411
412/// `Send` and `Sync` implementations for `SnapshotWithThreadMode` are safe, because `SnapshotWithThreadMode` is
413/// immutable and can be safely shared between threads.
414unsafe impl<D: DBAccess + Sync> Send for SnapshotWithThreadMode<'_, D> {}
415unsafe impl<D: DBAccess + Sync> Sync for SnapshotWithThreadMode<'_, D> {}