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}