Skip to content

Commit f3fd5aa

Browse files
authored
Merge pull request #652 from input-output-hk/djo/510/optimize-snapshot-digest
Optimize snapshot digest computation by adding a cache system
2 parents c625805 + d3a91fc commit f3fd5aa

File tree

26 files changed

+1583
-60
lines changed

26 files changed

+1583
-60
lines changed

Cargo.lock

Lines changed: 27 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

mithril-aggregator/src/command_args.rs

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,18 @@ use clap::{Parser, Subcommand};
22
use config::{builder::DefaultState, ConfigBuilder, Map, Source, Value, ValueKind};
33
use slog::Level;
44
use slog_scope::debug;
5-
use std::error::Error;
6-
use std::fs;
7-
use std::path::PathBuf;
8-
use std::sync::Arc;
9-
use tokio::sync::RwLock;
10-
use tokio::time::Duration;
5+
use std::{error::Error, fs, path::PathBuf, sync::Arc};
6+
use tokio::{sync::RwLock, time::Duration};
117

128
use mithril_common::{
139
certificate_chain::MithrilCertificateVerifier,
1410
chain_observer::{CardanoCliRunner, ChainObserver},
1511
crypto_helper::{key_decode_hex, ProtocolGenesisSigner, ProtocolGenesisVerifier},
1612
database::{ApplicationNodeType, DatabaseVersionChecker},
17-
digesters::{CardanoImmutableDigester, ImmutableFileSystemObserver},
13+
digesters::{
14+
cache::{ImmutableFileDigestCacheProvider, JsonImmutableFileDigestCacheProviderBuilder},
15+
CardanoImmutableDigester, ImmutableFileSystemObserver,
16+
},
1817
entities::{Epoch, HexEncodedGenesisSecretKey},
1918
store::{adapter::SQLiteAdapter, StakeStore},
2019
BeaconProviderImpl,
@@ -254,6 +253,16 @@ pub struct ServeCommand {
254253
/// Defaults to work folder
255254
#[clap(long)]
256255
pub snapshot_directory: Option<PathBuf>,
256+
257+
/// Disable immutables digests cache.
258+
#[clap(long)]
259+
disable_digests_cache: bool,
260+
261+
/// If set the existing immutables digests cache will be reset.
262+
///
263+
/// Will be ignored if set in conjunction with `--disable-digests-cache`.
264+
#[clap(long)]
265+
reset_digests_cache: bool,
257266
}
258267

259268
impl Source for ServeCommand {
@@ -357,6 +366,7 @@ impl ServeCommand {
357366
));
358367
let digester = Arc::new(CardanoImmutableDigester::new(
359368
config.db_directory.clone(),
369+
self.build_digester_cache_provider(&config).await?,
360370
slog_scope::logger(),
361371
));
362372
let multi_signer = Arc::new(RwLock::new(MultiSignerImpl::new(
@@ -453,6 +463,24 @@ impl ServeCommand {
453463
println!("Exiting...");
454464
Ok(())
455465
}
466+
467+
async fn build_digester_cache_provider(
468+
&self,
469+
config: &Configuration,
470+
) -> Result<Option<Arc<dyn ImmutableFileDigestCacheProvider>>, Box<dyn Error>> {
471+
if self.disable_digests_cache {
472+
return Ok(None);
473+
}
474+
let cache_provider = JsonImmutableFileDigestCacheProviderBuilder::new(
475+
&config.data_stores_directory,
476+
&format!("immutables_digests_{}.json", config.network),
477+
)
478+
.should_reset_digests_cache(self.reset_digests_cache)
479+
.build()
480+
.await?;
481+
482+
Ok(Some(Arc::new(cache_provider)))
483+
}
456484
}
457485

458486
/// Genesis tools

mithril-client/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ async-trait = "0.1.52"
1414
clap = { version = "4.0", features = ["derive", "env"] }
1515
cli-table = "0.4"
1616
config = "0.13.1"
17+
directories = "4.0.1"
1718
flate2 = "1.0.23"
1819
futures = "0.3"
1920
hex = "0.4.3"

mithril-client/src/commands/restore.rs

Lines changed: 54 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
use std::{error::Error, path::Path, sync::Arc};
2-
31
use clap::Parser;
42
use config::{builder::DefaultState, ConfigBuilder};
3+
use directories::ProjectDirs;
54
use mithril_common::{
65
certificate_chain::MithrilCertificateVerifier,
76
crypto_helper::{key_decode_hex, ProtocolGenesisVerifier},
8-
digesters::CardanoImmutableDigester,
7+
digesters::{
8+
cache::ImmutableFileDigestCacheProvider,
9+
cache::JsonImmutableFileDigestCacheProviderBuilder, CardanoImmutableDigester,
10+
},
911
};
10-
use slog_scope::debug;
12+
use slog_scope::{debug, warn};
13+
use std::{error::Error, path::Path, sync::Arc};
1114

1215
use crate::{AggregatorHTTPClient, AggregatorHandler, Config, Runtime};
1316

@@ -18,6 +21,16 @@ pub struct RestoreCommand {
1821
#[clap(long)]
1922
json: bool,
2023

24+
/// Disable immutables digests cache.
25+
#[clap(long)]
26+
disable_digests_cache: bool,
27+
28+
/// If set the existing immutables digests cache will be reset.
29+
///
30+
/// Will be ignored if set in conjunction with `--disable-digests-cache`.
31+
#[clap(long)]
32+
reset_digests_cache: bool,
33+
2134
/// Digest of the snapshot to download. Use the `list` command to get that information.
2235
digest: String,
2336
}
@@ -37,14 +50,21 @@ impl RestoreCommand {
3750
debug!("{:?}", config);
3851
let mut runtime = Runtime::new(config.network.clone());
3952
let aggregator_handler =
40-
AggregatorHTTPClient::new(config.network.clone(), config.aggregator_endpoint);
53+
AggregatorHTTPClient::new(config.network.clone(), config.aggregator_endpoint.clone());
4154
let certificate_verifier = Box::new(MithrilCertificateVerifier::new(slog_scope::logger()));
4255
let genesis_verification_key = key_decode_hex(&config.genesis_verification_key)?;
4356
let genesis_verifier =
4457
ProtocolGenesisVerifier::from_verification_key(genesis_verification_key);
4558
let unpacked_path = aggregator_handler.unpack_snapshot(&self.digest).await?;
59+
4660
let digester = Box::new(CardanoImmutableDigester::new(
4761
Path::new(&unpacked_path).into(),
62+
build_digester_cache_provider(
63+
self.disable_digests_cache,
64+
self.reset_digests_cache,
65+
&config,
66+
)
67+
.await?,
4868
slog_scope::logger(),
4969
));
5070
let output = runtime
@@ -73,3 +93,32 @@ docker run -v cardano-node-ipc:/ipc -v cardano-node-data:/data --mount type=bind
7393
Ok(())
7494
}
7595
}
96+
97+
async fn build_digester_cache_provider(
98+
disable_digests_cache: bool,
99+
reset_digests_cache: bool,
100+
config: &Config,
101+
) -> Result<Option<Arc<dyn ImmutableFileDigestCacheProvider>>, Box<dyn Error>> {
102+
if disable_digests_cache {
103+
return Ok(None);
104+
}
105+
106+
match ProjectDirs::from("io", "iohk", "mithril") {
107+
None => {
108+
warn!("Could not get cache directory, disabling immutables digests cache");
109+
Ok(None)
110+
}
111+
Some(project_dirs) => {
112+
let cache_provider = JsonImmutableFileDigestCacheProviderBuilder::new(
113+
project_dirs.cache_dir(),
114+
&format!("immutables_digests_{}.json", config.network),
115+
)
116+
.ensure_dir_exist()
117+
.should_reset_digests_cache(reset_digests_cache)
118+
.build()
119+
.await?;
120+
121+
Ok(Some(Arc::new(cache_provider)))
122+
}
123+
}
124+
}

mithril-common/Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,16 @@ repository = { workspace = true }
1111
[lib]
1212
crate-type = ["lib", "cdylib", "staticlib"]
1313

14+
[[bench]]
15+
name = "digester"
16+
harness = false
17+
1418
[dependencies]
1519
async-trait = "0.1.52"
1620
bech32 = "0.9.1"
1721
blake2 = "0.10.4"
1822
chrono = "0.4"
23+
digest = "0.10.6"
1924
ed25519-dalek = { version = "1.0.1", features = ["serde"] }
2025
fixed = "1.15.0"
2126
glob = "0.3"
@@ -36,6 +41,7 @@ serde_json = "1.0"
3641
serde_yaml = "0.9.10"
3742
sha2 = "0.10.2"
3843
slog = { version = "2.7.0", features = ["max_level_trace", "release_max_level_debug"] }
44+
slog-scope = "4.4.0"
3945
sqlite = "0.28"
4046
thiserror = "1.0.31"
4147
tokio = { version = "1.17.0", features = ["full"] }
@@ -51,7 +57,10 @@ mithril-stm = { path = "../mithril-stm" }
5157
mithril-stm = { path = "../mithril-stm", default-features = false, features = ["num-integer-backend"] }
5258

5359
[dev-dependencies]
60+
criterion = { version = "0.4.0", features = ["html_reports", "async_tokio"] }
61+
slog-async = "2.7.0"
5462
slog-scope = "4.4.0"
63+
slog-term = "2.9.0"
5564

5665
[features]
5766
default = ["allow_skip_signer_certification"]

mithril-common/benches/digester.rs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
use criterion::{criterion_group, criterion_main, Criterion};
2+
use mithril_common::{
3+
digesters::cache::{ImmutableFileDigestCacheProvider, JsonImmutableFileDigestCacheProvider},
4+
digesters::{
5+
cache::MemoryImmutableFileDigestCacheProvider, CardanoImmutableDigester, ImmutableDigester,
6+
},
7+
entities::{Beacon, ImmutableFileNumber},
8+
};
9+
use slog::Drain;
10+
use std::{
11+
fs,
12+
fs::File,
13+
io::prelude::Write,
14+
path::{Path, PathBuf},
15+
sync::Arc,
16+
};
17+
18+
fn temp_dir() -> PathBuf {
19+
std::env::temp_dir()
20+
.join("mithril_benches")
21+
.join("digester")
22+
}
23+
24+
fn db_dir() -> PathBuf {
25+
temp_dir().join("db").join("immutable")
26+
}
27+
28+
fn create_db(dir: &Path, number_of_immutables: ImmutableFileNumber, file_size: u64) {
29+
if dir.exists() {
30+
fs::remove_dir_all(dir).unwrap_or_else(|e| panic!("Could not remove dir {:?}: {}", dir, e));
31+
}
32+
fs::create_dir_all(dir).unwrap_or_else(|e| panic!("Could not create dir {:?}: {}", dir, e));
33+
34+
// + 1 to simulate "in-progress" immutable trio.
35+
for filename in (1..=(number_of_immutables + 1)).flat_map(|i| {
36+
[
37+
format!("{:05}.chunk", i),
38+
format!("{:05}.primary", i),
39+
format!("{:05}.secondary", i),
40+
]
41+
}) {
42+
let file = dir.join(Path::new(&filename));
43+
let mut source_file = File::create(file).unwrap();
44+
45+
write!(source_file, "This is a test file named '{}'", filename).unwrap();
46+
writeln!(source_file).unwrap();
47+
source_file.set_len(file_size).unwrap();
48+
}
49+
}
50+
51+
#[inline]
52+
fn create_logger() -> slog::Logger {
53+
let drain = slog_async::Async::new(slog::Discard).build().fuse();
54+
slog::Logger::root(Arc::new(drain), slog::o!())
55+
}
56+
57+
#[inline]
58+
async fn compute_digest(
59+
cache_provider: Option<Arc<dyn ImmutableFileDigestCacheProvider>>,
60+
number_of_immutables: ImmutableFileNumber,
61+
) {
62+
let digester = CardanoImmutableDigester::new(db_dir(), cache_provider, create_logger());
63+
digester
64+
.compute_digest(&Beacon::new("devnet".to_string(), 1, number_of_immutables))
65+
.await
66+
.expect("digest computation should not fail");
67+
}
68+
69+
fn criterion_benchmark(c: &mut Criterion) {
70+
let runtime = tokio::runtime::Runtime::new().unwrap();
71+
let number_of_immutable = 25;
72+
create_db(&db_dir(), number_of_immutable, 65536 * 4);
73+
74+
c.bench_function("digester no cache", |bencher| {
75+
bencher
76+
.to_async(&runtime)
77+
.iter(|| async { compute_digest(None, number_of_immutable).await })
78+
});
79+
c.bench_function("digester memory cache", |bencher| {
80+
let cache = Arc::new(MemoryImmutableFileDigestCacheProvider::default());
81+
82+
bencher
83+
.to_async(&runtime)
84+
.iter(|| async { compute_digest(Some(cache.clone()), number_of_immutable).await })
85+
});
86+
c.bench_function("digester json cache", |bencher| {
87+
let cache = Arc::new(JsonImmutableFileDigestCacheProvider::new(
88+
&temp_dir().join("immutable-cache-store.json"),
89+
));
90+
91+
bencher
92+
.to_async(&runtime)
93+
.iter(|| async { compute_digest(Some(cache.clone()), number_of_immutable).await })
94+
});
95+
}
96+
97+
criterion_group!(benches, criterion_benchmark);
98+
criterion_main!(benches);

0 commit comments

Comments
 (0)