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}