Skip to content

Commit b10ecaa

Browse files
apollo_proof_manager: stress test changes (do not merge)
1 parent 280cbb0 commit b10ecaa

14 files changed

Lines changed: 264 additions & 101 deletions

File tree

Cargo.lock

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

crates/apollo_dashboard/resources/dev_grafana.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1050,6 +1050,15 @@
10501050
"unit": "s"
10511051
}
10521052
},
1053+
{
1054+
"title": "Proof Verifications by Component",
1055+
"description": "The number of proof verifications by component (gateway vs consensus) (over the selected time range)",
1056+
"type": "stat",
1057+
"exprs": [
1058+
"sum by (component) (increase(proof_verification_count{cluster=~\"$cluster\", namespace=~\"$namespace\", pod=~\"$pod\"}[$__range]))"
1059+
],
1060+
"extra_params": {}
1061+
},
10531062
{
10541063
"title": "Proof Manager Store Latency",
10551064
"description": "The time it takes to store a proof in the proof manager",

crates/apollo_dashboard/src/panels/gateway.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,11 @@ use apollo_gateway::metrics::{
1313
LABEL_NAME_TX_TYPE as GATEWAY_LABEL_NAME_TX_TYPE,
1414
};
1515
use apollo_metrics::metrics::MetricQueryName;
16-
use apollo_transaction_converter::metrics::PROOF_VERIFICATION_LATENCY;
16+
use apollo_transaction_converter::metrics::{
17+
LABEL_NAME_COMPONENT,
18+
PROOF_VERIFICATION_COUNT,
19+
PROOF_VERIFICATION_LATENCY,
20+
};
1721

1822
use crate::dashboard::Row;
1923
use crate::panel::{Panel, PanelType, Unit};
@@ -148,6 +152,21 @@ fn get_panel_proof_verification_latency() -> Panel {
148152
.with_unit(Unit::Seconds)
149153
}
150154

155+
fn get_panel_proof_verification_count_by_component() -> Panel {
156+
Panel::new(
157+
"Proof Verifications by Component",
158+
"The number of proof verifications by component (gateway vs consensus) (over the selected \
159+
time range)",
160+
sum_by_label(
161+
&PROOF_VERIFICATION_COUNT,
162+
LABEL_NAME_COMPONENT,
163+
DisplayMethod::Increase(RANGE_DURATION),
164+
false,
165+
),
166+
PanelType::Stat,
167+
)
168+
}
169+
151170
fn get_panel_gateway_proof_manager_store_latency() -> Panel {
152171
Panel::from_hist(
153172
&GATEWAY_PROOF_MANAGER_STORE_LATENCY,
@@ -180,6 +199,7 @@ pub(crate) fn get_gateway_row() -> Row {
180199
get_panel_gateway_validate_stateful_tx_storage_time(),
181200
get_panel_gateway_validate_stateful_tx_storage_operations(),
182201
get_panel_proof_verification_latency(),
202+
get_panel_proof_verification_count_by_component(),
183203
get_panel_gateway_proof_manager_store_latency(),
184204
],
185205
)

crates/apollo_gateway/src/gateway.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ impl<
249249
// validation, to avoid storing proofs for rejected transactions.
250250
let store_result = self
251251
.transaction_converter
252-
.store_proof_in_proof_manager(proof_facts.clone(), proof.clone())
252+
.store_proof_in_proof_manager(proof_facts.clone(), tx_hash, proof.clone())
253253
.await;
254254
match store_result {
255255
Ok(proof_manager_store_duration) => {

crates/apollo_gateway/src/gateway_test.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -192,8 +192,8 @@ impl MockDependencies {
192192
self.mock_transaction_converter
193193
.expect_store_proof_in_proof_manager()
194194
.once()
195-
.with(eq(proof_facts), eq(proof))
196-
.returning(|_, _| Ok(std::time::Duration::ZERO));
195+
.withf(move |pf, _tx_hash, p| *pf == proof_facts && *p == proof)
196+
.returning(|_, _, _| Ok(std::time::Duration::ZERO));
197197
}
198198
}
199199

crates/apollo_proof_manager/src/communication.rs

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,25 @@ pub type RemoteProofManagerServer =
1313
impl ComponentRequestHandler<ProofManagerRequest, ProofManagerResponse> for ProofManager {
1414
async fn handle_request(&mut self, request: ProofManagerRequest) -> ProofManagerResponse {
1515
match request {
16-
ProofManagerRequest::SetProof(proof_facts, proof) => ProofManagerResponse::SetProof(
17-
self.set_proof(proof_facts, proof)
18-
.await
19-
.map_err(|e| ProofManagerError::ProofStorage(e.to_string())),
20-
),
21-
ProofManagerRequest::GetProof(proof_facts) => ProofManagerResponse::GetProof(
22-
self.get_proof(proof_facts)
23-
.await
24-
.map_err(|e| ProofManagerError::ProofStorage(e.to_string())),
25-
),
26-
ProofManagerRequest::ContainsProof(proof_facts) => ProofManagerResponse::ContainsProof(
27-
self.contains_proof(proof_facts)
16+
ProofManagerRequest::SetProof(proof_facts, tx_hash, proof) => {
17+
ProofManagerResponse::SetProof(
18+
self.set_proof(proof_facts, tx_hash, proof)
19+
.await
20+
.map_err(|e| ProofManagerError::ProofStorage(e.to_string())),
21+
)
22+
}
23+
ProofManagerRequest::GetProof(proof_facts, tx_hash) => ProofManagerResponse::GetProof(
24+
self.get_proof(proof_facts, tx_hash)
2825
.await
2926
.map_err(|e| ProofManagerError::ProofStorage(e.to_string())),
3027
),
28+
ProofManagerRequest::ContainsProof(proof_facts, tx_hash) => {
29+
ProofManagerResponse::ContainsProof(
30+
self.contains_proof(proof_facts, tx_hash)
31+
.await
32+
.map_err(|e| ProofManagerError::ProofStorage(e.to_string())),
33+
)
34+
}
3135
}
3236
}
3337
}

crates/apollo_proof_manager/src/proof_manager.rs

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6,34 +6,35 @@ use apollo_proof_manager_config::config::ProofManagerConfig;
66
use async_trait::async_trait;
77
use lru::LruCache;
88
use starknet_api::transaction::fields::{Proof, ProofFacts};
9+
use starknet_api::transaction::TransactionHash;
910
use starknet_types_core::felt::Felt;
1011

1112
use crate::proof_storage::{FsProofStorage, FsProofStorageError, ProofStorage};
1213

13-
/// In-memory LRU cache for proofs, keyed by the hash of the proof facts.
14+
/// In-memory LRU cache for proofs, keyed by the (proof facts hash, tx hash) pair.
1415
#[derive(Clone)]
1516
pub struct ProofCache {
16-
cache: Arc<Mutex<LruCache<Felt, Proof>>>,
17+
cache: Arc<Mutex<LruCache<(Felt, TransactionHash), Proof>>>,
1718
}
1819

1920
impl ProofCache {
2021
pub fn new(capacity: NonZeroUsize) -> Self {
2122
Self { cache: Arc::new(Mutex::new(LruCache::new(capacity))) }
2223
}
2324

24-
pub fn get(&self, facts_hash: &Felt) -> Option<Proof> {
25+
pub fn get(&self, key: &(Felt, TransactionHash)) -> Option<Proof> {
2526
let mut guard = self.cache.lock().expect("Failed to lock proof cache.");
26-
guard.get(facts_hash).cloned()
27+
guard.get(key).cloned()
2728
}
2829

29-
pub fn insert(&self, facts_hash: Felt, proof: Proof) {
30+
pub fn insert(&self, key: (Felt, TransactionHash), proof: Proof) {
3031
let mut guard = self.cache.lock().expect("Failed to lock proof cache.");
31-
guard.put(facts_hash, proof);
32+
guard.put(key, proof);
3233
}
3334

34-
pub fn contains(&self, facts_hash: &Felt) -> bool {
35+
pub fn contains(&self, key: &(Felt, TransactionHash)) -> bool {
3536
let guard = self.cache.lock().expect("Failed to lock proof cache.");
36-
guard.contains(facts_hash)
37+
guard.contains(key)
3738
}
3839
}
3940

@@ -54,45 +55,48 @@ impl ProofManager {
5455
pub async fn set_proof(
5556
&self,
5657
proof_facts: ProofFacts,
58+
tx_hash: TransactionHash,
5759
proof: Proof,
5860
) -> Result<(), FsProofStorageError> {
59-
if self.contains_proof(proof_facts.clone()).await? {
61+
if self.contains_proof(proof_facts.clone(), tx_hash).await? {
6062
return Ok(());
6163
}
62-
let facts_hash = proof_facts.hash();
63-
self.proof_storage.set_proof(facts_hash, proof.clone()).await?;
64-
self.cache.insert(facts_hash, proof);
64+
let key = (proof_facts.hash(), tx_hash);
65+
self.proof_storage.set_proof(key.0, tx_hash, proof.clone()).await?;
66+
self.cache.insert(key, proof);
6567
Ok(())
6668
}
6769

6870
pub async fn get_proof(
6971
&self,
7072
proof_facts: ProofFacts,
73+
tx_hash: TransactionHash,
7174
) -> Result<Option<Proof>, FsProofStorageError> {
72-
let facts_hash = proof_facts.hash();
75+
let key = (proof_facts.hash(), tx_hash);
7376
// Check cache first.
74-
if let Some(proof) = self.cache.get(&facts_hash) {
77+
if let Some(proof) = self.cache.get(&key) {
7578
return Ok(Some(proof));
7679
}
7780
// Fallback to filesystem.
78-
let proof = self.proof_storage.get_proof(facts_hash).await?;
81+
let proof = self.proof_storage.get_proof(key.0, tx_hash).await?;
7982
if let Some(proof) = &proof {
80-
self.cache.insert(facts_hash, proof.clone());
83+
self.cache.insert(key, proof.clone());
8184
}
8285
Ok(proof)
8386
}
8487

8588
pub async fn contains_proof(
8689
&self,
8790
proof_facts: ProofFacts,
91+
tx_hash: TransactionHash,
8892
) -> Result<bool, FsProofStorageError> {
89-
let facts_hash = proof_facts.hash();
93+
let key = (proof_facts.hash(), tx_hash);
9094
// Check cache first.
91-
if self.cache.contains(&facts_hash) {
95+
if self.cache.contains(&key) {
9296
return Ok(true);
9397
}
9498
// Fallback to filesystem.
95-
self.proof_storage.contains_proof(facts_hash).await
99+
self.proof_storage.contains_proof(key.0, tx_hash).await
96100
}
97101
}
98102

crates/apollo_proof_manager/src/proof_storage.rs

Lines changed: 64 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use std::error::Error;
22
use std::path::{Path, PathBuf};
33

44
use starknet_api::transaction::fields::Proof;
5+
use starknet_api::transaction::TransactionHash;
56
use starknet_types_core::felt::Felt;
67
use thiserror::Error;
78
use tokio::io::AsyncWriteExt;
@@ -13,9 +14,22 @@ mod proof_storage_test;
1314
#[async_trait::async_trait]
1415
pub trait ProofStorage: Send + Sync {
1516
type Error: Error;
16-
async fn set_proof(&self, facts_hash: Felt, proof: Proof) -> Result<(), Self::Error>;
17-
async fn get_proof(&self, facts_hash: Felt) -> Result<Option<Proof>, Self::Error>;
18-
async fn contains_proof(&self, facts_hash: Felt) -> Result<bool, Self::Error>;
17+
async fn set_proof(
18+
&self,
19+
facts_hash: Felt,
20+
tx_hash: TransactionHash,
21+
proof: Proof,
22+
) -> Result<(), Self::Error>;
23+
async fn get_proof(
24+
&self,
25+
facts_hash: Felt,
26+
tx_hash: TransactionHash,
27+
) -> Result<Option<Proof>, Self::Error>;
28+
async fn contains_proof(
29+
&self,
30+
facts_hash: Felt,
31+
tx_hash: TransactionHash,
32+
) -> Result<bool, Self::Error>;
1933
}
2034

2135
#[derive(Debug, Error)]
@@ -40,27 +54,30 @@ impl FsProofStorage {
4054
Ok(Self { persistent_root })
4155
}
4256

43-
/// Returns the directory that will hold the proof of a certain proof facts hash.
44-
/// For a proof facts hash: 0xa1b2c3d4... (rest of hash), the structure is:
57+
/// Returns the directory that will hold the proof of a certain proof facts hash and tx hash.
58+
/// For a proof facts hash: 0xa1b2c3d4... and tx hash, the structure is:
4559
/// a1/
4660
/// └── b2/
4761
/// └── a1b2c3d4.../
48-
fn get_proof_dir(&self, facts_hash: Felt) -> PathBuf {
49-
let facts_hash = hex::encode(facts_hash.to_bytes_be());
50-
let (first_msb_byte, second_msb_byte, _rest_of_bytes) =
51-
(&facts_hash[..2], &facts_hash[2..4], &facts_hash[4..]);
52-
PathBuf::from(first_msb_byte).join(second_msb_byte).join(facts_hash)
62+
/// └── <tx_hash_hex>/
63+
fn get_proof_dir(&self, facts_hash: Felt, tx_hash: TransactionHash) -> PathBuf {
64+
let facts_hash_hex = hex::encode(facts_hash.to_bytes_be());
65+
let (first_msb_byte, second_msb_byte, _rest) =
66+
(&facts_hash_hex[..2], &facts_hash_hex[2..4], &facts_hash_hex[4..]);
67+
let tx_hash_hex = hex::encode(tx_hash.0.to_bytes_be());
68+
PathBuf::from(first_msb_byte).join(second_msb_byte).join(&facts_hash_hex).join(tx_hash_hex)
5369
}
5470

55-
fn get_persistent_dir(&self, facts_hash: Felt) -> PathBuf {
56-
self.persistent_root.join(self.get_proof_dir(facts_hash))
71+
fn get_persistent_dir(&self, facts_hash: Felt, tx_hash: TransactionHash) -> PathBuf {
72+
self.persistent_root.join(self.get_proof_dir(facts_hash, tx_hash))
5773
}
5874

5975
async fn get_persistent_dir_with_create(
6076
&self,
6177
facts_hash: Felt,
78+
tx_hash: TransactionHash,
6279
) -> FsProofStorageResult<PathBuf> {
63-
let path = self.get_persistent_dir(facts_hash);
80+
let path = self.get_persistent_dir(facts_hash, tx_hash);
6481
if let Some(parent) = path.parent() {
6582
tokio::fs::create_dir_all(parent).await?;
6683
}
@@ -71,9 +88,10 @@ impl FsProofStorage {
7188
async fn create_tmp_dir(
7289
&self,
7390
facts_hash: Felt,
91+
tx_hash: TransactionHash,
7492
) -> FsProofStorageResult<(tempfile::TempDir, PathBuf)> {
75-
// Compute the final persistent directory for this `facts_hash`
76-
let persistent_dir = self.get_persistent_dir(facts_hash);
93+
// Compute the final persistent directory for this `facts_hash` and `tx_hash`
94+
let persistent_dir = self.get_persistent_dir(facts_hash, tx_hash);
7795
let parent_dir = persistent_dir
7896
.parent()
7997
.expect("Proof persistent dir should have a parent")
@@ -106,26 +124,32 @@ impl FsProofStorage {
106124
}
107125

108126
/// Reads a proof from a file in binary format.
109-
async fn read_proof_from_file(&self, facts_hash: Felt) -> FsProofStorageResult<Proof> {
110-
let file_path = self.get_persistent_dir(facts_hash).join("proof");
127+
async fn read_proof_from_file(
128+
&self,
129+
facts_hash: Felt,
130+
tx_hash: TransactionHash,
131+
) -> FsProofStorageResult<Proof> {
132+
let file_path = self.get_persistent_dir(facts_hash, tx_hash).join("proof");
111133
let buffer = tokio::fs::read(&file_path).await?;
112134
Ok(Proof::from(buffer))
113135
}
114136

115137
async fn write_proof_atomically(
116138
&self,
117139
facts_hash: Felt,
140+
tx_hash: TransactionHash,
118141
proof: Proof,
119142
) -> FsProofStorageResult<()> {
120143
// Write proof to a temporary directory.
121-
let (_tmp_root, tmp_dir) = self.create_tmp_dir(facts_hash).await?;
144+
let (_tmp_root, tmp_dir) = self.create_tmp_dir(facts_hash, tx_hash).await?;
122145
self.write_proof_to_file(&tmp_dir, &proof).await?;
123146

124147
// Atomically rename directory to persistent one.
125148
// If a concurrent write already placed the proof at the persistent path, the rename
126149
// will fail (e.g. ENOTEMPTY on Linux). Since proofs are deterministic for a given
127-
// facts_hash, the existing proof is identical and we can safely treat this as success.
128-
let persistent_dir = self.get_persistent_dir_with_create(facts_hash).await?;
150+
// facts_hash and tx_hash, the existing proof is identical and we can safely treat this as
151+
// success.
152+
let persistent_dir = self.get_persistent_dir_with_create(facts_hash, tx_hash).await?;
129153
match tokio::fs::rename(&tmp_dir, &persistent_dir).await {
130154
Ok(()) => Ok(()),
131155
Err(_)
@@ -142,16 +166,25 @@ impl FsProofStorage {
142166
impl ProofStorage for FsProofStorage {
143167
type Error = FsProofStorageError;
144168

145-
async fn set_proof(&self, facts_hash: Felt, proof: Proof) -> Result<(), Self::Error> {
146-
self.write_proof_atomically(facts_hash, proof).await
169+
async fn set_proof(
170+
&self,
171+
facts_hash: Felt,
172+
tx_hash: TransactionHash,
173+
proof: Proof,
174+
) -> Result<(), Self::Error> {
175+
self.write_proof_atomically(facts_hash, tx_hash, proof).await
147176
}
148177

149-
async fn get_proof(&self, facts_hash: Felt) -> Result<Option<Proof>, Self::Error> {
150-
if !self.contains_proof(facts_hash).await? {
178+
async fn get_proof(
179+
&self,
180+
facts_hash: Felt,
181+
tx_hash: TransactionHash,
182+
) -> Result<Option<Proof>, Self::Error> {
183+
if !self.contains_proof(facts_hash, tx_hash).await? {
151184
return Ok(None);
152185
}
153186

154-
match self.read_proof_from_file(facts_hash).await {
187+
match self.read_proof_from_file(facts_hash, tx_hash).await {
155188
Ok(proof) => Ok(Some(proof)),
156189
Err(FsProofStorageError::IoError(e)) if e.kind() == std::io::ErrorKind::NotFound => {
157190
Ok(None)
@@ -160,7 +193,11 @@ impl ProofStorage for FsProofStorage {
160193
}
161194
}
162195

163-
async fn contains_proof(&self, facts_hash: Felt) -> Result<bool, Self::Error> {
164-
Ok(tokio::fs::try_exists(self.get_persistent_dir(facts_hash)).await?)
196+
async fn contains_proof(
197+
&self,
198+
facts_hash: Felt,
199+
tx_hash: TransactionHash,
200+
) -> Result<bool, Self::Error> {
201+
Ok(tokio::fs::try_exists(self.get_persistent_dir(facts_hash, tx_hash)).await?)
165202
}
166203
}

0 commit comments

Comments
 (0)