Skip to content

Commit 9824ab5

Browse files
committed
perf(server): reuse SuiClient across requests
crates/server/src/server.rs:102 Store rpc_url in AppState and use tokio::sync::OnceCell to lazily initialize and reuse a single sui_sdk::SuiClient. - crates/server/src/server.rs:130 Add AppState::sui_client() and replace per-request SuiClientBuilder::build(...) calls with the cached client. - crates/server/src/server.rs:225 Simplify Axum state from (Arc<AppState>, Url) to Arc<AppState>, and update status, orderbook, deep_supply, and summary to use the cached client.
1 parent 1ba8951 commit 9824ab5

File tree

4 files changed

+299
-13
lines changed

4 files changed

+299
-13
lines changed

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/server/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ bigdecimal = "0.4.9"
3232
chrono = "0.4.42"
3333
thiserror = "1.0"
3434

35+
[dev-dependencies]
36+
rand = "0.8"
37+
futures = "0.3"
38+
3539
[[bin]]
3640
name = "deepbook-server"
3741
path = "src/main.rs"

crates/server/src/server.rs

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use std::time::{SystemTime, UNIX_EPOCH};
2828
use std::{collections::HashMap, net::SocketAddr};
2929
use sui_pg_db::DbArgs;
3030
use tokio::net::TcpListener;
31+
use tokio::sync::OnceCell;
3132
use tower_http::cors::{AllowMethods, Any, CorsLayer};
3233
use url::Url;
3334

