Skip to main content

bb_runtime/snapshot/
mod.rs

1//! `NodeSnapshot`
2//!
3//! Captures the snapshottable state of a running Node so a fresh
4//! Node can be reconstructed via `Node::restore(snap)` and resume
5//! execution.
6//!
7//! **scope** - ships the Rust struct surface. The 11 proto
8//! mirror messages outlined in
9//! `docs/internal/IMPLEMENTATION_PLAN.md`  are deferred to the
10//! cross-stage audit; on-the-wire snapshot transfer is not a
11//! acceptance-gate requirement and the in-memory Rust
12//! surface is sufficient for the round-trip semantics. The fields
13//! line up 1:1 with the proto-spec field shapes so the future
14//! addition is mechanical.
15
16use serde::{Deserialize, Serialize};
17
18use crate::concrete::ComponentPackage;
19use crate::node::NodeConfig;
20
21pub mod transient;
22pub use transient::TransientSnapshot;
23
24/// Top-level snapshot per ENGINE.md §15.1.
25#[derive(Clone, Debug, Serialize, Deserialize)]
26pub struct NodeSnapshot {
27    /// Monotonically bumped on every `Node::restore`. Used by hosts
28    /// to detect "is this the same Node since N seconds ago?".
29    pub incarnation: u64,
30
31    /// The NodeConfig captured at snapshot time. NodeConfig must
32    /// match exactly at restore time.
33    pub config: NodeConfigSnapshot,
34
35    /// Installed graphs (post-analysis FunctionProto bytes).
36    pub graphs: Vec<NamedGraphSnapshot>,
37
38    /// Per-component serialized state.
39    pub components: Vec<NamedComponentSnapshot>,
40
41    /// In-flight transient state (frontier, slot_table, …).
42    pub transient: TransientSnapshot,
43}
44
45/// Stable serializable view of `NodeConfig` (the runtime struct has
46/// fields that aren't 1:1 serde-friendly).
47#[derive(Clone, Debug, Default, Serialize, Deserialize)]
48pub struct NodeConfigSnapshot {
49    /// Canonical multihash bytes for `NodeConfig.peer_id`.
50    /// Empty when the snapshot was taken without a configured peer.
51    #[serde(default)]
52    pub peer_id: Vec<u8>,
53    /// `NodeConfig.cycle_op_budget`.
54    pub cycle_op_budget: Option<usize>,
55    /// `NodeConfig.max_pending_async`.
56    #[serde(default)]
57    pub max_pending_async: Option<usize>,
58    /// `NodeConfig.max_outbound_queue`.
59    #[serde(default)]
60    pub max_outbound_queue: Option<usize>,
61    /// `NodeConfig.bus_capacity`.
62    pub bus_capacity: usize,
63}
64
65impl From<&NodeConfig> for NodeConfigSnapshot {
66    fn from(c: &NodeConfig) -> Self {
67        Self {
68            peer_id: c.peer_id.to_bytes(),
69            cycle_op_budget: c.cycle_op_budget,
70            max_pending_async: c.max_pending_async,
71            max_outbound_queue: c.max_outbound_queue,
72            bus_capacity: c.bus_capacity,
73        }
74    }
75}
76
77/// One installed graph's snapshot.
78#[derive(Clone, Debug, Serialize, Deserialize)]
79pub struct NamedGraphSnapshot {
80    /// Graph name (key in `engine.graphs`).
81    pub name: String,
82    /// Prost-serialized `FunctionProto` bytes.
83    pub function_proto_bytes: Vec<u8>,
84}
85
86/// One component's snapshot.
87#[derive(Clone, Debug, Serialize, Deserialize)]
88pub struct NamedComponentSnapshot {
89    /// `ConcreteComponent::TYPE_NAME`.
90    pub type_name: String,
91    /// Per-Node `instance_id` used in NodeProto metadata.
92    pub instance_id: u32,
93    /// `ConcreteComponent::PACKAGE`.
94    pub package: ComponentPackage,
95    /// Captured state bytes from `ConcreteComponent::serialize`.
96    pub state_bytes: Vec<u8>,
97}
98
99impl NodeSnapshot {
100    /// Bincode-encode the snapshot for on-disk persistence or
101    /// inter-process transfer. ships this as the only
102    /// transport; may add the prost-bytes equivalent.
103    pub fn encode(&self) -> Vec<u8> {
104        bincode::serialize(self).expect("NodeSnapshot serde is infallible for valid types")
105    }
106
107    /// Decode a snapshot from bincode bytes.
108    pub fn decode(bytes: &[u8]) -> Result<Self, bincode::Error> {
109        bincode::deserialize(bytes)
110    }
111}
112