#![deny(unstable_features, unused_imports, unused_qualifications, clippy::all)]
#![warn(
clippy::inline_always, // Do not use unless benchmarked (explicit allow).
)]
#![allow(
clippy::collapsible_if, // Style preference.
clippy::explicit_auto_deref, // Style preference.
clippy::needless_as_bytes, // Style preference. Better make those things explicit.
clippy::needless_borrow, // Style preference.
clippy::needless_borrows_for_generic_args, // Style preference.
)]
#[macro_use]
extern crate log;
#[macro_use]
extern crate lazy_static;
#[macro_use]
extern crate serde_derive;
mod channel;
mod config;
mod executor;
mod lexer;
mod query;
mod stopwords;
mod store;
mod tasker;
use std::ops::Deref;
use std::str::FromStr;
use std::thread;
use std::time::Duration;
use clap::{Arg, Command};
use log::LevelFilter;
use channel::listen::{ChannelListen, ChannelListenBuilder};
use channel::statistics::ensure_states as ensure_states_channel_statistics;
use config::logger::ConfigLogger;
use config::options::Config;
use config::reader::ConfigReader;
use store::fst::StoreFSTPool;
use store::kv::StoreKVPool;
use tasker::runtime::TaskerBuilder;
use tasker::shutdown::ShutdownSignal;
struct AppArgs {
config: String,
}
#[cfg(unix)]
#[cfg(feature = "allocator-jemalloc")]
#[global_allocator]
static ALLOC: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
pub static LINE_FEED: &str = "\r\n";
pub static THREAD_NAME_CHANNEL_MASTER: &str = "sonic-channel-master";
pub static THREAD_NAME_CHANNEL_CLIENT: &str = "sonic-channel-client";
pub static THREAD_NAME_TASKER: &str = "sonic-tasker";
macro_rules! gen_spawn_managed {
($name:expr, $method:ident, $thread_name:ident, $managed_fn:ident) => {
fn $method() {
debug!("spawn managed thread: {}", $name);
let worker = thread::Builder::new()
.name($thread_name.to_string())
.spawn(|| $managed_fn::build().run());
let has_error = if let Ok(worker_thread) = worker {
worker_thread.join().is_err()
} else {
true
};
if has_error == true {
error!("managed thread crashed ({}), setting it up again", $name);
thread::sleep(Duration::from_secs(1));
$method();
}
}
};
}
lazy_static! {
static ref APP_ARGS: AppArgs = make_app_args();
static ref APP_CONF: Config = ConfigReader::make();
}
gen_spawn_managed!(
"channel",
spawn_channel,
THREAD_NAME_CHANNEL_MASTER,
ChannelListenBuilder
);
gen_spawn_managed!("tasker", spawn_tasker, THREAD_NAME_TASKER, TaskerBuilder);
const DEFAULT_CONFIG_FILE_PATH: &str = "./config.cfg";
fn make_app_args() -> AppArgs {
let matches = Command::new(clap::crate_name!())
.version(clap::crate_version!())
.author(clap::crate_authors!())
.about(clap::crate_description!())
.arg(
Arg::new("config")
.short('c')
.long("config")
.help("Path to configuration file")
.default_value(DEFAULT_CONFIG_FILE_PATH),
)
.get_matches();
AppArgs {
config: matches
.get_one::<String>("config")
.expect("invalid config value")
.to_owned(),
}
}
fn ensure_states() {
let (_, _) = (APP_ARGS.deref(), APP_CONF.deref());
ensure_states_channel_statistics();
}
fn main() {
let _logger = ConfigLogger::init(
LevelFilter::from_str(&APP_CONF.server.log_level).expect("invalid log level"),
);
let shutdown_signal = ShutdownSignal::new();
info!("starting up");
ensure_states();
thread::spawn(spawn_tasker);
thread::spawn(spawn_channel);
info!("started");
shutdown_signal.at_exit(move |signal| {
info!("stopping gracefully (got signal: {})", signal);
ChannelListen::teardown();
StoreKVPool::flush(true);
StoreFSTPool::consolidate(true);
info!("stopped");
});
}