@@ -106,6 +107,8 @@ pub const DEPOSITED_ASSETS_PATH: &str = "/deposited_assets/:balance_manager_ids"
106107
pub struct AppState {
107108
reader: Reader,
108109
metrics: Arc<RpcMetrics>,
110+
rpc_url: Url,
111+
sui_client: Arc<OnceCell<sui_sdk::SuiClient>>,
109112
deepbook_package_id: String,
110113
deep_token_package_id: String,
111114
deep_treasury_id: String,
@@ -116,6 +119,7 @@ impl AppState {
116119
database_url: Url,
117120
args: DbArgs,
118121
registry: &Registry,
122+
rpc_url: Url,
119123
deepbook_package_id: String,
120124
deep_token_package_id: String,
121125
deep_treasury_id: String,
@@ -125,11 +129,27 @@ impl AppState {
125129
Ok(Self {
126130
reader,
127131
metrics,
132+
rpc_url,
133+
sui_client: Arc::new(OnceCell::new()),
128134
deepbook_package_id,
129135
deep_token_package_id,
130136
deep_treasury_id,
131137
})
132138
}
139+
140+
/// Returns a reference to the shared SuiClient instance.
141+
/// Lazily initializes the client on first access and caches it for subsequent calls
142+
pub async fn sui_client(&self) -> Result<&sui_sdk::SuiClient, DeepBookError> {
143+
self.sui_client
144+
.get_or_try_init(|| async {
145+
SuiClientBuilder::default()
146+
.build(self.rpc_url.as_str())
147+
.await
148+
})
149+
.await
150+
.map_err(DeepBookError::from)
151+
}
152+
133153
pub(crate) fn metrics(&self) -> &RpcMetrics {
134154
&self.metrics
135155
}
@@ -178,6 +198,7 @@ pub async fn run_server(
178198
database_url,
179199
db_arg,
180200
metrics.registry(),
201+
rpc_url,
181202
deepbook_package_id,
182203
deep_token_package_id,
183204
deep_treasury_id,
@@ -192,15 +213,15 @@ pub async fn run_server(
192213
});
193214

194215
let listener = TcpListener::bind(socket_address).await?;
195-
axum::serve(listener, make_router(Arc::new(state), rpc_url))
216+
axum::serve(listener, make_router(Arc::new(state)))
196217
.with_graceful_shutdown(async move {
197218
cancellation_token.cancelled().await;
198219
})
199220
.await?;
200221

201222
Ok(())
202223
}
203-
pub(crate) fn make_router(state: Arc<AppState>, rpc_url: Url) -> Router {
224+
pub(crate) fn make_router(state: Arc<AppState>) -> Router {
204225
let cors = CorsLayer::new()
205226
.allow_methods(AllowMethods::list(vec![Method::GET, Method::OPTIONS]))
206227
.allow_headers(Any)
@@ -271,7 +292,7 @@ pub(crate) fn make_router(state: Arc<AppState>, rpc_url: Url) -> Router {
271292
.route(DEEP_SUPPLY_PATH, get(deep_supply))
272293
.route(SUMMARY_PATH, get(summary))
273294
.route(STATUS_PATH, get(status))
274-
.with_state((state.clone(), rpc_url));
295+
.with_state(state.clone());
275296

276297
db_routes
277298
.merge(rpc_routes)
@@ -286,13 +307,13 @@ async fn health_check() -> StatusCode {
286307
/// Get indexer status including checkpoint lag
287308
async fn status(
288309
Query(params): Query<StatusQueryParams>,
289-
State((state, rpc_url)): State<(Arc<AppState>, Url)>,
310+
State(state): State<Arc<AppState>>,
290311
) -> Result<Json<serde_json::Value>, DeepBookError> {
291312
// Get watermarks from the database
292313
let watermarks = state.reader.get_watermarks().await?;
293314

294315
// Get the latest checkpoint from Sui RPC
295-
let sui_client = SuiClientBuilder::default().build(rpc_url.as_str()).await?;
316+
let sui_client = state.sui_client().await?;
296317
let latest_checkpoint = sui_client
297318
.read_api()
298319
.get_latest_checkpoint_sequence_number()
@@ -680,7 +701,7 @@ async fn fetch_historical_volume(
680701

681702
#[allow(clippy::get_first)]
682703
async fn summary(
683-
State((state, rpc_url)): State<(Arc<AppState>, Url)>,
704+
State(state): State<Arc<AppState>>,
684705
) -> Result<Json<Vec<HashMap<String, Value>>>, DeepBookError> {
685706
// Fetch pools metadata first since it's required for other functions
686707
let pools = state.reader.get_pools().await?;
@@ -722,7 +743,7 @@ async fn summary(
722743
orderbook(
723744
Path(pool_name_clone),
724745
Query(HashMap::from([("level".to_string(), "1".to_string())])),
725-
State((state.clone(), rpc_url.clone())),
746+
State(state.clone()),
726747
)
727748
})
728749
.collect();
@@ -1287,7 +1308,7 @@ pub async fn assets(
12871308
async fn orderbook(
12881309
Path(pool_name): Path<String>,
12891310
Query(params): Query<HashMap<String, String>>,
1290-
State((state, rpc_url)): State<(Arc<AppState>, Url)>,
1311+
State(state): State<Arc<AppState>>,
12911312
) -> Result<Json<HashMap<String, Value>>, DeepBookError> {
12921313
let depth = params
12931314
.get("depth")
@@ -1341,7 +1362,7 @@ async fn orderbook(
13411362

13421363
let pool_address = ObjectID::from_hex_literal(&pool_id)?;
13431364

1344-
let sui_client = SuiClientBuilder::default().build(rpc_url.as_str()).await?;
1365+
let sui_client = state.sui_client().await?;
13451366
let mut ptb = ProgrammableTransactionBuilder::new();
13461367

13471368
let pool_object: SuiObjectResponse = sui_client
@@ -1497,10 +1518,8 @@ async fn orderbook(
14971518
}
14981519

14991520
/// DEEP total supply
1500-
async fn deep_supply(
1501-
State((state, rpc_url)): State<(Arc<AppState>, Url)>,
1502-
) -> Result<Json<u64>, DeepBookError> {
1503-
let sui_client = SuiClientBuilder::default().build(rpc_url.as_str()).await?;
1521+
async fn deep_supply(State(state): State<Arc<AppState>>) -> Result<Json<u64>, DeepBookError> {
1522+
let sui_client = state.sui_client().await?;
15041523
let mut ptb = ProgrammableTransactionBuilder::new();
15051524

15061525
let deep_treasury_object_id = ObjectID::from_hex_literal(&state.deep_treasury_id)?;

0 commit comments

Comments
 (0)