Skip to main content

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;