Skip to content

Commit

Permalink
feat(cardano-chain-follower): Overhead benchmark implementations (pal…
Browse files Browse the repository at this point in the history
…las + cardano-chain-follower) (#211)

* feat(cardano-chain-follower): Basic benchmark implementations (pallas + cardano-chain-follower)

Co-authored-by: Joaquín Rosales <joaquin.rosales@iohk.io>

* feat(cardano-chain-follower): Working snapshot_tip function and some output improvements

* feat(cardano-chain-follower): Change directory structure to match hermes crates conventions

* feat(cardano-chain-follower): Revert Earthfile to version 0.7 to make CI pass

* feat(cardano-chain-follower): Fix spelling and benchmark Earthfile

* chore(cardano-chain-follower): Add some READMEs to the cardano-chain-follower overhead benchmark

---------

Co-authored-by: Joaquín Rosales <joaquin.rosales@iohk.io>
Co-authored-by: Oleksandr Prokhorenko <djminikin@gmail.com>
  • Loading branch information
3 people committed Apr 25, 2024
1 parent 801c976 commit 433a72b
Show file tree
Hide file tree
Showing 13 changed files with 503 additions and 0 deletions.
1 change: 1 addition & 0 deletions .config/dictionaries/project.dic
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Datelike
dreps
encryptor
Errno
Earthfile
excalidraw
fadvise
fcntl
Expand Down
40 changes: 40 additions & 0 deletions hermes/crates/cardano-chain-follower/testbed/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
[workspace]
resolver = "2"
members = ["overhead_benchmark"]

[workspace.package]
edition = "2021"

[workspace.lints.rust]
warnings = "deny"
missing_docs = "deny"
let_underscore_drop = "deny"
non_ascii_idents = "deny"
single_use_lifetimes = "deny"
trivial_casts = "deny"
trivial_numeric_casts = "deny"

[workspace.lints.rustdoc]
broken_intra_doc_links = "deny"
invalid_codeblock_attributes = "deny"
invalid_html_tags = "deny"
invalid_rust_codeblocks = "deny"
bare_urls = "deny"
unescaped_backticks = "deny"

[workspace.lints.clippy]
pedantic = "deny"
unwrap_used = "deny"
expect_used = "deny"
exit = "deny"
get_unwrap = "deny"
index_refutable_slice = "deny"
indexing_slicing = "deny"
match_on_vec_items = "deny"
match_wild_err_arm = "deny"
missing_panics_doc = "deny"
panic = "deny"
string_slice = "deny"
unchecked_duration_subtraction = "deny"
unreachable = "deny"
missing_docs_in_private_items = "deny"
5 changes: 5 additions & 0 deletions hermes/crates/cardano-chain-follower/testbed/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Testbed

This workspace contains test scenarios for the `cardano-chain-follower` crate
in order the measure implementation details that are not so easily tested by
automated test frameworks.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
local_preprod_mithril_snapshot/
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "overhead_benchmark"
version = "0.1.0"
edition.workspace = true

[lints]
workspace = true

[dependencies]
cardano-chain-follower = { path = "../.." }

anyhow = "1.0.82"
clap = { version = "4.5.4", features = ["derive", "help", "usage", "std"], default-features = false }
pallas-traverse = "0.24.0"
pallas-hardano = "0.24.0"
tokio = { version = "1.37.0", features = ["macros", "sync", "rt-multi-thread", "rt", "net"] }
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
VERSION 0.7

IMPORT ../../../.. AS hermes

build:
FROM hermes+builder

WORKDIR crates/cardano-chain-follower/testbed
RUN cargo build -p overhead_benchmark --release

SAVE ARTIFACT target/release/overhead_benchmark overhead_benchmark

local-run-preprod:
ARG --required BENCH_NAME

FROM +build

COPY --dir github.com/input-output-hk/catalyst-ci/earthly/mithril_snapshot+package-preprod-snapshot/snapshot/immutable mithril_snapshot
COPY +build/overhead_benchmark overhead_benchmark_bin
RUN ./overhead_benchmark_bin --bench-name $BENCH_NAME --mithril-snapshot-path ./mithril_snapshot

local-save-preprod-snapshot:
FROM github.com/input-output-hk/catalyst-ci/earthly/mithril_snapshot+package-preprod-snapshot
SAVE ARTIFACT immutable AS LOCAL local_preprod_mithril_snapshot
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Overhead Benchmark

The purpose of this benchmark is to measure the overhead that using the `cardano-chain-follower` crate
has on top of the `pallas` crate which is used to implement most of the chain follower features.

## Running

In order to execute the benchmark you need a valid Mithril snapshot to point it to.
It doesn't matter which network the snapshot is from because the benchmark will only read the data from it.

There are 2 modes in which the benchmark can be executed:

| Benchmark name | Description |
---------------------------|--------------|
| pallas | When executed with `--bench-name pallas`, the benchmark reads the Mithril snapshot from origin to its tip using only the `pallas` crate mechanisms |
| cardano&#x2011;chain&#x2011;follower | When executed with `--bench-name cardano-chain-follower` it uses the `cardano-chain-follower` crate to follow the chain from origin to the tip of the specified snapshot |

One way of executing the benchmark is as follows:

```sh
cargo run --release -- --bench-name cardano-chain-follower --mithril-snapshot-path PATH_TO_MITHRIL_SNAPSHOT
```

## Earthfile

The Earthfile has targets for building and running the benchmark.
It also contains targets to fetch Mithril snapshots and save them locally.
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//! Benchmark implementation that uses the chain-follower-crate to read all the
//! blocks from the specified Mithril snapshot.

use cardano_chain_follower::{ChainUpdate, Follower, FollowerConfigBuilder, Network};
use pallas_traverse::MultiEraBlock;

use super::{monitor, snapshot_tip, BenchmarkParams};

/// Executes the benchmark.
pub async fn run(params: BenchmarkParams) -> anyhow::Result<()> {
let mithril_snapshot_tip_data = snapshot_tip(&params.mithril_snapshot_path)?
.ok_or(anyhow::anyhow!("Failed to find snapshot tip"))?;
let mithril_snapshot_tip_block = MultiEraBlock::decode(&mithril_snapshot_tip_data)?;

let monitor_task_handle = monitor::spawn_task(mithril_snapshot_tip_block.number());

let config = FollowerConfigBuilder::default()
.follow_from(cardano_chain_follower::PointOrTip::Point(
cardano_chain_follower::Point::Origin,
))
.mithril_snapshot_path(params.mithril_snapshot_path)
.build();

let mut follower = Follower::connect(
"relays-new.cardano-mainnet.iohk.io:3001",
Network::Mainnet,
config,
)
.await?;

loop {
let update = follower.next().await?;

match update {
ChainUpdate::Block(raw_block_data) => {
let block_data = raw_block_data.decode()?;

monitor_task_handle.send_update(monitor::BenchmarkStats {
blocks_read: 1,
block_bytes_read: raw_block_data.as_ref().len() as u64,
current_block: block_data.number(),
current_slot: block_data.slot(),
})?;

if block_data.number() >= mithril_snapshot_tip_block.number() {
break;
}
},
ChainUpdate::Rollback(_) => {
anyhow::bail!("Unexpected rollback: benchmark should not receive rollback events");
},
}
}

monitor_task_handle.close().await;

Ok(())
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//! Benchmark-specific implementations.

pub mod cardano_chain_follower;
mod monitor;
pub mod pallas;

use std::{
collections::BinaryHeap,
ffi::OsStr,
fs,
path::{Path, PathBuf},
};

use pallas_traverse::MultiEraBlock;

/// Parameters accepted by the benchmarks.
pub struct BenchmarkParams {
/// Path to the Mithril snapshot that will be read by the benchmark.
pub mithril_snapshot_path: PathBuf,
}

/// Locate the tip of a Mithril snapshot.
///
/// NOTE(fsgr):
/// This was needed because the current implementation found in pallas fails if
/// the Immutable DB indexes are corrupted. This seems to happen often since
/// Mithril snapshots are taken while the Cardano node is running. For this reason,
/// this function finds the most recent *valid* block in the snapshot in order to
/// get the benchmarks working for now.
fn snapshot_tip(path: &Path) -> anyhow::Result<Option<Vec<u8>>> {
// First we collect all the .chunk files in an ordered manner.
let mut chunk_files = BinaryHeap::new();

for result in fs::read_dir(path)? {
let entry = result?;

let path = entry.path();

match path.extension().map(OsStr::to_string_lossy) {
None => continue,
Some(ext) => {
if ext != "chunk" {
continue;
}
},
}

if let Some(stem) = path.file_stem() {
chunk_files.push(stem.to_string_lossy().to_string());
}
}

while let Some(filename) = chunk_files.pop() {
let reader = pallas_hardano::storage::immutable::chunk::read_blocks(path, &filename)?;

if let Some(last_valid_block_data) = reader.map_while(Result::ok).last() {
if MultiEraBlock::decode(&last_valid_block_data).is_ok() {
return Ok(Some(last_valid_block_data));
}
}
}

Ok(None)
}

0 comments on commit 433a72b

Please sign in to comment.