From 1ebd7ac9574ec3fb87e53d8e77fb119a889c5f10 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 3 Apr 2025 17:25:33 +0000 Subject: [PATCH 01/10] docs: clarify ready endpoint checks for fully hydrated cache Co-Authored-By: Tejas Badadare --- apps/hermes/server/src/api/rest/ready.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/apps/hermes/server/src/api/rest/ready.rs b/apps/hermes/server/src/api/rest/ready.rs index 92b538440d..1ea62798ac 100644 --- a/apps/hermes/server/src/api/rest/ready.rs +++ b/apps/hermes/server/src/api/rest/ready.rs @@ -8,6 +8,15 @@ use { }, }; +/// Endpoint that returns OK (200) only when the cache is fully hydrated. +/// +/// The cache is considered fully hydrated when all of the following conditions are met: +/// - `has_completed_recently`: The latest completed update is recent (within the staleness threshold) +/// - `is_not_behind`: The latest completed slot isn't too far behind the latest observed slot +/// - `is_metadata_loaded`: Price feeds metadata is not empty +/// +/// If any of these conditions are not met, the endpoint returns SERVICE_UNAVAILABLE (503) +/// along with detailed metadata about the readiness state. pub async fn ready(State(state): State>) -> Response where S: Aggregates, From b82542e8782fdc2bcc28b3e225606823fc255645 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 3 Apr 2025 17:27:39 +0000 Subject: [PATCH 02/10] style: fix formatting issue in ready.rs Co-Authored-By: Tejas Badadare --- apps/hermes/server/src/api/rest/ready.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/hermes/server/src/api/rest/ready.rs b/apps/hermes/server/src/api/rest/ready.rs index 1ea62798ac..98ef1c7ee8 100644 --- a/apps/hermes/server/src/api/rest/ready.rs +++ b/apps/hermes/server/src/api/rest/ready.rs @@ -9,7 +9,7 @@ use { }; /// Endpoint that returns OK (200) only when the cache is fully hydrated. -/// +/// /// The cache is considered fully hydrated when all of the following conditions are met: /// - `has_completed_recently`: The latest completed update is recent (within the staleness threshold) /// - `is_not_behind`: The latest completed slot isn't too far behind the latest observed slot From 0d673e4e92ca4f761704dc91c043f34082701d6d Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 3 Apr 2025 17:33:50 +0000 Subject: [PATCH 03/10] feat: add is_ready method to Cache trait and update ready endpoint Co-Authored-By: Tejas Badadare --- apps/hermes/server/src/api/rest/ready.rs | 20 +++++++++++++++----- apps/hermes/server/src/state/cache.rs | 8 ++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/apps/hermes/server/src/api/rest/ready.rs b/apps/hermes/server/src/api/rest/ready.rs index 98ef1c7ee8..401c0307a0 100644 --- a/apps/hermes/server/src/api/rest/ready.rs +++ b/apps/hermes/server/src/api/rest/ready.rs @@ -1,11 +1,12 @@ use { - crate::{api::ApiState, state::aggregate::Aggregates}, + crate::{api::ApiState, state::{aggregate::Aggregates, cache::Cache}}, axum::{ extract::State, http::StatusCode, response::{IntoResponse, Response}, Json, }, + serde_json::json, }; /// Endpoint that returns OK (200) only when the cache is fully hydrated. @@ -19,11 +20,20 @@ use { /// along with detailed metadata about the readiness state. pub async fn ready(State(state): State>) -> Response where - S: Aggregates, + S: Aggregates + Cache, { let state = &*state.state; - match Aggregates::is_ready(state).await { - (true, _) => (StatusCode::OK, "OK").into_response(), - (false, metadata) => (StatusCode::SERVICE_UNAVAILABLE, Json(metadata)).into_response(), + let (aggregates_ready, metadata) = Aggregates::is_ready(state).await; + let cache_ready = Cache::is_ready(state).await; + + if aggregates_ready && cache_ready { + (StatusCode::OK, "OK").into_response() + } else { + let response_metadata = json!({ + "aggregates_ready": aggregates_ready, + "cache_ready": cache_ready, + "details": metadata + }); + (StatusCode::SERVICE_UNAVAILABLE, Json(response_metadata)).into_response() } } diff --git a/apps/hermes/server/src/state/cache.rs b/apps/hermes/server/src/state/cache.rs index 8a0c1162af..692302cbce 100644 --- a/apps/hermes/server/src/state/cache.rs +++ b/apps/hermes/server/src/state/cache.rs @@ -136,6 +136,8 @@ pub trait Cache { request_time: RequestTime, filter: MessageStateFilter, ) -> Result>; + /// + async fn is_ready(&self) -> bool; } #[async_trait::async_trait] @@ -274,6 +276,12 @@ where let cache = self.into().wormhole_merkle_state_cache.read().await; Ok(cache.get(&slot).cloned()) } + + /// + async fn is_ready(&self) -> bool { + let message_cache = self.into().message_cache.read().await; + !message_cache.is_empty() + } } async fn retrieve_message_state( From 7c7c7162226be217a335434a87e95d85f19e7af5 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 3 Apr 2025 17:36:12 +0000 Subject: [PATCH 04/10] fix: rename is_ready to is_cache_ready to avoid name conflict Co-Authored-By: Tejas Badadare --- apps/hermes/server/src/api/rest/ready.rs | 2 +- apps/hermes/server/src/state/cache.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/hermes/server/src/api/rest/ready.rs b/apps/hermes/server/src/api/rest/ready.rs index 401c0307a0..1f07c3eb3d 100644 --- a/apps/hermes/server/src/api/rest/ready.rs +++ b/apps/hermes/server/src/api/rest/ready.rs @@ -24,7 +24,7 @@ where { let state = &*state.state; let (aggregates_ready, metadata) = Aggregates::is_ready(state).await; - let cache_ready = Cache::is_ready(state).await; + let cache_ready = Cache::is_cache_ready(state).await; if aggregates_ready && cache_ready { (StatusCode::OK, "OK").into_response() diff --git a/apps/hermes/server/src/state/cache.rs b/apps/hermes/server/src/state/cache.rs index 692302cbce..d555bc20f0 100644 --- a/apps/hermes/server/src/state/cache.rs +++ b/apps/hermes/server/src/state/cache.rs @@ -137,7 +137,7 @@ pub trait Cache { filter: MessageStateFilter, ) -> Result>; /// - async fn is_ready(&self) -> bool; + async fn is_cache_ready(&self) -> bool; } #[async_trait::async_trait] @@ -278,7 +278,7 @@ where } /// - async fn is_ready(&self) -> bool { + async fn is_cache_ready(&self) -> bool { let message_cache = self.into().message_cache.read().await; !message_cache.is_empty() } From 41547f99083db9b6772247dff91909387db36958 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 3 Apr 2025 17:39:24 +0000 Subject: [PATCH 05/10] style: fix formatting issues to pass CI Co-Authored-By: Tejas Badadare --- apps/hermes/server/src/api/rest/ready.rs | 7 +++++-- apps/hermes/server/src/state/cache.rs | 4 ++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/apps/hermes/server/src/api/rest/ready.rs b/apps/hermes/server/src/api/rest/ready.rs index 1f07c3eb3d..b544a2a883 100644 --- a/apps/hermes/server/src/api/rest/ready.rs +++ b/apps/hermes/server/src/api/rest/ready.rs @@ -1,5 +1,8 @@ use { - crate::{api::ApiState, state::{aggregate::Aggregates, cache::Cache}}, + crate::{ + api::ApiState, + state::{aggregate::Aggregates, cache::Cache}, + }, axum::{ extract::State, http::StatusCode, @@ -25,7 +28,7 @@ where let state = &*state.state; let (aggregates_ready, metadata) = Aggregates::is_ready(state).await; let cache_ready = Cache::is_cache_ready(state).await; - + if aggregates_ready && cache_ready { (StatusCode::OK, "OK").into_response() } else { diff --git a/apps/hermes/server/src/state/cache.rs b/apps/hermes/server/src/state/cache.rs index d555bc20f0..55208525b2 100644 --- a/apps/hermes/server/src/state/cache.rs +++ b/apps/hermes/server/src/state/cache.rs @@ -136,7 +136,7 @@ pub trait Cache { request_time: RequestTime, filter: MessageStateFilter, ) -> Result>; - /// + /// async fn is_cache_ready(&self) -> bool; } @@ -277,7 +277,7 @@ where Ok(cache.get(&slot).cloned()) } - /// + /// async fn is_cache_ready(&self) -> bool { let message_cache = self.into().message_cache.read().await; !message_cache.is_empty() From fbcef36e914339732ecce3df6baa8d5507b7255a Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 3 Apr 2025 17:41:39 +0000 Subject: [PATCH 06/10] docs: add documentation for is_cache_ready method Co-Authored-By: Tejas Badadare --- apps/hermes/server/src/state/cache.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/apps/hermes/server/src/state/cache.rs b/apps/hermes/server/src/state/cache.rs index 55208525b2..a15434e3aa 100644 --- a/apps/hermes/server/src/state/cache.rs +++ b/apps/hermes/server/src/state/cache.rs @@ -136,7 +136,6 @@ pub trait Cache { request_time: RequestTime, filter: MessageStateFilter, ) -> Result>; - /// async fn is_cache_ready(&self) -> bool; } @@ -277,7 +276,6 @@ where Ok(cache.get(&slot).cloned()) } - /// async fn is_cache_ready(&self) -> bool { let message_cache = self.into().message_cache.read().await; !message_cache.is_empty() From d646c0609a8df72f921fda0d1e10b5ebba954d1d Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 3 Apr 2025 18:04:14 +0000 Subject: [PATCH 07/10] feat: update is_cache_ready to check if cache is at least 90% full Co-Authored-By: Tejas Badadare --- apps/hermes/server/src/state/cache.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/hermes/server/src/state/cache.rs b/apps/hermes/server/src/state/cache.rs index a15434e3aa..aec43b09dd 100644 --- a/apps/hermes/server/src/state/cache.rs +++ b/apps/hermes/server/src/state/cache.rs @@ -276,9 +276,12 @@ where Ok(cache.get(&slot).cloned()) } + /// async fn is_cache_ready(&self) -> bool { let message_cache = self.into().message_cache.read().await; - !message_cache.is_empty() + let cache_size = self.into().cache_size as usize; + + message_cache.len() >= (cache_size * 9 / 10) } } From f3e11fdff94f3c69bc8c4b92098aecb7fc12cb08 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Thu, 3 Apr 2025 18:23:55 +0000 Subject: [PATCH 08/10] docs: complete docstring for is_cache_ready method Co-Authored-By: Tejas Badadare --- apps/hermes/server/src/state/cache.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/hermes/server/src/state/cache.rs b/apps/hermes/server/src/state/cache.rs index aec43b09dd..bc126b8c0e 100644 --- a/apps/hermes/server/src/state/cache.rs +++ b/apps/hermes/server/src/state/cache.rs @@ -136,6 +136,10 @@ pub trait Cache { request_time: RequestTime, filter: MessageStateFilter, ) -> Result>; + /// Checks if the cache contains sufficient data to serve requests. + /// + /// The cache is considered ready when it is at or near its maximum capacity. + async fn is_cache_ready(&self) -> bool; } From 65b4445642e922f61ab3e20215db7562d2f2e2d9 Mon Sep 17 00:00:00 2001 From: Tejas Badadare <17058023+tejasbadadare@users.noreply.github.com> Date: Thu, 3 Apr 2025 11:32:18 -0700 Subject: [PATCH 09/10] doc: add docstring --- apps/hermes/server/src/state/cache.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/apps/hermes/server/src/state/cache.rs b/apps/hermes/server/src/state/cache.rs index bc126b8c0e..36134bd284 100644 --- a/apps/hermes/server/src/state/cache.rs +++ b/apps/hermes/server/src/state/cache.rs @@ -135,11 +135,7 @@ pub trait Cache { ids: Vec, request_time: RequestTime, filter: MessageStateFilter, - ) -> Result>; - /// Checks if the cache contains sufficient data to serve requests. - /// - /// The cache is considered ready when it is at or near its maximum capacity. - + ) -> Result>; async fn is_cache_ready(&self) -> bool; } @@ -280,7 +276,13 @@ where Ok(cache.get(&slot).cloned()) } + /// Checks if the cache contains sufficient data to serve requests. + /// Specifically, TWAP requests -- to serve a TWAP of X seconds, there + /// needs to be X seconds of data in the cache. We need to serve TWAPs + /// from the cache since TwapMessages only exist in Hermes at the moment, + /// not Benchmarks. /// + /// The cache is considered ready when it is at or near its maximum capacity. async fn is_cache_ready(&self) -> bool { let message_cache = self.into().message_cache.read().await; let cache_size = self.into().cache_size as usize; From 692a293c3d32da691c07b14464afcf4910c9655f Mon Sep 17 00:00:00 2001 From: Tejas Badadare Date: Thu, 3 Apr 2025 11:45:50 -0700 Subject: [PATCH 10/10] fix: use slots to measure readiness --- apps/hermes/server/Cargo.lock | 2 +- apps/hermes/server/Cargo.toml | 2 +- apps/hermes/server/src/state/cache.rs | 20 ++++++++++---------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/apps/hermes/server/Cargo.lock b/apps/hermes/server/Cargo.lock index b853a87f6a..358f91f3f3 100644 --- a/apps/hermes/server/Cargo.lock +++ b/apps/hermes/server/Cargo.lock @@ -1868,7 +1868,7 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "hermes" -version = "0.8.5" +version = "0.8.6" dependencies = [ "anyhow", "async-trait", diff --git a/apps/hermes/server/Cargo.toml b/apps/hermes/server/Cargo.toml index 7c69e2a323..5c8f3921c9 100644 --- a/apps/hermes/server/Cargo.toml +++ b/apps/hermes/server/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hermes" -version = "0.8.5" +version = "0.8.6" description = "Hermes is an agent that provides Verified Prices from the Pythnet Pyth Oracle." edition = "2021" diff --git a/apps/hermes/server/src/state/cache.rs b/apps/hermes/server/src/state/cache.rs index 36134bd284..1dffe7970f 100644 --- a/apps/hermes/server/src/state/cache.rs +++ b/apps/hermes/server/src/state/cache.rs @@ -135,7 +135,7 @@ pub trait Cache { ids: Vec, request_time: RequestTime, filter: MessageStateFilter, - ) -> Result>; + ) -> Result>; async fn is_cache_ready(&self) -> bool; } @@ -276,18 +276,18 @@ where Ok(cache.get(&slot).cloned()) } - /// Checks if the cache contains sufficient data to serve requests. - /// Specifically, TWAP requests -- to serve a TWAP of X seconds, there + /// Checks if the cache contains sufficient data to serve requests. + /// Specifically, TWAP requests -- to serve a TWAP of X seconds, there /// needs to be X seconds of data in the cache. We need to serve TWAPs - /// from the cache since TwapMessages only exist in Hermes at the moment, + /// from the cache since TwapMessages only exist in Hermes at the moment, /// not Benchmarks. - /// - /// The cache is considered ready when it is at or near its maximum capacity. + /// + /// The cache is considered ready when it is at or near its maximum slot capacity. async fn is_cache_ready(&self) -> bool { - let message_cache = self.into().message_cache.read().await; - let cache_size = self.into().cache_size as usize; - - message_cache.len() >= (cache_size * 9 / 10) + let message_cache = self.into().accumulator_messages_cache.read().await; + let max_cache_size = self.into().cache_size as usize; + + message_cache.len() >= (max_cache_size * 9 / 10) } }