Skip to main content

nodedb_cluster/mirror/bootstrap/
envelope.rs

1// SPDX-License-Identifier: BUSL-1.1
2
3//! Wire envelope and result types for cross-cluster snapshot transfer.
4
5use std::path::PathBuf;
6use std::sync::Arc;
7
8use serde::{Deserialize, Serialize};
9
10use nodedb_types::{Lsn, MirrorStatus};
11
12/// Progress callback invoked by [`super::MirrorBootstrapReceiver`] every
13/// `PROGRESS_REPORT_CHUNK_BYTES` to update `MirrorStatus::Bootstrapping`.
14pub type ProgressCallback = Arc<dyn Fn(MirrorStatus) + Send + Sync + 'static>;
15
16/// Granularity at which the receiver reports progress: ~1 MiB.
17pub const PROGRESS_REPORT_CHUNK_BYTES: u64 = 1024 * 1024;
18
19/// Wire envelope that wraps a cross-cluster snapshot chunk.
20///
21/// Encoded with zerompk (MessagePack) and placed in the `data` field of the
22/// existing [`nodedb_raft::InstallSnapshotRequest`].  The in-cluster Raft
23/// machinery transfers the bytes unchanged; the mirror receiver unwraps this
24/// envelope.
25///
26/// # Integrity
27///
28/// `total_crc32c` is the CRC32C of the *entire* snapshot payload (concatenation
29/// of every chunk's `data` in offset order).  The source computes it once over
30/// the snapshot file and stamps the same value into every envelope of the
31/// transfer.  The receiver maintains a running CRC32C as chunks arrive and
32/// validates it against `total_crc32c` when `done = true`; mismatch raises
33/// [`super::super::error::MirrorError::SnapshotCrcMismatch`] and the partial
34/// file is discarded.
35#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
36pub struct CrossClusterSnapshotEnvelope {
37    /// Cluster-id of the source cluster that produced this snapshot.
38    pub source_cluster_id: String,
39    /// Database id on the source cluster being mirrored.
40    pub source_database_id: String,
41    /// WAL LSN at which this snapshot was taken.  After the snapshot is
42    /// fully applied the mirror sets `last_applied` to this value and
43    /// begins streaming AppendEntries from `snapshot_lsn + 1`.
44    pub snapshot_lsn: u64,
45    /// Total snapshot size in bytes (same value in every chunk of the same
46    /// snapshot transfer).
47    pub total_bytes: u64,
48    /// CRC32C over the entire snapshot payload (same value in every chunk of
49    /// the same snapshot transfer).  Validated by the receiver on the final
50    /// chunk.
51    pub total_crc32c: u32,
52    /// Byte offset within the snapshot for this chunk.
53    pub offset: u64,
54    /// Chunk payload bytes.
55    pub data: Vec<u8>,
56    /// True on the final chunk.
57    pub done: bool,
58}
59
60/// Outcome of processing a single incoming snapshot chunk.
61#[derive(Debug)]
62pub enum BootstrapChunkOutcome {
63    /// More chunks expected; `bytes_done` is the total received so far.
64    Pending { bytes_done: u64 },
65    /// All chunks received, CRC validated, file committed.
66    /// The mirror should now set `status = MirrorStatus::Following` and
67    /// begin streaming AppendEntries from `snapshot_lsn + 1`.
68    Committed {
69        snapshot_lsn: Lsn,
70        snapshot_path: PathBuf,
71    },
72}