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