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> {}