Validator snapshot
Load every account owned by a program, or a list of accounts, from a Solana validator snapshot on disk or downloaded from a node.
On this page
- Quick example
- Configuration
- SnapshotDatasource::
new - SnapshotSource
- Behaviour
- Troubleshooting
- Build fails in clang-sys: couldn't find any valid shared libraries matching: ['libclang.
so', …] - Genesis archive not found: …/genesis.
tar. bz2 - No loadable full snapshot archive found in …/snapshots
- Failed to download …/genesis.
tar. bz2 after 3 retries - The process is killed during loading
- See also
SnapshotDatasource loads a full validator snapshot with Agave's own loader, scans every account in it, and emits the ones owned by programs you list (or with addresses you list) as account updates at the snapshot's slot. Use it to bootstrap complete account state for a large program without getProgramAccounts limits, when you can afford the disk, memory and time a mainnet snapshot needs.
| Crate | carbon-validator-snapshot-datasource 2.0.0 |
| Install | cargo add carbon-validator-snapshot-datasource@2. |
| Emits | AccountUpdate |
| Mode | Point-in-time state at the snapshot slot. Finishes after the scan. |
| Provider | None: a snapshot archive on disk, or a node that serves / and / (Agave RPC nodes do, including api.). |
| API docs | docs.rs |
| Source | datasources/ |
Quick example#
This program loads every Orca Whirlpool pool account from a snapshot and prints its liquidity. It compiles as shown; to run it, set SNAPSHOT_ to a ledger directory or SNAPSHOT_ to a node.
cargo new snapshot && cd snapshot
cargo add carbon-core@2.0.0 carbon-log-metrics@2.0.0 carbon-orca-whirlpool-decoder@2.0.0 carbon-validator-snapshot-datasource@2.0.0
cargo add tokio@1 --features full
cargo add env_logger@0.11 log@0.4use {
carbon_core::{
account::AccountProcessorInputType, error::CarbonResult, pipeline::Pipeline,
processor::Processor,
},
carbon_log_metrics::LogMetrics,
carbon_orca_whirlpool_decoder::{
accounts::OrcaWhirlpoolAccount, OrcaWhirlpoolDecoder, PROGRAM_ID,
},
carbon_validator_snapshot_datasource::{SnapshotDatasource, SnapshotSource},
std::{env, sync::Arc},
};
#[tokio::main]
async fn main() -> CarbonResult<()> {
env_logger::init();
let source = match env::var("SNAPSHOT_PATH") {
// A ledger directory with genesis.tar.bz2 and snapshots/<full snapshot archive>
Ok(path) => SnapshotSource::LocalPath(path.into()),
// A node that serves /genesis.tar.bz2 and /snapshot.tar.bz2, e.g. http://1.2.3.4:8899
Err(_) => SnapshotSource::Remote {
url: env::var("SNAPSHOT_URL").expect("set SNAPSHOT_PATH or SNAPSHOT_URL"),
},
};
let datasource = SnapshotDatasource::new(
source,
vec![PROGRAM_ID], // owners: every account owned by the Orca Whirlpool program
vec![], // accounts: specific pubkeys, whatever their owner
);
Pipeline::builder()
.datasource(datasource)
.metrics(Arc::new(LogMetrics::new()))
.account(OrcaWhirlpoolDecoder, PoolLogger)
.build()?
.run()
.await
}
struct PoolLogger;
impl Processor<AccountProcessorInputType<'_, OrcaWhirlpoolAccount>> for PoolLogger {
async fn process(
&mut self,
input: &AccountProcessorInputType<'_, OrcaWhirlpoolAccount>,
) -> CarbonResult<()> {
if let OrcaWhirlpoolAccount::Whirlpool(pool) = &input.decoded_account.data {
println!(
"slot {} pool {} liquidity={}",
input.metadata.slot, input.metadata.pubkey, pool.liquidity
);
}
Ok(())
}
}cargo build --release
SNAPSHOT_PATH=/data/ledger RUST_LOG=info ./target/release/snapshotBuild in release mode: loading a snapshot in a debug build is much slower. With RUST_ you see Snapshot loaded at slot … in …s, starting account scan, one line per pool, then Snapshot processing completed. Pointed at a directory without a genesis archive, it stops at once:
[ERROR carbon_core::pipeline] error consuming datasource: FailedToConsumeDatasource("Genesis archive not found: \"/data/ledger/genesis.tar.bz2\". Expected genesis.tar.bz2")Configuration#
SnapshotDatasource:: new#
pub fn new(source: SnapshotSource, owners: Vec<Pubkey>, accounts: Vec<Pubkey>) -> Self| Parameter | Type | Meaning |
|---|---|---|
source | SnapshotSource | Where the snapshot comes from. |
owners | Vec<Pubkey> | Emit every account owned by one of these programs. |
accounts | Vec<Pubkey> | Also emit these accounts, whatever their owner. |
If both lists are empty, the datasource logs No owners or accounts specified, skipping account scan and emits nothing.
SnapshotSource#
pub enum SnapshotSource {
LocalPath(PathBuf),
Remote { url: String },
}| Variant | Meaning |
|---|---|
LocalPath(path) | A ledger directory containing genesis. and a snapshots/ directory with a full snapshot archive (snapshot-<; an incremental archive next to it is also loaded). The loader unpacks accounts into <. |
Remote { url } | A base URL. The datasource downloads < and <, following up to 10 redirects, into ./ledger under the current working directory, then loads it like a local path. Pass the node's address, not the archive URL. |
Behaviour#
- Downloading. Each file is tried 3 times, 2 seconds apart. The saved file name comes from the
Content-Dispositionheader or the final redirect URL, so a node's redirect tosnapshot-<keeps the name Agave expects. Progress is logged every 50 MB and 5 seconds. Only the full snapshot is downloaded.slot>-< hash>. tar. zst - Loading. Agave's
bank_loads the snapshot in load-only mode, skipping the initial accounts hash. A loaded bank at slot 0 is rejected as invalid. Loading runs on the async task that calledforks_ utils consumeand can't be cancelled. - Scanning. Every account is visited once on a blocking thread. Accounts with zero lamports are skipped. Matches are emitted as
AccountUpdatewith the snapshot slot andtransaction_; no deletions are emitted.signature: None - Ordering. The order of the accounts database, not by pubkey or slot.
- Backpressure. Updates are sent with
blocking_, so the scan waits for the pipeline and nothing is dropped.send - Finishing.
consumereturns after the scan. A pipeline with only this datasource then shuts down; next to a streaming datasource, the pipeline keeps running. - Errors. Missing directories or archives, a failed download, or a snapshot Agave can't load return
FailedToConsumeDatasourcewith the reason. - Metrics.
validator_(counter),snapshot_ accounts_ processed_ total validator_,snapshot_ load_ duration_ milliseconds validator_(histograms).snapshot_ scan_ duration_ milliseconds
Troubleshooting#
Build fails in clang-sys: couldn't find any valid shared libraries matching: ['libclang. so', …]#
Install libclang (sudo apt install clang libclang-dev on Debian and Ubuntu), or point LIBCLANG_ at the directory that contains libclang.so.
Genesis archive not found: …/ genesis. tar. bz2#
LocalPath must point at the ledger directory itself, with genesis. at its top level.
No loadable full snapshot archive found in …/ snapshots#
Put the full snapshot archive in < and keep its original snapshot-< name; Agave finds archives by name.
Failed to download …/ genesis. tar. bz2 after 3 retries#
The URL is not a node's base address, or the node doesn't serve snapshots. Remote { url } appends / itself: use https:/, not …/.
The process is killed during loading#
Out of memory or disk. Mainnet snapshots need a machine sized like an RPC node. For a single program, GpaDatasource or Helius gPA v2 is far cheaper.