158 lines
4.8 KiB
Rust
158 lines
4.8 KiB
Rust
//! This module implements the proxmox backup chunked data storage
|
|
//!
|
|
//! A chunk is simply defined as binary blob. We store them inside a
|
|
//! `ChunkStore`, addressed by the SHA256 digest of the binary
|
|
//! blob. This technology is also known as content-addressable
|
|
//! storage.
|
|
//!
|
|
//! We store larger files by splitting them into chunks. The resulting
|
|
//! SHA256 digest list is stored as separate index file. The
|
|
//! `DynamicIndex*` format is able to deal with dynamic chunk sizes,
|
|
//! whereas the `FixedIndex*` format is an optimization to store a
|
|
//! list of equal sized chunks.
|
|
//!
|
|
//! # ChunkStore Locking
|
|
//!
|
|
//! We need to be able to restart the proxmox-backup service daemons,
|
|
//! so that we can update the software without rebooting the host. But
|
|
//! such restarts must not abort running backup jobs, so we need to
|
|
//! keep the old service running until those jobs are finished. This
|
|
//! implies that we need some kind of locking for the
|
|
//! ChunkStore. Please note that it is perfectly valid to have
|
|
//! multiple parallel ChunkStore writers, even when they write the
|
|
//! same chunk (because the chunk would have the same name and the
|
|
//! same data). The only real problem is garbage collection, because
|
|
//! we need to avoid deleting chunks which are still referenced.
|
|
//!
|
|
//! * Read Index Files:
|
|
//!
|
|
//! Acquire shared lock for .idx files.
|
|
//!
|
|
//!
|
|
//! * Delete Index Files:
|
|
//!
|
|
//! Acquire exclusive lock for .idx files. This makes sure that we do
|
|
//! not delete index files while they are still in use.
|
|
//!
|
|
//!
|
|
//! * Create Index Files:
|
|
//!
|
|
//! Acquire shared lock for ChunkStore (process wide).
|
|
//!
|
|
//! Note: When creating .idx files, we create temporary (.tmp) file,
|
|
//! then do an atomic rename ...
|
|
//!
|
|
//!
|
|
//! * Garbage Collect:
|
|
//!
|
|
//! Acquire exclusive lock for ChunkStore (process wide). If we have
|
|
//! already an shared lock for ChunkStore, try to updraged that
|
|
//! lock.
|
|
//!
|
|
//!
|
|
//! * Server Restart
|
|
//!
|
|
//! Try to abort running garbage collection to release exclusive
|
|
//! ChunkStore lock asap. Start new service with existing listening
|
|
//! socket.
|
|
//!
|
|
//!
|
|
//! # Garbage Collection (GC)
|
|
//!
|
|
//! Deleting backups is as easy as deleting the corresponding .idx
|
|
//! files. Unfortunately, this does not free up any storage, because
|
|
//! those files just contains references to chunks.
|
|
//!
|
|
//! To free up some storage, we run a garbage collection process at
|
|
//! regular intervals. The collector uses an mark and sweep
|
|
//! approach. In the first phase, it scans all .idx files to mark used
|
|
//! chunks. The second phase then removes all unmarked chunks from the
|
|
//! store.
|
|
//!
|
|
//! The above locking mechanism makes sure that we are the only
|
|
//! process running GC. But we still want to be able to create backups
|
|
//! during GC, so there may be multiple backup threads/tasks
|
|
//! running. Either started before GC started, or started while GC is
|
|
//! running.
|
|
//!
|
|
//! ## `atime` based GC
|
|
//!
|
|
//! The idea here is to mark chunks by updating the `atime` (access
|
|
//! timestamp) on the chunk file. This is quite simple and does not
|
|
//! need additional RAM.
|
|
//!
|
|
//! One minor problem is that recent Linux versions use the `relatime`
|
|
//! mount flag by default for performance reasons (yes, we want
|
|
//! that). When enabled, `atime` data is written to the disk only if
|
|
//! the file has been modified since the `atime` data was last updated
|
|
//! (`mtime`), or if the file was last accessed more than a certain
|
|
//! amount of time ago (by default 24h). So we may only delete chunks
|
|
//! with `atime` older than 24 hours.
|
|
//!
|
|
//! Another problem arise from running backups. The mark phase does
|
|
//! not find any chunks from those backups, because there is no .idx
|
|
//! file for them (created after the backup). Chunks created or
|
|
//! touched by those backups may have an `atime` as old as the start
|
|
//! time of those backup. Please not that the backup start time may
|
|
//! predate the GC start time. Se we may only delete chunk older than
|
|
//! the start time of those running backup jobs.
|
|
//!
|
|
//!
|
|
//! ## Store `marks` in RAM using a HASH
|
|
//!
|
|
//! Not sure if this is better. TODO
|
|
|
|
#[macro_export]
|
|
macro_rules! PROXMOX_BACKUP_PROTOCOL_ID_V1 {
|
|
() => { "proxmox-backup-protocol-v1" }
|
|
}
|
|
|
|
#[macro_export]
|
|
macro_rules! PROXMOX_BACKUP_READER_PROTOCOL_ID_V1 {
|
|
() => { "proxmox-backup-reader-protocol-v1" }
|
|
}
|
|
|
|
mod file_formats;
|
|
pub use file_formats::*;
|
|
|
|
mod crypt_config;
|
|
pub use crypt_config::*;
|
|
|
|
mod key_derivation;
|
|
pub use key_derivation::*;
|
|
|
|
mod data_chunk;
|
|
pub use data_chunk::*;
|
|
|
|
mod data_blob;
|
|
pub use data_blob::*;
|
|
|
|
mod chunk_stream;
|
|
pub use chunk_stream::*;
|
|
|
|
mod chunk_stat;
|
|
pub use chunk_stat::*;
|
|
|
|
pub use proxmox_protocol::Chunker;
|
|
|
|
mod read_chunk;
|
|
pub use read_chunk::*;
|
|
|
|
mod chunk_store;
|
|
pub use chunk_store::*;
|
|
|
|
mod index;
|
|
pub use index::*;
|
|
|
|
mod fixed_index;
|
|
pub use fixed_index::*;
|
|
|
|
mod dynamic_index;
|
|
pub use dynamic_index::*;
|
|
|
|
mod backup_info;
|
|
pub use backup_info::*;
|
|
|
|
mod datastore;
|
|
pub use datastore::*;
|