grapevine/src/main.rs
Charles Hall 5be1e20eb4
call maximize_fd_limit at top of main
This way we don't shoot ourselves in the foot by forgetting to do it for
other subcommands (e.g. that manipulate the database) in the future.
2024-11-01 13:16:36 -07:00

131 lines
3.8 KiB
Rust

// Avoid spurious warnings with --no-default-features, which isn't expected to
// work anyway
#![cfg_attr(not(any(feature = "sqlite", feature = "rocksdb")), allow(unused))]
use std::process::ExitCode;
use clap::Parser;
#[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))]
use tikv_jemallocator::Jemalloc;
use tracing::{error, info};
mod api;
mod cli;
mod config;
mod database;
mod error;
mod observability;
mod service;
mod utils;
pub(crate) use api::ruma_wrapper::{Ar, Ra};
pub(crate) use config::Config;
pub(crate) use service::{pdu::PduEvent, services, Services};
pub(crate) use utils::error::{Error, Result};
#[cfg(all(not(target_env = "msvc"), feature = "jemalloc"))]
#[global_allocator]
static GLOBAL: Jemalloc = Jemalloc;
/// Returns the current version of the crate with extra info if supplied
///
/// Set the environment variable `GRAPEVINE_VERSION_EXTRA` to any UTF-8 string
/// to include it in parenthesis after the SemVer version. A common value are
/// git commit hashes.
fn version() -> String {
let cargo_pkg_version = env!("CARGO_PKG_VERSION");
match option_env!("GRAPEVINE_VERSION_EXTRA") {
Some(x) => format!("{cargo_pkg_version} ({x})"),
None => cargo_pkg_version.to_owned(),
}
}
#[derive(Debug, Clone, Copy)]
enum ApplicationState {
Ready,
Reloading,
Stopping,
}
fn set_application_state(state: ApplicationState) {
info!(?state, "Application state changed");
#[cfg(feature = "systemd")]
{
use sd_notify::NotifyState;
fn notify(states: &[NotifyState<'_>]) {
sd_notify::notify(false, states)
.expect("should be able to notify systemd");
}
match state {
ApplicationState::Ready => notify(&[NotifyState::Ready]),
ApplicationState::Reloading => {
let timespec = nix::time::clock_gettime(
nix::time::ClockId::CLOCK_MONOTONIC,
)
.expect("CLOCK_MONOTONIC should be usable");
let monotonic_usec =
timespec.tv_sec() * 1_000_000 + timespec.tv_nsec() / 1000;
notify(&[
NotifyState::Reloading,
NotifyState::Custom(&format!(
"MONOTONIC_USEC={monotonic_usec}",
)),
]);
}
ApplicationState::Stopping => notify(&[NotifyState::Stopping]),
};
}
}
#[tokio::main]
async fn main() -> ExitCode {
// This is needed for opening lots of file descriptors, which tends to
// happen more often when using RocksDB and making lots of federation
// connections at startup. The soft limit is usually 1024, and the hard
// limit is usually 512000; I've personally seen it hit >2000.
//
// * https://www.freedesktop.org/software/systemd/man/systemd.exec.html#id-1.12.2.1.17.6
// * https://github.com/systemd/systemd/commit/0abf94923b4a95a7d89bc526efc84e7ca2b71741
#[cfg(unix)]
maximize_fd_limit()
.expect("should be able to increase the soft limit to the hard limit");
let args = cli::Args::parse();
let Err(e) = args.run().await else {
return ExitCode::SUCCESS;
};
eprintln!(
"Error: {}",
error::DisplayWithSources {
error: &e,
infix: "\n Caused by: "
}
);
ExitCode::FAILURE
}
#[cfg(unix)]
#[tracing::instrument(err)]
fn maximize_fd_limit() -> Result<(), nix::errno::Errno> {
use nix::sys::resource::{getrlimit, setrlimit, Resource};
use tracing::debug;
let res = Resource::RLIMIT_NOFILE;
let (soft_limit, hard_limit) = getrlimit(res)?;
debug!(soft_limit, "Current nofile soft limit");
setrlimit(res, hard_limit, hard_limit)?;
debug!(hard_limit, "Increased nofile soft limit to the hard limit");
Ok(())
}