Skip to content

Commit 39e0031

Browse files
feat(router): cache ledger clients
Signed-off-by: Flavio Caetano Garcia <flavio.garcia@serpro.gov.br>
1 parent 315b8c5 commit 39e0031

5 files changed

Lines changed: 64 additions & 32 deletions

File tree

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ _Resources_ section. The [Docker for Mac](https://docs.docker.com/docker-for-mac
7979

8080
## Managing smart contracts
8181

82-
See [README.md](/smart_contracts/README.md).
82+
See [README.md](./smart_contracts/README.md).
8383

8484
## Client library
8585

vdr/src/client/ledger_router.rs

Lines changed: 54 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
use std::collections::HashMap;
1+
use std::{
2+
collections::HashMap,
3+
sync::{Arc, Mutex},
4+
};
25

36
use crate::{
47
client::LedgerClient,
@@ -28,7 +31,7 @@ impl Default for LedgerMode {
2831
#[derive(Debug)]
2932
pub enum LedgerResult {
3033
/// A fully initialized LedgerClient
31-
Client(LedgerClient),
34+
Client(Arc<LedgerClient>),
3235
/// A configuration-only LedgerClientConfig
3336
Config(LedgerClientConfig),
3437
}
@@ -67,6 +70,8 @@ pub struct LedgerRouter {
6770
configs: HashMap<String, LedgerClientConfig>,
6871
default_network: String,
6972
mode: LedgerMode,
73+
// cache thread-safe
74+
clients: Arc<Mutex<HashMap<String, Arc<LedgerClient>>>>,
7075
}
7176

7277
impl LedgerRouter {
@@ -88,6 +93,7 @@ impl LedgerRouter {
8893
configs,
8994
default_network: default_network.unwrap_or(DEFAULT_NETWORK).to_string(),
9095
mode: mode.unwrap_or_default(),
96+
clients: Arc::new(Mutex::new(HashMap::new())),
9197
}
9298
}
9399

@@ -101,10 +107,15 @@ impl LedgerRouter {
101107
pub fn get_ledger_for_identifier(&self, identifier: &str) -> VdrResult<LedgerResult> {
102108
let network = Self::extract_network(identifier)?;
103109

104-
let config = self
110+
let (resolved_network, config) = self
105111
.configs
106112
.get(&network)
107-
.or_else(|| self.configs.get(&self.default_network))
113+
.map(|c| (network.clone(), c))
114+
.or_else(|| {
115+
self.configs
116+
.get(&self.default_network)
117+
.map(|c| (self.default_network.clone(), c))
118+
})
108119
.ok_or_else(|| {
109120
VdrError::RouterConfigError(format!(
110121
"Ledger for network '{}' not configured and no default network defined",
@@ -114,13 +125,23 @@ impl LedgerRouter {
114125

115126
match self.mode {
116127
LedgerMode::LedgerClient => {
117-
let client = LedgerClient::new(
128+
// cache lookup
129+
let mut cache = self.clients.lock().unwrap();
130+
131+
if let Some(client) = cache.get(&resolved_network).cloned() {
132+
return Ok(LedgerResult::Client(client));
133+
}
134+
135+
let client = Arc::new(LedgerClient::new(
118136
config.chain_id,
119137
&config.rpc_node,
120138
&config.contract_configs,
121139
config.network.as_deref(),
122140
config.quorum_config.as_ref(),
123-
)?;
141+
)?);
142+
143+
cache.insert(resolved_network.clone(), client.clone());
144+
124145
Ok(LedgerResult::Client(client))
125146
}
126147
LedgerMode::ConfigOnly => Ok(LedgerResult::Config(config.clone())),
@@ -151,14 +172,20 @@ impl LedgerRouter {
151172
)));
152173
}
153174

154-
match parts.len() {
155-
3 => Ok(DEFAULT_NETWORK.to_string()),
156-
n if n >= 4 => Ok(parts[2].to_string()),
157-
_ => Err(VdrError::RouterConfigError(format!(
158-
"Invalid did:ethr format: {}",
159-
identifier
160-
))),
175+
if parts.len() == 3 {
176+
return Ok(DEFAULT_NETWORK.to_string());
161177
}
178+
179+
if parts.len() >= 4 {
180+
return parts.get(2).map(|s| s.to_string()).ok_or_else(|| {
181+
VdrError::RouterConfigError(format!("Invalid did:ethr format: {}", identifier))
182+
});
183+
}
184+
185+
Err(VdrError::RouterConfigError(format!(
186+
"Invalid did:ethr format: {}",
187+
identifier
188+
)))
162189
}
163190

164191
/// Submits a transaction to a ledger based on its identifier
@@ -194,16 +221,14 @@ impl LedgerRouter {
194221
}
195222

196223
let mut results = HashMap::new();
197-
for (network, config) in self.configs.iter() {
198-
let client = LedgerClient::new(
199-
config.chain_id,
200-
&config.rpc_node,
201-
&config.contract_configs,
202-
config.network.as_deref(),
203-
config.quorum_config.as_ref(),
204-
)?;
205-
let ping_status = client.ping().await?;
206-
results.insert(network.clone(), ping_status);
224+
for (network, _) in self.configs.iter() {
225+
match self.get_ledger_for_identifier(&format!("did:ethr:{}:0x0", network))? {
226+
LedgerResult::Client(client) => {
227+
let ping_status = client.ping().await?;
228+
results.insert(network.clone(), ping_status);
229+
}
230+
_ => {}
231+
}
207232
}
208233
Ok(results)
209234
}
@@ -304,4 +329,10 @@ mod tests {
304329
LedgerResult::Config(_) => panic!("Expected LedgerResult::Client, got Config"),
305330
}
306331
}
332+
333+
#[test]
334+
fn test_invalid_did_format() {
335+
let err = LedgerRouter::extract_network("invalid").unwrap_err();
336+
assert!(matches!(err, VdrError::RouterConfigError(_)));
337+
}
307338
}

vdr/uniffi/src/ffi/client.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) 2024 DSR Corporation, Denver, Colorado.
22
// https://www.dsr-corporation.com
33
// SPDX-License-Identifier: Apache-2.0
4+
use std::sync::Arc;
45

56
use crate::{
67
ffi::{
@@ -15,7 +16,7 @@ use indy_besu_vdr::{ContractConfig as ContractConfig_, LedgerClient as LedgerCli
1516

1617
#[derive(uniffi::Object)]
1718
pub struct LedgerClient {
18-
pub client: LedgerClient_,
19+
pub client: Arc<LedgerClient_>,
1920
}
2021

2122
#[uniffi::export(async_runtime = "tokio")]
@@ -33,13 +34,13 @@ impl LedgerClient {
3334
.map(ContractConfig::into)
3435
.collect();
3536
let quorum_config = quorum_config.map(QuorumConfig::into);
36-
let client = LedgerClient_::new(
37+
let client = Arc::new(LedgerClient_::new(
3738
chain_id,
3839
&node_address,
3940
&contract_configs,
4041
network.as_deref(),
4142
quorum_config.as_ref(),
42-
)?;
43+
)?);
4344
Ok(LedgerClient { client })
4445
}
4546

vdr/uniffi/src/ffi/ledger_router.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ impl LedgerRouter {
103103
ledgers.insert(network_name, ledger_config);
104104
}
105105

106-
let default_network = default_network_name.unwrap_or_else(|| "default".to_string());
106+
let default_network = default_network_name.unwrap_or_else(|| DEFAULT_NETWORK.to_string());
107107

108108
// Initialize the underlying LedgerRouter (client mode)
109109
let router = LedgerRouter_::new(
@@ -131,9 +131,9 @@ impl LedgerRouter {
131131
let ledger_result = self.router.get_ledger_for_identifier(identifier)?;
132132

133133
let client = match ledger_result {
134-
LedgerResult::Client(c) => c,
134+
LedgerResult::Client(c) => c.clone(),
135135
LedgerResult::Config(_) => {
136-
return Err(VdrError::ClientInvalidResponse {
136+
return Err(VdrError::RouterConfigError {
137137
msg: "Expected LedgerClient but got config".to_string(),
138138
});
139139
}

vdr/wrappers/python/demo/test-multiledger.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ async def demo():
7575
status = await router.ping_all()
7676
for ledger_name, ping_status in status.items():
7777
s = ping_status.status
78-
# tenta pegar o nome do enum, senão imprime o próprio objeto
78+
# Try to get the enum name; otherwise fallback to the object itself
7979
status_name = getattr(s, "name", str(s))
8080
print(f" {ledger_name}: {{'status': '{status_name}'}}")
8181

@@ -95,7 +95,7 @@ async def demo():
9595
receipt = await client.get_receipt(txn_hash)
9696
print(' Transaction receipt: ' + receipt)
9797

98-
print("3. Resolve DID Document:" +network1)
98+
print("3. Resolve DID Document:" +network2)
9999
client = router.get_ledger_for_identifier(did)
100100
print("Router Return:" +client.network())
101101
resolved_did_doc = await DidResolver.resolve_did(client, did, None)

0 commit comments

Comments
 (0)