Skip to main content

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