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}