From 69fec2a5118ab7f4af741bb421afb12e8fee927e Mon Sep 17 00:00:00 2001 From: Alastair Ong Date: Mon, 20 Apr 2026 12:57:58 +0100 Subject: [PATCH] Add 15s TTL caching to order-by-owner and order-by-token endpoints Co-Authored-By: Claude Opus 4.6 --- src/error.rs | 6 +++++ src/main.rs | 5 ++++ src/routes/orders/get_by_owner.rs | 38 ++++++++++++++++++++++++------ src/routes/orders/get_by_token.rs | 39 +++++++++++++++++++++++++------ src/routes/orders/mod.rs | 3 +++ src/types/orders.rs | 4 +++- 6 files changed, 80 insertions(+), 15 deletions(-) diff --git a/src/error.rs b/src/error.rs index 72b222b..6408e55 100644 --- a/src/error.rs +++ b/src/error.rs @@ -91,6 +91,12 @@ impl<'r> Responder<'r, 'static> for ApiError { } } +impl From> for ApiError { + fn from(arc: std::sync::Arc) -> Self { + (*arc).clone() + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/main.rs b/src/main.rs index 4b6af71..03c5e9d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -125,10 +125,15 @@ pub(crate) fn rocket( let options = Options::Index | Options::NormalizeDirs; + let orders_by_token_cache = routes::orders::orders_by_token_cache(); + let orders_by_owner_cache = routes::orders::orders_by_owner_cache(); + Ok(rocket::custom(figment) .manage(pool) .manage(rate_limiter) .manage(raindex_config) + .manage(orders_by_token_cache) + .manage(orders_by_owner_cache) .mount("/", routes::health::routes()) .mount("/v1/tokens", routes::tokens::routes()) .mount("/v1/swap", routes::swap::routes()) diff --git a/src/routes/orders/get_by_owner.rs b/src/routes/orders/get_by_owner.rs index 129232a..972f21b 100644 --- a/src/routes/orders/get_by_owner.rs +++ b/src/routes/orders/get_by_owner.rs @@ -13,6 +13,18 @@ use rocket::serde::json::Json; use rocket::State; use tracing::Instrument; +use crate::cache::AppCache; +use std::time::Duration; + +const ORDERS_BY_OWNER_CACHE_TTL: Duration = Duration::from_secs(15); +const ORDERS_BY_OWNER_CACHE_CAPACITY: u64 = 1_000; + +pub(crate) type OrdersByOwnerCache = AppCache<(Address, u16, u16), OrdersListResponse>; + +pub(crate) fn orders_by_owner_cache() -> OrdersByOwnerCache { + AppCache::new(ORDERS_BY_OWNER_CACHE_CAPACITY, ORDERS_BY_OWNER_CACHE_TTL) +} + pub(crate) async fn process_get_orders_by_owner( ds: &dyn OrdersListDataSource, address: Address, @@ -71,6 +83,7 @@ pub async fn get_orders_by_address( _global: GlobalRateLimit, _key: AuthenticatedKey, shared_raindex: &State, + orders_cache: &State, span: TracingSpan, address: ValidatedAddress, params: OrdersPaginationParams, @@ -78,13 +91,24 @@ pub async fn get_orders_by_address( async move { tracing::info!(address = ?address, params = ?params, "request received"); let addr = address.0; - let page = params.page; - let page_size = params.page_size; - let raindex = shared_raindex.read().await; - let ds = RaindexOrdersListDataSource { - client: raindex.client(), - }; - let response = process_get_orders_by_owner(&ds, addr, page, page_size).await?; + let page = params.page.unwrap_or(1); + let page_size = params + .page_size + .unwrap_or(DEFAULT_PAGE_SIZE as u16) + .min(MAX_PAGE_SIZE); + let cache_key = (addr, page, page_size); + + let response = orders_cache + .get_or_try_insert(cache_key, || async { + let raindex = shared_raindex.read().await; + let ds = RaindexOrdersListDataSource { + client: raindex.client(), + }; + process_get_orders_by_owner(&ds, addr, Some(page), Some(page_size)).await + }) + .await + .map_err(ApiError::from)?; + Ok(Json(response)) } .instrument(span.0) diff --git a/src/routes/orders/get_by_token.rs b/src/routes/orders/get_by_token.rs index 68dcee0..f835aca 100644 --- a/src/routes/orders/get_by_token.rs +++ b/src/routes/orders/get_by_token.rs @@ -14,6 +14,19 @@ use rocket::serde::json::Json; use rocket::State; use tracing::Instrument; +use crate::cache::AppCache; +use std::time::Duration; + +const ORDERS_CACHE_TTL: Duration = Duration::from_secs(15); +const ORDERS_CACHE_CAPACITY: u64 = 1_000; + +pub(crate) type OrdersByTokenCache = + AppCache<(Address, Option, u16, u16), OrdersListResponse>; + +pub(crate) fn orders_by_token_cache() -> OrdersByTokenCache { + AppCache::new(ORDERS_CACHE_CAPACITY, ORDERS_CACHE_TTL) +} + pub(crate) async fn process_get_orders_by_token( ds: &dyn OrdersListDataSource, address: Address, @@ -88,6 +101,7 @@ pub async fn get_orders_by_token( _global: GlobalRateLimit, _key: AuthenticatedKey, shared_raindex: &State, + orders_cache: &State, span: TracingSpan, address: ValidatedAddress, params: OrdersByTokenParams, @@ -96,13 +110,24 @@ pub async fn get_orders_by_token( tracing::info!(address = ?address, params = ?params, "request received"); let addr = address.0; let side = params.side; - let page = params.page; - let page_size = params.page_size; - let raindex = shared_raindex.read().await; - let ds = RaindexOrdersListDataSource { - client: raindex.client(), - }; - let response = process_get_orders_by_token(&ds, addr, side, page, page_size).await?; + let page = params.page.unwrap_or(1); + let page_size = params + .page_size + .unwrap_or(DEFAULT_PAGE_SIZE as u16) + .min(MAX_PAGE_SIZE); + let cache_key = (addr, side, page, page_size); + + let response = orders_cache + .get_or_try_insert(cache_key, || async { + let raindex = shared_raindex.read().await; + let ds = RaindexOrdersListDataSource { + client: raindex.client(), + }; + process_get_orders_by_token(&ds, addr, side, Some(page), Some(page_size)).await + }) + .await + .map_err(ApiError::from)?; + Ok(Json(response)) } .instrument(span.0) diff --git a/src/routes/orders/mod.rs b/src/routes/orders/mod.rs index 760d8d4..6ab57da 100644 --- a/src/routes/orders/mod.rs +++ b/src/routes/orders/mod.rs @@ -319,6 +319,9 @@ pub use get_by_owner::*; pub use get_by_token::*; pub use get_by_tx::*; +pub(crate) use get_by_owner::{orders_by_owner_cache, OrdersByOwnerCache}; +pub(crate) use get_by_token::{orders_by_token_cache, OrdersByTokenCache}; + pub fn routes() -> Vec { rocket::routes![ get_by_tx::get_orders_by_tx, diff --git a/src/types/orders.rs b/src/types/orders.rs index 63151c7..6d9936f 100644 --- a/src/types/orders.rs +++ b/src/types/orders.rs @@ -16,7 +16,9 @@ pub struct OrdersPaginationParams { pub page_size: Option, } -#[derive(Debug, Clone, Serialize, Deserialize, FromFormField, ToSchema)] +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, FromFormField, ToSchema, +)] #[serde(rename_all = "camelCase")] pub enum OrderSide { Input,