navi_vm\snapshot/mod.rs
1//! Save and restore VM instance state.
2//!
3//! This module provides [`Instance::save_state`] and
4//! [`Instance::restore_state`] for pausing execution at bar N and resuming
5//! from bar N+1 later.
6//!
7//! # Server-side best practices
8//!
9//! In a server deployment, each running script typically processes thousands
10//! of historical bars before reaching the live feed. Replaying all historical
11//! bars on every restart is expensive. Snapshots let you checkpoint the VM
12//! state after the historical replay, then reload it instantly on the next
13//! start.
14//!
15//! ## Recommended workflow
16//!
17//! ```text
18//! Cold start
19//! │
20//! ├─ Build Instance (InstanceBuilder)
21//! ├─ let mut handle = instance.run(symbol, timeframe)
22//! ├─ while let Some(e) = handle.next_event().await { … }
23//! ├─ handle.save_state() → Vec<u8> (or instance.run_to_end() then save_state())
24//! ├─ Persist the bytes (database, object storage, local disk, …)
25//! └─ Continue feeding live bars
26//!
27//! Warm restart
28//! │
29//! ├─ Load the persisted bytes
30//! ├─ let mut instance = Instance::restore_state(&bytes)?
31//! │ .with_data_provider(provider).build()?;
32//! └─ instance.run_to_end() — automatically resumes from the next bar
33//! ```
34//!
35//! ## When to save
36//!
37//! The current bar **must** be confirmed before saving — `save_state()` returns
38//! [`SnapshotError::UnconfirmedBar`](crate::snapshot::SnapshotError::UnconfirmedBar) otherwise. Save once after the last
39//! confirmed bar and resave whenever you want to advance the checkpoint.
40//!
41//! ## Candlestick data
42//!
43//! Candlestick data is **not** included in the snapshot. When `run()` is
44//! called on a restored instance, the VM automatically passes
45//! `last_bar_time + 1` to the data provider so that only new bars are
46//! fetched.
47//!
48//! ## Versioning and invalidation
49//!
50//! Snapshots embed a format version (`SNAPSHOT_VERSION`) and the compiled
51//! [`Program`](navi_program::Program). A snapshot is only valid
52//! for the exact binary that created it: a new Navi release, a changed
53//! script, or modified inputs all require discarding the snapshot and
54//! replaying history from scratch.
55//!
56//! A simple invalidation strategy: store the snapshot alongside a hash of the
57//! Pine source (or the compiled program bytes). On startup, compare the hash;
58//! if it differs, delete the snapshot and do a full cold replay.
59//!
60//! ## Example
61//!
62//! ```rust,ignore
63//! use navi_vm::{Instance, snapshot::SnapshotError};
64//!
65//! // --- Cold start: replay history and checkpoint ---
66//! let mut instance = build_instance(script, symbol_info, timeframe)
67//! .run_to_end(symbol, timeframe)
68//! .await?;
69//! let snapshot: Vec<u8> = instance.save_state()?; // bar must be confirmed
70//! persist_to_storage(&snapshot);
71//!
72//! // --- Warm restart: restore and continue ---
73//! let bytes: Vec<u8> = load_from_storage();
74//! let instance = Instance::restore_state(&bytes)?
75//! .with_data_provider(provider)
76//! .build()?
77//! .run_to_end(symbol, timeframe)
78//! .await?; // auto-resumes
79//! ```
80
81pub(crate) mod restore;
82pub(crate) mod save;
83pub(crate) mod types;
84
85pub use restore::RestoreBuilder;
86pub use types::SnapshotError;