Constellation, Spacedust, Slingshot, UFOs: atproto crates and services for microcosm

healthcheck

Changed files
+58 -2
slingshot
+8
slingshot/src/error.rs
···
}
#[derive(Debug, Error)]
+
pub enum HealthCheckError {
+
#[error("failed to send checkin: {0}")]
+
HealthCheckError(#[from] reqwest::Error),
+
}
+
+
#[derive(Debug, Error)]
pub enum MainTaskError {
#[error(transparent)]
ConsumerTaskError(#[from] ConsumerError),
···
ServerTaskError(#[from] ServerError),
#[error(transparent)]
IdentityTaskError(#[from] IdentityError),
+
#[error(transparent)]
+
HealthCheckError(#[from] HealthCheckError),
#[error("firehose cache failed to close: {0}")]
FirehoseCacheCloseError(foyer::Error),
}
+32
slingshot/src/healthcheck.rs
···
+
use crate::error::HealthCheckError;
+
use reqwest::Client;
+
use std::time::Duration;
+
use tokio::time::sleep;
+
use tokio_util::sync::CancellationToken;
+
+
pub async fn healthcheck(
+
endpoint: String,
+
shutdown: CancellationToken,
+
) -> Result<(), HealthCheckError> {
+
let client = Client::builder()
+
.user_agent(format!(
+
"microcosm slingshot v{} (dev: @bad-example.com)",
+
env!("CARGO_PKG_VERSION")
+
))
+
.no_proxy()
+
.timeout(Duration::from_secs(10))
+
.build()?;
+
+
loop {
+
tokio::select! {
+
res = client.get(&endpoint).send() => {
+
let _ = res
+
.and_then(|r| r.error_for_status())
+
.inspect_err(|e| log::error!("failed to send healthcheck: {e}"));
+
},
+
_ = shutdown.cancelled() => break,
+
}
+
sleep(Duration::from_secs(51)).await;
+
}
+
Ok(())
+
}
+2
slingshot/src/lib.rs
···
mod consumer;
pub mod error;
mod firehose_cache;
+
mod healthcheck;
mod identity;
mod record;
mod server;
pub use consumer::consume;
pub use firehose_cache::firehose_cache;
+
pub use healthcheck::healthcheck;
pub use identity::Identity;
pub use record::{CachedRecord, ErrorResponseObject, Repo};
pub use server::serve;
+14 -1
slingshot/src/main.rs
···
// use foyer::HybridCache;
// use foyer::{Engine, DirectFsDeviceOptions, HybridCacheBuilder};
use metrics_exporter_prometheus::PrometheusBuilder;
-
use slingshot::{Identity, Repo, consume, error::MainTaskError, firehose_cache, serve};
+
use slingshot::{
+
Identity, Repo, consume, error::MainTaskError, firehose_cache, healthcheck, serve,
+
};
use std::path::PathBuf;
use clap::Parser;
···
/// recommended in production, but mind the file permissions.
#[arg(long)]
certs: Option<PathBuf>,
+
/// an web address to send healtcheck pings to every ~51s or so
+
#[arg(long)]
+
healthcheck: Option<String>,
}
#[tokio::main]
···
.await?;
Ok(())
});
+
+
if let Some(hc) = args.healthcheck {
+
let healthcheck_shutdown = shutdown.clone();
+
tasks.spawn(async move {
+
healthcheck(hc, healthcheck_shutdown).await?;
+
Ok(())
+
});
+
}
tokio::select! {
_ = shutdown.cancelled() => log::warn!("shutdown requested"),
+2 -1
slingshot/src/server.rs
···
Listener, TcpListener,
acme::{AutoCert, LETS_ENCRYPT_PRODUCTION},
},
-
middleware::{Cors, Tracing},
+
middleware::{CatchPanic, Cors, Tracing},
};
use poem_openapi::{
ApiResponse, ContactObject, ExternalDocumentObject, Object, OpenApi, OpenApiService, Tags,
···
.allow_methods([Method::GET])
.allow_credentials(false),
)
+
.with(CatchPanic::new())
.with(Tracing);
Server::new(listener)
.name("slingshot")