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::{AsColumnFamilyRef, DBCommon, Error, ThreadMode, ffi, ffi_util::to_cpath};
22use std::{marker::PhantomData, path::Path};
23
24/// Default value for the `log_size_for_flush` parameter passed to
25/// `ffi::rocksdb_checkpoint_create`.
26///
27/// A value of `0` forces RocksDB to flush memtables as needed before creating
28/// the checkpoint. This helps ensure the checkpoint includes the most recent
29/// writes (which may still be in memtables at the time of checkpoint creation).
30///
31/// Forcing a flush can create new SST file(s), potentially very small L0 SSTs
32/// if little data has been written since the last flush.
33const DEFAULT_LOG_SIZE_FOR_FLUSH: u64 = 0_u64;
34
35/// Database's checkpoint object.
36/// Used to create checkpoints of the specified DB from time to time.
37pub struct Checkpoint<'db> {
38    inner: *mut ffi::rocksdb_checkpoint_t,
39    _db: PhantomData<&'db ()>,
40}
41
42impl<'db> Checkpoint<'db> {
43    /// Creates new checkpoint object for specific DB.
44    ///
45    /// Does not actually produce checkpoints, call `.create_checkpoint()` method to produce
46    /// a DB checkpoint.
47    pub fn new<T: ThreadMode, I: DBInner>(db: &'db DBCommon<T, I>) -> Result<Self, Error> {
48        let checkpoint: *mut ffi::rocksdb_checkpoint_t;
49
50        unsafe {
51            checkpoint = ffi_try!(ffi::rocksdb_checkpoint_object_create(db.inner.inner()));
52        }
53
54        if checkpoint.is_null() {
55            return Err(Error::new("Could not create checkpoint object.".to_owned()));
56        }
57
58        Ok(Self {
59            inner: checkpoint,
60            _db: PhantomData,
61        })
62    }
63
64    /// Creates a new physical RocksDB checkpoint in the directory specified by `path`.
65    ///
66    /// A checkpoint is a consistent, read-only view of the database at a specific
67    /// point in time. Internally, RocksDB creates a new MANIFEST and metadata files
68    /// and hard-links the relevant SST files, making the checkpoint efficient to
69    /// create and safe to keep for long-lived reads.
70    ///
71    /// This method uses the default `log_size_for_flush` value (`0`), which instructs
72    /// RocksDB to flush memtables as needed before creating the checkpoint. Forcing
73    /// a flush ensures that the checkpoint includes the most recent writes that may
74    /// still reside in memtables at the time of checkpoint creation.
75    ///
76    /// Forcing a flush may create new SST file(s), including very small L0 SSTs if
77    /// little data has been written since the last flush. Applications that create
78    /// checkpoints frequently or during periods of low write volume may wish to
79    /// control this behavior by using an API that allows specifying
80    /// `log_size_for_flush`.
81    ///
82    /// Note:
83    /// - Checkpoints are always SST-based and never depend on WAL files or live
84    ///   memtables when opened.
85    /// - If writes are performed with WAL disabled, forcing a flush is required to
86    ///   ensure those writes appear in the checkpoint.
87    /// - When using RocksDB TransactionDB with two-phase commit (2PC), RocksDB will
88    ///   always flush regardless of the `log_size_for_flush` setting.
89    pub fn create_checkpoint<P: AsRef<Path>>(&self, path: P) -> Result<(), Error> {
90        let c_path = to_cpath(path)?;
91        unsafe {
92            ffi_try!(ffi::rocksdb_checkpoint_create(
93                self.inner,
94                c_path.as_ptr(),
95                DEFAULT_LOG_SIZE_FOR_FLUSH,
96            ));
97        }
98        Ok(())
99    }
100
101    /// Creates a new physical DB checkpoint in `path`, allowing the caller to
102    /// control `log_size_for_flush`.
103    ///
104    /// `log_size_for_flush` is forwarded to RocksDB's Checkpoint API:
105    /// - `0` forces a flush as needed before checkpoint creation, which helps the
106    ///   checkpoint include the latest writes; this may create new SST file(s).
107    /// - A non-zero value:
108    ///   - **Expected behavior** (once RocksDB bug is fixed): Only forces a flush
109    ///     if the total WAL size exceeds the specified threshold. When a flush is
110    ///     not forced and WAL writing is enabled, RocksDB includes WAL files in
111    ///     the checkpoint that are replayed on open to reconstruct recent writes.
112    ///     This avoids creating small SST files during periods of low write volume,
113    ///     at the cost of additional checkpoint storage space for the copied WAL.
114    ///   - **Current behavior** (RocksDB bug): Never flushes, regardless of WAL
115    ///     size. The checkpoint will always include WAL files instead of flushing
116    ///     to SST. See: <https://github.com/facebook/rocksdb/pull/14193>
117    ///
118    /// In practice, using a non-zero value means checkpoints may represent an
119    /// *older, fully materialized database state* rather than the instantaneous
120    /// state at the time the checkpoint is created.
121    ///
122    /// Note:
123    /// - If writes are performed with WAL disabled, using a non-zero
124    ///   `log_size_for_flush` may cause those writes to be absent from
125    ///   the checkpoint.
126    /// - When using RocksDB TransactionDB with two-phase commit (2PC),
127    ///   RocksDB will always flush regardless of `log_size_for_flush`.
128    pub fn create_checkpoint_with_log_size<P: AsRef<Path>>(
129        &self,
130        path: P,
131        log_size_for_flush: u64,
132    ) -> Result<(), Error> {
133        let c_path = to_cpath(path)?;
134        unsafe {
135            ffi_try!(ffi::rocksdb_checkpoint_create(
136                self.inner,
137                c_path.as_ptr(),
138                log_size_for_flush,
139            ));
140        }
141        Ok(())
142    }
143
144    /// Export a specified Column Family
145    ///
146    /// Creates copies of the live SST files at the specified export path.
147    ///
148    /// - SST files will be created as hard links when the directory specified
149    ///   is in the same partition as the db directory, copied otherwise.
150    /// - the path must not yet exist - a new directory will be created as part of the export.
151    /// - Always triggers a flush.
152    ///
153    /// # Examples
154    ///
155    /// ```rust
156    /// use rust_rocksdb::{DB, checkpoint::Checkpoint};
157    ///
158    /// fn export_column_family(db: &DB, column_family_name: &str, export_path: &str) {
159    ///    let cp = Checkpoint::new(&db).unwrap();
160    ///    let cf = db.cf_handle(column_family_name).unwrap();
161    ///
162    ///    let export_metadata = cp.export_column_family(&cf, export_path).unwrap();
163    ///
164    ///    assert!(export_metadata.get_files().len() > 0);
165    /// }
166    /// ```
167    ///
168    /// See also: [`DB::create_column_family_with_import`](crate::DB::create_column_family_with_import).
169    pub fn export_column_family<P: AsRef<Path>>(
170        &self,
171        column_family: &impl AsColumnFamilyRef,
172        path: P,
173    ) -> Result<ExportImportFilesMetaData, Error> {
174        let c_path = to_cpath(path)?;
175        let column_family_handle = column_family.inner();
176        let metadata = unsafe {
177            ffi_try!(ffi::rocksdb_checkpoint_export_column_family(
178                self.inner,
179                column_family_handle,
180                c_path.as_ptr(),
181            ))
182        };
183        Ok(ExportImportFilesMetaData { inner: metadata })
184    }
185}
186
187impl Drop for Checkpoint<'_> {
188    fn drop(&mut self) {
189        unsafe {
190            ffi::rocksdb_checkpoint_object_destroy(self.inner);
191        }
192    }
193}