diff --git a/api/src/context.rs b/api/src/context.rs index f4053c76eecb9..3f8f781410b9c 100644 --- a/api/src/context.rs +++ b/api/src/context.rs @@ -252,7 +252,7 @@ impl Context { } pub fn health_check_route(&self) -> BoxedFilter<(impl Reply,)> { - diem_json_rpc::runtime::health_check_route(self.db.clone()) + super::health_check::health_check_route(self.db.clone()) } pub fn jsonrpc_routes(&self) -> BoxedFilter<(impl Reply,)> { diff --git a/api/src/health_check.rs b/api/src/health_check.rs new file mode 100644 index 0000000000000..31fc18ad2a366 --- /dev/null +++ b/api/src/health_check.rs @@ -0,0 +1,64 @@ +// Copyright (c) The Diem Core Contributors +// SPDX-License-Identifier: Apache-2.0 + +use anyhow::{ensure, Result}; +use std::{ + ops::Sub, + sync::Arc, + time::{Duration, SystemTime, UNIX_EPOCH}, +}; +use storage_interface::MoveDbReader; +use warp::{filters::BoxedFilter, reject, Filter, Reply}; + +// HealthCheckParams is optional params for different layer's health check. +// If no param is provided, server return 200 by default to indicate HTTP server is running health. +#[derive(serde::Deserialize)] +struct HealthCheckParams { + // Health check returns 200 when this param is provided and meet the following condition: + // server latest ledger info timestamp >= server current time timestamp - duration_secs + pub duration_secs: Option, +} + +#[derive(Debug)] +struct HealthCheckError; +impl reject::Reject for HealthCheckError {} + +pub fn health_check_route(health_diem_db: Arc) -> BoxedFilter<(impl Reply,)> { + warp::path!("-" / "healthy") + .and(warp::path::end()) + .and(warp::query().map(move |params: HealthCheckParams| params)) + .and(warp::any().map(move || health_diem_db.clone())) + .and(warp::any().map(SystemTime::now)) + .and_then(health_check) + .boxed() +} + +async fn health_check( + params: HealthCheckParams, + db: Arc, + now: SystemTime, +) -> Result, warp::Rejection> { + if let Some(duration) = params.duration_secs { + let ledger_info = db + .get_latest_ledger_info() + .map_err(|_| reject::custom(HealthCheckError))?; + let timestamp = ledger_info.ledger_info().timestamp_usecs(); + + check_latest_ledger_info_timestamp(duration, timestamp, now) + .map_err(|_| reject::custom(HealthCheckError))?; + } + Ok(Box::new("diem-node:ok")) +} + +pub fn check_latest_ledger_info_timestamp( + duration_sec: u64, + timestamp_usecs: u64, + now: SystemTime, +) -> Result<()> { + let timestamp = Duration::from_micros(timestamp_usecs); + let expectation = now + .sub(Duration::from_secs(duration_sec)) + .duration_since(UNIX_EPOCH)?; + ensure!(timestamp >= expectation); + Ok(()) +} diff --git a/api/src/lib.rs b/api/src/lib.rs index e8b8bc0583657..047d6862e4d0c 100644 --- a/api/src/lib.rs +++ b/api/src/lib.rs @@ -4,6 +4,7 @@ mod accounts; mod context; mod events; +mod health_check; mod index; pub(crate) mod log; mod metrics; diff --git a/api/src/runtime.rs b/api/src/runtime.rs index 1fd87429b0c1d..444d6f407ab58 100644 --- a/api/src/runtime.rs +++ b/api/src/runtime.rs @@ -113,7 +113,6 @@ mod tests { use diem_config::config::NodeConfig; use diem_types::chain_id::ChainId; - use serde_json::json; use crate::{ runtime::bootstrap, diff --git a/api/src/tests/index_test.rs b/api/src/tests/index_test.rs index 2068296153660..99a77af5e88cf 100644 --- a/api/src/tests/index_test.rs +++ b/api/src/tests/index_test.rs @@ -51,18 +51,6 @@ async fn test_health_check() { assert_eq!(resp.status(), 200) } -#[tokio::test] -async fn test_enable_jsonrpc_api() { - let context = new_test_context(); - let resp = context - .post( - "/", - json!({"jsonrpc": "2.0", "method": "get_metadata", "id": 1}), - ) - .await; - assert_eq!(resp["result"]["version"].as_u64().unwrap(), 0) -} - #[tokio::test] async fn test_openapi_spec() { let context = new_test_context(); diff --git a/api/src/tests/test_context.rs b/api/src/tests/test_context.rs index 3d749733a8737..c14fc0c778fa5 100644 --- a/api/src/tests/test_context.rs +++ b/api/src/tests/test_context.rs @@ -7,7 +7,7 @@ use diem_api_types::{ mime_types, HexEncodedBytes, TransactionOnChainData, X_DIEM_CHAIN_ID, X_DIEM_LEDGER_TIMESTAMP, X_DIEM_LEDGER_VERSION, }; -use diem_config::config::{ApiConfig, JsonRpcConfig, RoleType}; +use diem_config::config::{ApiConfig, RoleType}; use diem_crypto::{hash::HashValue, SigningKey}; use diem_genesis_tool::validator_builder::{RootKeys, ValidatorBuilder}; use diem_global_constants::OWNER_ACCOUNT; @@ -74,7 +74,6 @@ pub fn new_test_context() -> TestContext { db.clone(), mempool.ac_client.clone(), RoleType::Validator, - JsonRpcConfig::default(), ApiConfig::default(), ), rng,