Add healthcheck for db, new clippy lints

This commit is contained in:
asonix 2023-01-29 13:21:36 -06:00
parent 96547230bc
commit f9cad61049
13 changed files with 78 additions and 46 deletions

View file

@ -5,7 +5,7 @@ fn git_info() {
if let Ok(output) = Command::new("git").args(["rev-parse", "HEAD"]).output() {
if output.status.success() {
let git_hash = String::from_utf8_lossy(&output.stdout);
println!("cargo:rustc-env=GIT_HASH={}", git_hash);
println!("cargo:rustc-env=GIT_HASH={git_hash}");
println!("cargo:rustc-env=GIT_SHORT_HASH={}", &git_hash[..8])
}
}
@ -16,7 +16,7 @@ fn git_info() {
{
if output.status.success() {
let git_branch = String::from_utf8_lossy(&output.stdout);
println!("cargo:rustc-env=GIT_BRANCH={}", git_branch);
println!("cargo:rustc-env=GIT_BRANCH={git_branch}");
}
}
}
@ -32,11 +32,11 @@ fn version_info() -> Result<(), anyhow::Error> {
let data: toml::Value = toml::from_str(&cargo_data)?;
if let Some(version) = data["package"]["version"].as_str() {
println!("cargo:rustc-env=PKG_VERSION={}", version);
println!("cargo:rustc-env=PKG_VERSION={version}");
}
if let Some(name) = data["package"]["name"].as_str() {
println!("cargo:rustc-env=PKG_NAME={}", name);
println!("cargo:rustc-env=PKG_NAME={name}");
}
Ok(())

View file

@ -40,11 +40,11 @@ impl std::fmt::Display for Counter {
let labels = self
.labels
.iter()
.map(|(k, v)| format!("{}: {}", k, v))
.map(|(k, v)| format!("{k}: {v}"))
.collect::<Vec<_>>()
.join(", ");
write!(f, "{} - {}", labels, self.value)
write!(f, "{labels} - {}", self.value)
}
}
@ -59,11 +59,11 @@ impl std::fmt::Display for Gauge {
let labels = self
.labels
.iter()
.map(|(k, v)| format!("{}: {}", k, v))
.map(|(k, v)| format!("{k}: {v}"))
.collect::<Vec<_>>()
.join(", ");
write!(f, "{} - {}", labels, self.value)
write!(f, "{labels} - {}", self.value)
}
}
@ -78,7 +78,7 @@ impl std::fmt::Display for Histogram {
let labels = self
.labels
.iter()
.map(|(k, v)| format!("{}: {}", k, v))
.map(|(k, v)| format!("{k}: {v}"))
.collect::<Vec<_>>()
.join(", ");
@ -87,15 +87,15 @@ impl std::fmt::Display for Histogram {
.iter()
.map(|(k, v)| {
if let Some(v) = v {
format!("{}: {:.6}", k, v)
format!("{k}: {v:.6}")
} else {
format!("{}: None,", k)
format!("{k}: None,")
}
})
.collect::<Vec<_>>()
.join(", ");
write!(f, "{} - {}", labels, value)
write!(f, "{labels} - {value}")
}
}
@ -172,18 +172,18 @@ impl Snapshot {
continue;
}
println!("\t{}", key);
println!("\t{key}");
for counter in counters {
println!("\t\t{}", counter);
println!("\t\t{counter}");
}
}
for (key, counters) in merging {
println!("\t{}", key);
println!("\t{key}");
for (_, counter) in counters {
if let Some(counter) = counter.merge() {
println!("\t\t{}", counter);
println!("\t\t{counter}");
}
}
}
@ -192,10 +192,10 @@ impl Snapshot {
if !self.gauges.is_empty() {
println!("Gauges");
for (key, gauges) in self.gauges {
println!("\t{}", key);
println!("\t{key}");
for gauge in gauges {
println!("\t\t{}", gauge);
println!("\t\t{gauge}");
}
}
}
@ -203,10 +203,10 @@ impl Snapshot {
if !self.histograms.is_empty() {
println!("Histograms");
for (key, histograms) in self.histograms {
println!("\t{}", key);
println!("\t{key}");
for histogram in histograms {
println!("\t\t{}", histogram);
println!("\t\t{histogram}");
}
}
}

View file

@ -170,7 +170,7 @@ impl Config {
let config: ParsedConfig = config.try_deserialize()?;
let scheme = if config.https { "https" } else { "http" };
let base_uri = iri!(format!("{}://{}", scheme, config.hostname)).into_absolute();
let base_uri = iri!(format!("{scheme}://{}", config.hostname)).into_absolute();
let tls = match (config.tls_key, config.tls_cert) {
(Some(key), Some(cert)) => Some(TlsConfig { key, cert }),
@ -207,8 +207,8 @@ impl Config {
let source_url = match Self::git_hash() {
Some(hash) => format!(
"{}{}{}",
config.source_repo, config.repository_commit_base, hash
"{}{}{hash}",
config.source_repo, config.repository_commit_base
)
.parse()
.expect("constructed source URL is valid"),
@ -332,7 +332,7 @@ impl Config {
match AdminConfig::build(api_token) {
Ok(conf) => Some(actix_web::web::Data::new(conf)),
Err(e) => {
tracing::error!("Error creating admin config: {}", e);
tracing::error!("Error creating admin config: {e}");
None
}
}
@ -371,7 +371,7 @@ impl Config {
pub(crate) fn software_version() -> String {
if let Some(git) = Self::git_version() {
return format!("v{}-{}", Self::version(), git);
return format!("v{}-{git}", Self::version());
}
format!("v{}", Self::version())
@ -381,7 +381,7 @@ impl Config {
let branch = Self::git_branch()?;
let hash = Self::git_short_hash()?;
Some(format!("{}-{}", branch, hash))
Some(format!("{branch}-{hash}"))
}
fn name() -> &'static str {
@ -463,7 +463,7 @@ impl Config {
resolved
}
UrlKind::Media(uuid) => FixedBaseResolver::new(self.base_uri.as_ref())
.resolve(IriRelativeStr::new(&format!("media/{}", uuid))?.as_ref())
.resolve(IriRelativeStr::new(&format!("media/{uuid}"))?.as_ref())
.try_to_dedicated_string()?,
UrlKind::NodeInfo => FixedBaseResolver::new(self.base_uri.as_ref())
.resolve(IriRelativeStr::new("nodeinfo/2.0.json")?.as_ref())

View file

@ -182,7 +182,7 @@ impl Node {
let authority = url.authority_str().ok_or(ErrorKind::MissingDomain)?;
let scheme = url.scheme_str();
let base = iri!(format!("{}://{}", scheme, authority));
let base = iri!(format!("{scheme}://{authority}"));
Ok(Node {
base,

View file

@ -10,7 +10,10 @@ use rsa::{
use sled::{Batch, Tree};
use std::{
collections::{BTreeMap, HashMap},
sync::Arc,
sync::{
atomic::{AtomicU64, Ordering},
Arc,
},
time::SystemTime,
};
use time::OffsetDateTime;
@ -22,6 +25,8 @@ pub(crate) struct Db {
}
struct Inner {
healthz: Tree,
healthz_counter: Arc<AtomicU64>,
actor_id_actor: Tree,
public_key_id_actor_id: Tree,
connected_actor_ids: Tree,
@ -242,6 +247,8 @@ impl Db {
fn build_inner(restricted_mode: bool, db: sled::Db) -> Result<Self, Error> {
Ok(Db {
inner: Arc::new(Inner {
healthz: db.open_tree("healthz")?,
healthz_counter: Arc::new(AtomicU64::new(0)),
actor_id_actor: db.open_tree("actor-id-actor")?,
public_key_id_actor_id: db.open_tree("public-key-id-actor-id")?,
connected_actor_ids: db.open_tree("connected-actor-ids")?,
@ -273,6 +280,21 @@ impl Db {
Ok(t)
}
pub(crate) async fn check_health(&self) -> Result<(), Error> {
let next = self.inner.healthz_counter.fetch_add(1, Ordering::Relaxed);
self.unblock(move |inner| {
inner
.healthz
.insert("healthz", &next.to_be_bytes()[..])
.map_err(Error::from)
})
.await?;
self.inner.healthz.flush_async().await?;
self.unblock(move |inner| inner.healthz.get("healthz").map_err(Error::from))
.await?;
Ok(())
}
pub(crate) async fn mark_last_seen(
&self,
nodes: HashMap<String, OffsetDateTime>,
@ -468,7 +490,7 @@ impl Db {
pub(crate) async fn is_connected(&self, base_id: IriString) -> Result<bool, Error> {
let scheme = base_id.scheme_str();
let authority = base_id.authority_str().ok_or(ErrorKind::MissingDomain)?;
let prefix = format!("{}://{}", scheme, authority);
let prefix = format!("{scheme}://{authority}");
self.unblock(move |inner| {
let connected = inner
@ -528,7 +550,7 @@ impl Db {
}
pub(crate) async fn remove_connection(&self, actor_id: IriString) -> Result<(), Error> {
tracing::debug!("Removing Connection: {}", actor_id);
tracing::debug!("Removing Connection: {actor_id}");
self.unblock(move |inner| {
inner
.connected_actor_ids
@ -540,7 +562,7 @@ impl Db {
}
pub(crate) async fn add_connection(&self, actor_id: IriString) -> Result<(), Error> {
tracing::debug!("Adding Connection: {}", actor_id);
tracing::debug!("Adding Connection: {actor_id}");
self.unblock(move |inner| {
inner
.connected_actor_ids

View file

@ -45,7 +45,7 @@ impl QueryInstance {
.authority_str()
.ok_or(ErrorKind::MissingDomain)?;
let scheme = self.actor_id.scheme_str();
let instance_uri = iri!(format!("{}://{}/api/v1/instance", scheme, authority));
let instance_uri = iri!(format!("{scheme}://{authority}/api/v1/instance"));
let instance = match state
.requests

View file

@ -39,7 +39,7 @@ impl QueryNodeinfo {
.authority_str()
.ok_or(ErrorKind::MissingDomain)?;
let scheme = self.actor_id.scheme_str();
let well_known_uri = iri!(format!("{}://{}/.well-known/nodeinfo", scheme, authority));
let well_known_uri = iri!(format!("{scheme}://{authority}/.well-known/nodeinfo"));
let well_known = match state
.requests
@ -168,7 +168,7 @@ impl<'de> serde::de::Visitor<'de> for SupportedVersionVisitor {
type Value = SupportedVersion;
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "a string starting with '{}'", SUPPORTED_VERSIONS)
write!(f, "a string starting with '{SUPPORTED_VERSIONS}'")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
@ -187,7 +187,7 @@ impl<'de> serde::de::Visitor<'de> for SupportedNodeinfoVisitor {
type Value = SupportedNodeinfo;
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "a string starting with '{}'", SUPPORTED_NODEINFO)
write!(f, "a string starting with '{SUPPORTED_NODEINFO}'")
}
fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>

View file

@ -39,7 +39,7 @@ use self::{
db::Db,
jobs::create_workers,
middleware::{DebugPayload, MyVerify, RelayResolver, Timings},
routes::{actor, inbox, index, nodeinfo, nodeinfo_meta, statics},
routes::{actor, healthz, inbox, index, nodeinfo, nodeinfo_meta, statics},
};
fn init_subscriber(
@ -273,6 +273,7 @@ async fn do_server_main(
app.wrap(Compress::default())
.wrap(TracingLogger::default())
.wrap(Timings)
.route("/healthz", web::get().to(healthz))
.service(web::resource("/").route(web::get().to(index)))
.service(web::resource("/media/{path}").route(web::get().to(routes::media)))
.service(

View file

@ -75,7 +75,7 @@ impl MyVerify {
Ok(res) => res.actor_id().ok_or(ErrorKind::MissingId),
Err(e) => {
if e.is_gone() {
tracing::warn!("Actor gone: {}", public_key_id);
tracing::warn!("Actor gone: {public_key_id}");
return Ok(false);
} else {
return Err(e);
@ -178,13 +178,13 @@ mod tests {
#[test]
fn handles_masto_keys() {
println!("{}", ASONIX_DOG_KEY);
println!("{ASONIX_DOG_KEY}");
let _ = RsaPublicKey::from_public_key_pem(ASONIX_DOG_KEY.trim()).unwrap();
}
#[test]
fn handles_pleromo_keys() {
println!("{}", KARJALAZET_KEY);
println!("{KARJALAZET_KEY}");
let _ = RsaPublicKey::from_public_key_pem(KARJALAZET_KEY.trim()).unwrap();
}

View file

@ -61,7 +61,7 @@ impl Breakers {
if let Some(mut breaker) = self.inner.get_mut(authority) {
breaker.fail();
if !breaker.should_try() {
tracing::warn!("Failed breaker for {}", authority);
tracing::warn!("Failed breaker for {authority}");
}
false
} else {
@ -235,7 +235,7 @@ impl Requests {
if let Ok(bytes) = res.body().await {
if let Ok(s) = String::from_utf8(bytes.as_ref().to_vec()) {
if !s.is_empty() {
tracing::warn!("Response from {}, {}", parsed_url, s);
tracing::warn!("Response from {parsed_url}, {s}");
}
}
}

View file

@ -1,4 +1,5 @@
mod actor;
mod healthz;
mod inbox;
mod index;
mod media;
@ -7,6 +8,7 @@ mod statics;
pub(crate) use self::{
actor::route as actor,
healthz::route as healthz,
inbox::route as inbox,
index::route as index,
media::route as media,

7
src/routes/healthz.rs Normal file
View file

@ -0,0 +1,7 @@
use crate::{data::State, error::Error};
use actix_web::{web, HttpResponse};
pub(crate) async fn route(state: web::Data<State>) -> Result<HttpResponse, Error> {
state.db.check_health().await?;
Ok(HttpResponse::Ok().finish())
}

View file

@ -89,19 +89,19 @@ async fn answer(bot: Bot, msg: Message, cmd: Command, db: Db) -> ResponseResult<
.await?;
}
Command::Block { domain } if db.add_blocks(vec![domain.clone()]).await.is_ok() => {
bot.send_message(msg.chat.id, format!("{} has been blocked", domain))
bot.send_message(msg.chat.id, format!("{domain} has been blocked"))
.await?;
}
Command::Unblock { domain } if db.remove_blocks(vec![domain.clone()]).await.is_ok() => {
bot.send_message(msg.chat.id, format!("{} has been unblocked", domain))
bot.send_message(msg.chat.id, format!("{domain} has been unblocked"))
.await?;
}
Command::Allow { domain } if db.add_allows(vec![domain.clone()]).await.is_ok() => {
bot.send_message(msg.chat.id, format!("{} has been allowed", domain))
bot.send_message(msg.chat.id, format!("{domain} has been allowed"))
.await?;
}
Command::Disallow { domain } if db.remove_allows(vec![domain.clone()]).await.is_ok() => {
bot.send_message(msg.chat.id, format!("{} has been disallowed", domain))
bot.send_message(msg.chat.id, format!("{domain} has been disallowed"))
.await?;
}
Command::ListAllowed => {