Skip to main content

rust_rocksdb/
checkpoint.rs

1// Copyright 2018 Eugene P.
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//! Implementation of bindings to RocksDB Checkpoint[1] API
17//!
18//! [1]: https://github.com/facebook/rocksdb/wiki/Checkpoints
19
20use crate::db::{DBInner, ExportImportFilesMetaData};
21use crate::{
22    AsColumnFamilyRef, DBCommon, Error, OptimisticTransactionDB, ThreadMode, TransactionDB, ffi,
23    ffi_util::to_cpath,
24};
25use std::{marker::PhantomData, path::Path};
26
27/// Default value for the `log_size_for_flush` parameter passed to
28/// `ffi::rocksdb_checkpoint_create`.
29///
30/// A value of `0` forces RocksDB to flush memtables as needed before creating
31/// the checkpoint. This helps ensure the checkpoint includes the most recent
32/// writes (which may still be in memtables at the time of checkpoint creation).
33///
34/// Forcing a flush can create new SST file(s), potentially very small L0 SSTs
35/// if little data has been written since the last flush.
36const DEFAULT_LOG_SIZE_FOR_FLUSH: u64 = 0_u64;
37
38/// Database's checkpoint object.
39/// Used to create checkpoints of the specified DB from time to time.
40///
41/// A `Checkpoint` must not outlive the `DB` it was created from:
42///
43/// ```compile_fail,E0597
44/// use rust_rocksdb::{checkpoint::Checkpoint, DB};
45///
46/// let _checkpoint = {
47///     let db = DB::open_default("foo").unwrap();
48///     Checkpoint::new(&db)
49/// };
50/// ```
51pub struct Checkpoint<'db> {
52    inner: *mut ffi::rocksdb_checkpoint_t,
53    _db: PhantomData<&'db ()>,
54}
55
56impl<'db> Checkpoint<'db> {
57    /// Creates new checkpoint object for specific DB.
58    ///
59    /// Does not actually produce checkpoints, call `.create_checkpoint()` method to produce
60    /// a DB checkpoint.
61    pub fn new<T: ThreadMode, I: DBInner>(db: &'db DBCommon<T, I>) -> Result<Self, Error> {
62        let checkpoint: *mut ffi::rocksdb_checkpoint_t;
63
64        unsafe {
65            checkpoint = ffi_try!(ffi::rocksdb_checkpoint_object_create(db.inner.inner()));
66        }
67
68        if checkpoint.is_null() {
69            return Err(Error::new("Could not create checkpoint object.".to_owned()));
70        }
71
72        Ok(Self {
73            inner: checkpoint,
74            _db: PhantomData,
75        })
76    }
77
78    /// Creates a new physical RocksDB checkpoint in the directory specified by `path`.
79    ///
80    /// A checkpoint is a consistent, read-only view of the database at a specific
81    /// point in time. Internally, RocksDB creates a new MANIFEST and metadata files
82    /// and hard-links the relevant SST files, making the checkpoint efficient to
83    /// create and safe to keep for long-lived reads.
84    ///
85    /// This method uses the default `log_size_for_flush` value (`0`), which instructs
86    /// RocksDB to flush memtables as needed before creating the checkpoint. Forcing
87    /// a flush ensures that the checkpoint includes the most recent writes that may
88    /// still reside in memtables at the time of checkpoint creation.
89    ///
90    /// Forcing a flush may create new SST file(s), including very small L0 SSTs if
91    /// little data has been written since the last flush. Applications that create
92    /// checkpoints frequently or during periods of low write volume may wish to
93    /// control this behavior by using an API that allows specifying
94    /// `log_size_for_flush`.
95    ///
96    /// Note:
97    /// - Checkpoints are always SST-based and never depend on WAL files or live
98    ///   memtables when opened.
99    /// - If writes are performed with WAL disabled, forcing a flush is required to
100    ///   ensure those writes appear in the checkpoint.
101    /// - When using RocksDB TransactionDB with two-phase commit (2PC), RocksDB will
102    ///   always flush regardless of the `log_size_for_flush` setting.
103    pub fn create_checkpoint<P: AsRef<Path>>(&self, path: P) -> Result<(), Error> {
104        let c_path = to_cpath(path)?;
105        unsafe {
106            ffi_try!(ffi::rocksdb_checkpoint_create(
107                self.inner,
108                c_path.as_ptr(),
109                DEFAULT_LOG_SIZE_FOR_FLUSH,
110            ));
111        }
112        Ok(())
113    }
114
115    /// Creates a new physical DB checkpoint in `path`, allowing the caller to
116    /// control `log_size_for_flush`.
117    ///
118    /// `log_size_for_flush` is forwarded to RocksDB's Checkpoint API:
119    /// - `0` forces a flush as needed before checkpoint creation, which helps the
120    ///   checkpoint include the latest writes; this may create new SST file(s).
121    /// - A non-zero value:
122    ///   - **Expected behavior** (once RocksDB bug is fixed): Only forces a flush
123    ///     if the total WAL size exceeds the specified threshold. When a flush is
124    ///     not forced and WAL writing is enabled, RocksDB includes WAL files in
125    ///     the checkpoint that are replayed on open to reconstruct recent writes.
126    ///     This avoids creating small SST files during periods of low write volume,
127    ///     at the cost of additional checkpoint storage space for the copied WAL.
128    ///   - **Current behavior** (RocksDB bug): Never flushes, regardless of WAL
129    ///     size. The checkpoint will always include WAL files instead of flushing
130    ///     to SST. See: <https://github.com/facebook/rocksdb/pull/14193>
131    ///
132    /// In practice, using a non-zero value means checkpoints may represent an
133    /// *older, fully materialized database state* rather than the instantaneous
134    /// state at the time the checkpoint is created.
135    ///
136    /// Note:
137    /// - If writes are performed with WAL disabled, using a non-zero
138    ///   `log_size_for_flush` may cause those writes to be absent from
139    ///   the checkpoint.
140    /// - When using RocksDB TransactionDB with two-phase commit (2PC),
141    ///   RocksDB will always flush regardless of `log_size_for_flush`.
142    pub fn create_checkpoint_with_log_size<P: AsRef<Path>>(
143        &self,
144        path: P,
145        log_size_for_flush: u64,
146    ) -> Result<(), Error> {
147        let c_path = to_cpath(path)?;
148        unsafe {
149            ffi_try!(ffi::rocksdb_checkpoint_create(
150                self.inner,
151                c_path.as_ptr(),
152                log_size_for_flush,
153            ));
154        }
155        Ok(())
156    }
157
158    /// Export a specified Column Family
159    ///
160    /// Creates copies of the live SST files at the specified export path.
161    ///
162    /// - SST files will be created as hard links when the directory specified
163    ///   is in the same partition as the db directory, copied otherwise.
164    /// - the path must not yet exist - a new directory will be created as part of the export.
165    /// - Always triggers a flush.
166    ///
167    /// # Examples
168    ///
169    /// ```rust
170    /// use rust_rocksdb::{DB, checkpoint::Checkpoint};
171    ///
172    /// fn export_column_family(db: &DB, column_family_name: &str, export_path: &str) {
173    ///    let cp = Checkpoint::new(&db).unwrap();
174    ///    let cf = db.cf_handle(column_family_name).unwrap();
175    ///
176    ///    let export_metadata = cp.export_column_family(&cf, export_path).unwrap();
177    ///
178    ///    assert!(export_metadata.get_files().len() > 0);
179    /// }
180    /// ```
181    ///
182    /// See also: [`DB::create_column_family_with_import`](crate::DB::create_column_family_with_import).
183    pub fn export_column_family<P: AsRef<Path>>(
184        &self,
185        column_family: &impl AsColumnFamilyRef,
186        path: P,
187    ) -> Result<ExportImportFilesMetaData, Error> {
188        let c_path = to_cpath(path)?;
189        let column_family_handle = column_family.inner();
190        let metadata = unsafe {
191            ffi_try!(ffi::rocksdb_checkpoint_export_column_family(
192                self.inner,
193                column_family_handle,
194                c_path.as_ptr(),
195            ))
196        };
197        Ok(ExportImportFilesMetaData { inner: metadata })
198    }
199}
200
201impl Drop for Checkpoint<'_> {
202    fn drop(&mut self) {
203        unsafe {
204            ffi::rocksdb_checkpoint_object_destroy(self.inner);
205        }
206    }
207}
208
209/// TransactionDB's checkpoint object.
210/// Used to create checkpoints of the specified TransactionDB from time to time.
211///
212/// A `TransactionDBCheckpoint` must not outlive the `TransactionDB` it was created from:
213///
214/// ```compile_fail,E0597
215/// use rust_rocksdb::{checkpoint::TransactionDBCheckpoint, SingleThreaded, TransactionDB};
216///
217/// let _checkpoint = {
218///     let db = TransactionDB::<SingleThreaded>::open_default("foo").unwrap();
219///     TransactionDBCheckpoint::new(&db)
220/// };
221/// ```
222///
223/// RocksDB TransactionDB checkpoints are expected to flush memtables regardless
224/// of the `log_size_for_flush` setting.
225pub struct TransactionDBCheckpoint<'db> {
226    inner: *mut ffi::rocksdb_checkpoint_t,
227    _db: PhantomData<&'db ()>,
228}
229
230impl<'db> TransactionDBCheckpoint<'db> {
231    /// Creates new checkpoint object for a specific TransactionDB.
232    ///
233    /// Does not actually produce checkpoints, call `.create_checkpoint()` to produce
234    /// a TransactionDB checkpoint.
235    pub fn new<T: ThreadMode>(db: &'db TransactionDB<T>) -> Result<Self, Error> {
236        let checkpoint: *mut ffi::rocksdb_checkpoint_t;
237
238        unsafe {
239            checkpoint = ffi_try!(ffi::rocksdb_transactiondb_checkpoint_object_create(
240                db.inner
241            ));
242        }
243
244        if checkpoint.is_null() {
245            return Err(Error::new("Could not create checkpoint object.".to_owned()));
246        }
247
248        Ok(Self {
249            inner: checkpoint,
250            _db: PhantomData,
251        })
252    }
253
254    /// Creates a new physical RocksDB checkpoint in the directory specified by `path`.
255    ///
256    /// This method uses the default `log_size_for_flush` value (`0`), which instructs
257    /// RocksDB to flush memtables as needed before creating the checkpoint.
258    pub fn create_checkpoint<P: AsRef<Path>>(&self, path: P) -> Result<(), Error> {
259        self.create_checkpoint_with_log_size(path, DEFAULT_LOG_SIZE_FOR_FLUSH)
260    }
261
262    /// Creates a new physical RocksDB checkpoint in `path`, allowing the caller to
263    /// control `log_size_for_flush`.
264    ///
265    /// The value is forwarded to RocksDB's Checkpoint API. TransactionDB
266    /// checkpoints may still flush memtables regardless of this threshold.
267    pub fn create_checkpoint_with_log_size<P: AsRef<Path>>(
268        &self,
269        path: P,
270        log_size_for_flush: u64,
271    ) -> Result<(), Error> {
272        let c_path = to_cpath(path)?;
273        unsafe {
274            ffi_try!(ffi::rocksdb_checkpoint_create(
275                self.inner,
276                c_path.as_ptr(),
277                log_size_for_flush,
278            ));
279        }
280        Ok(())
281    }
282
283    /// Export a specified Column Family.
284    ///
285    /// Creates copies of the live SST files at the specified export path.
286    ///
287    /// - SST files will be created as hard links when the directory specified
288    ///   is in the same partition as the db directory, copied otherwise.
289    /// - the path must not yet exist - a new directory will be created as part of the export.
290    /// - Always triggers a flush.
291    ///
292    /// See also: [`DB::create_column_family_with_import`](crate::DB::create_column_family_with_import).
293    pub fn export_column_family<P: AsRef<Path>>(
294        &self,
295        column_family: &impl AsColumnFamilyRef,
296        path: P,
297    ) -> Result<ExportImportFilesMetaData, Error> {
298        let c_path = to_cpath(path)?;
299        let column_family_handle = column_family.inner();
300        let metadata = unsafe {
301            ffi_try!(ffi::rocksdb_checkpoint_export_column_family(
302                self.inner,
303                column_family_handle,
304                c_path.as_ptr(),
305            ))
306        };
307        Ok(ExportImportFilesMetaData { inner: metadata })
308    }
309}
310
311impl Drop for TransactionDBCheckpoint<'_> {
312    fn drop(&mut self) {
313        unsafe {
314            ffi::rocksdb_checkpoint_object_destroy(self.inner);
315        }
316    }
317}
318
319/// `OptimisticTransactionDB`'s checkpoint object.
320/// Used to create checkpoints of the specified `OptimisticTransactionDB` from
321/// time to time.
322///
323/// An `OptimisticTransactionDBCheckpoint` must not outlive the
324/// `OptimisticTransactionDB` it was created from:
325///
326/// ```compile_fail,E0597
327/// use rust_rocksdb::{
328///     checkpoint::OptimisticTransactionDBCheckpoint, OptimisticTransactionDB, SingleThreaded,
329/// };
330///
331/// let _checkpoint = {
332///     let db = OptimisticTransactionDB::<SingleThreaded>::open_default("foo").unwrap();
333///     OptimisticTransactionDBCheckpoint::new(&db)
334/// };
335/// ```
336pub struct OptimisticTransactionDBCheckpoint<'db> {
337    inner: *mut ffi::rocksdb_checkpoint_t,
338    _db: PhantomData<&'db ()>,
339}
340
341impl<'db> OptimisticTransactionDBCheckpoint<'db> {
342    /// Creates new checkpoint object for a specific `OptimisticTransactionDB`.
343    ///
344    /// Does not actually produce checkpoints, call `.create_checkpoint()` to
345    /// produce one.
346    pub fn new<T: ThreadMode>(db: &'db OptimisticTransactionDB<T>) -> Result<Self, Error> {
347        let checkpoint: *mut ffi::rocksdb_checkpoint_t;
348
349        unsafe {
350            checkpoint = ffi_try!(
351                ffi::rocksdb_optimistictransactiondb_checkpoint_object_create(db.inner.db)
352            );
353        }
354
355        if checkpoint.is_null() {
356            return Err(Error::new("Could not create checkpoint object.".to_owned()));
357        }
358
359        Ok(Self {
360            inner: checkpoint,
361            _db: PhantomData,
362        })
363    }
364
365    /// Creates a new physical RocksDB checkpoint in the directory specified by `path`.
366    ///
367    /// This method uses the default `log_size_for_flush` value (`0`), which instructs
368    /// RocksDB to flush memtables as needed before creating the checkpoint.
369    pub fn create_checkpoint<P: AsRef<Path>>(&self, path: P) -> Result<(), Error> {
370        self.create_checkpoint_with_log_size(path, DEFAULT_LOG_SIZE_FOR_FLUSH)
371    }
372
373    /// Creates a new physical RocksDB checkpoint in `path`, allowing the caller to
374    /// control `log_size_for_flush`.
375    pub fn create_checkpoint_with_log_size<P: AsRef<Path>>(
376        &self,
377        path: P,
378        log_size_for_flush: u64,
379    ) -> Result<(), Error> {
380        let c_path = to_cpath(path)?;
381        unsafe {
382            ffi_try!(ffi::rocksdb_checkpoint_create(
383                self.inner,
384                c_path.as_ptr(),
385                log_size_for_flush,
386            ));
387        }
388        Ok(())
389    }
390
391    /// Export a specified Column Family.
392    ///
393    /// Creates copies of the live SST files at the specified export path.
394    ///
395    /// - SST files will be created as hard links when the directory specified
396    ///   is in the same partition as the db directory, copied otherwise.
397    /// - the path must not yet exist, a new directory is created as part of the export.
398    /// - Always triggers a flush.
399    ///
400    /// See also: [`DB::create_column_family_with_import`](crate::DB::create_column_family_with_import).
401    pub fn export_column_family<P: AsRef<Path>>(
402        &self,
403        column_family: &impl AsColumnFamilyRef,
404        path: P,
405    ) -> Result<ExportImportFilesMetaData, Error> {
406        let c_path = to_cpath(path)?;
407        let column_family_handle = column_family.inner();
408        let metadata = unsafe {
409            ffi_try!(ffi::rocksdb_checkpoint_export_column_family(
410                self.inner,
411                column_family_handle,
412                c_path.as_ptr(),
413            ))
414        };
415        Ok(ExportImportFilesMetaData { inner: metadata })
416    }
417}
418
419impl Drop for OptimisticTransactionDBCheckpoint<'_> {
420    fn drop(&mut self) {
421        unsafe {
422            ffi::rocksdb_checkpoint_object_destroy(self.inner);
423        }
424    }
425}