2022-10-18 00:47:58 +03:00
|
|
|
//! Used by admins for health checks and inspecting global statistics.
|
|
|
|
//!
|
|
|
|
//! For ease of development, users can currently access these endponts.
|
|
|
|
//! They will eventually move to another port.
|
|
|
|
|
2023-05-13 21:13:02 +03:00
|
|
|
use super::{FrontendJsonResponseCache, FrontendResponseCacheKey};
|
|
|
|
use crate::{
|
|
|
|
app::{Web3ProxyApp, APP_USER_AGENT},
|
|
|
|
frontend::errors::Web3ProxyError,
|
|
|
|
};
|
|
|
|
use axum::{body::Bytes, http::StatusCode, response::IntoResponse, Extension};
|
2022-10-27 03:12:42 +03:00
|
|
|
use axum_macros::debug_handler;
|
2023-05-13 21:13:02 +03:00
|
|
|
use futures::Future;
|
|
|
|
use once_cell::sync::Lazy;
|
2022-09-10 05:59:07 +03:00
|
|
|
use serde_json::json;
|
2023-05-13 21:13:02 +03:00
|
|
|
use std::{sync::Arc, time::Duration};
|
|
|
|
use tokio::time::Instant;
|
|
|
|
|
|
|
|
static HEALTH_OK: Lazy<Bytes> = Lazy::new(|| Bytes::from("OK\n"));
|
|
|
|
static HEALTH_NOT_OK: Lazy<Bytes> = Lazy::new(|| Bytes::from(":(\n"));
|
|
|
|
|
|
|
|
static BACKUPS_NEEDED_TRUE: Lazy<Bytes> = Lazy::new(|| Bytes::from("true\n"));
|
|
|
|
static BACKUPS_NEEDED_FALSE: Lazy<Bytes> = Lazy::new(|| Bytes::from("false\n"));
|
|
|
|
|
|
|
|
/// simple ttl for
|
|
|
|
// TODO: make this generic for any cache/key
|
|
|
|
async fn _quick_cache_ttl<Fut>(
|
|
|
|
app: Arc<Web3ProxyApp>,
|
|
|
|
cache: Arc<FrontendJsonResponseCache>,
|
|
|
|
key: FrontendResponseCacheKey,
|
|
|
|
f: impl Fn(Arc<Web3ProxyApp>) -> Fut,
|
|
|
|
) -> (StatusCode, Bytes)
|
|
|
|
where
|
|
|
|
Fut: Future<Output = (StatusCode, Bytes)>,
|
|
|
|
{
|
|
|
|
let mut response;
|
|
|
|
let expire_at;
|
|
|
|
|
|
|
|
(response, expire_at) = cache
|
|
|
|
.get_or_insert_async::<Web3ProxyError>(&key, async {
|
|
|
|
let expire_at = Instant::now() + Duration::from_millis(1000);
|
|
|
|
|
|
|
|
let response = f(app.clone()).await;
|
|
|
|
|
|
|
|
Ok((response, expire_at))
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
|
|
|
if Instant::now() >= expire_at {
|
|
|
|
// TODO: this expiration isn't perfect
|
|
|
|
// parallel requests could overwrite eachother
|
|
|
|
// its good enough for now
|
|
|
|
let expire_at = Instant::now() + Duration::from_millis(1000);
|
|
|
|
|
|
|
|
response = f(app).await;
|
|
|
|
|
|
|
|
cache.insert(key, (response.clone(), expire_at));
|
|
|
|
}
|
|
|
|
|
|
|
|
response
|
|
|
|
}
|
2022-06-05 22:58:47 +03:00
|
|
|
|
2022-10-18 00:47:58 +03:00
|
|
|
/// Health check page for load balancers to use.
|
2022-10-20 01:26:33 +03:00
|
|
|
#[debug_handler]
|
2023-02-03 00:58:04 +03:00
|
|
|
pub async fn health(
|
|
|
|
Extension(app): Extension<Arc<Web3ProxyApp>>,
|
2023-05-13 21:13:02 +03:00
|
|
|
Extension(cache): Extension<Arc<FrontendJsonResponseCache>>,
|
2023-02-03 00:58:04 +03:00
|
|
|
) -> impl IntoResponse {
|
2023-05-13 21:13:02 +03:00
|
|
|
_quick_cache_ttl(app, cache, FrontendResponseCacheKey::Health, _health).await
|
|
|
|
}
|
2023-02-03 00:58:04 +03:00
|
|
|
|
2023-05-13 21:13:02 +03:00
|
|
|
async fn _health(app: Arc<Web3ProxyApp>) -> (StatusCode, Bytes) {
|
|
|
|
if app.balanced_rpcs.synced() {
|
|
|
|
(StatusCode::OK, HEALTH_OK.clone())
|
2022-06-29 21:22:53 +03:00
|
|
|
} else {
|
2023-05-13 21:13:02 +03:00
|
|
|
(StatusCode::SERVICE_UNAVAILABLE, HEALTH_NOT_OK.clone())
|
2022-06-29 21:22:53 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-14 03:15:01 +03:00
|
|
|
/// Easy alerting if backup servers are in use.
|
2023-05-13 21:13:02 +03:00
|
|
|
#[debug_handler]
|
|
|
|
pub async fn backups_needed(
|
|
|
|
Extension(app): Extension<Arc<Web3ProxyApp>>,
|
|
|
|
Extension(cache): Extension<Arc<FrontendJsonResponseCache>>,
|
|
|
|
) -> impl IntoResponse {
|
|
|
|
_quick_cache_ttl(
|
|
|
|
app,
|
|
|
|
cache,
|
|
|
|
FrontendResponseCacheKey::BackupsNeeded,
|
|
|
|
_backups_needed,
|
|
|
|
)
|
|
|
|
.await
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn _backups_needed(app: Arc<Web3ProxyApp>) -> (StatusCode, Bytes) {
|
2023-04-14 03:15:01 +03:00
|
|
|
let code = {
|
2023-05-13 01:15:32 +03:00
|
|
|
let consensus_rpcs = app
|
|
|
|
.balanced_rpcs
|
|
|
|
.watch_consensus_rpcs_sender
|
|
|
|
.borrow()
|
|
|
|
.clone();
|
2023-04-14 03:15:01 +03:00
|
|
|
|
2023-05-13 01:15:32 +03:00
|
|
|
if let Some(ref consensus_rpcs) = consensus_rpcs {
|
2023-04-14 03:15:01 +03:00
|
|
|
if consensus_rpcs.backups_needed {
|
|
|
|
StatusCode::INTERNAL_SERVER_ERROR
|
|
|
|
} else {
|
|
|
|
StatusCode::OK
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// if no consensus, we still "need backups". we just don't have any. which is worse
|
|
|
|
StatusCode::INTERNAL_SERVER_ERROR
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if matches!(code, StatusCode::OK) {
|
2023-05-13 21:13:02 +03:00
|
|
|
(code, BACKUPS_NEEDED_FALSE.clone())
|
2023-04-14 03:15:01 +03:00
|
|
|
} else {
|
2023-05-13 21:13:02 +03:00
|
|
|
(code, BACKUPS_NEEDED_TRUE.clone())
|
2023-04-14 03:15:01 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-18 00:47:58 +03:00
|
|
|
/// Very basic status page.
|
|
|
|
///
|
2022-08-05 22:22:23 +03:00
|
|
|
/// TODO: replace this with proper stats and monitoring
|
2022-10-20 01:26:33 +03:00
|
|
|
#[debug_handler]
|
2022-11-16 23:17:33 +03:00
|
|
|
pub async fn status(
|
|
|
|
Extension(app): Extension<Arc<Web3ProxyApp>>,
|
2023-05-13 21:13:02 +03:00
|
|
|
Extension(cache): Extension<Arc<FrontendJsonResponseCache>>,
|
2022-11-16 23:17:33 +03:00
|
|
|
) -> impl IntoResponse {
|
2023-05-13 21:13:02 +03:00
|
|
|
_quick_cache_ttl(app, cache, FrontendResponseCacheKey::Status, _status).await
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: this doesn't need to be async, but _quick_cache_ttl needs an async function
|
|
|
|
async fn _status(app: Arc<Web3ProxyApp>) -> (StatusCode, Bytes) {
|
|
|
|
// TODO: what else should we include? uptime, cache hit rates, cpu load, memory used
|
|
|
|
// TODO: the hostname is probably not going to change. only get once at the start?
|
|
|
|
let body = json!({
|
|
|
|
"version": APP_USER_AGENT,
|
|
|
|
"chain_id": app.config.chain_id,
|
|
|
|
"balanced_rpcs": app.balanced_rpcs,
|
|
|
|
"private_rpcs": app.private_rpcs,
|
|
|
|
"bundler_4337_rpcs": app.bundler_4337_rpcs,
|
|
|
|
"hostname": app.hostname,
|
|
|
|
});
|
|
|
|
|
|
|
|
let body = body.to_string().into_bytes();
|
|
|
|
|
|
|
|
let body = Bytes::from(body);
|
|
|
|
|
|
|
|
let code = if app.balanced_rpcs.synced() {
|
|
|
|
StatusCode::OK
|
|
|
|
} else {
|
|
|
|
StatusCode::INTERNAL_SERVER_ERROR
|
|
|
|
};
|
2022-09-10 05:59:07 +03:00
|
|
|
|
2023-05-13 21:13:02 +03:00
|
|
|
(code, body)
|
2022-06-05 22:58:47 +03:00
|
|
|
}
|