160 lines
7.5 KiB
Rust
160 lines
7.5 KiB
Rust
//! This is a tool that uses the synapse_compress_state library to
|
|
//! reduce the size of the synapse state_groups_state table in a postgres
|
|
//! database.
|
|
//!
|
|
//! It adds the tables state_compressor_state and state_compressor_progress
|
|
//! to the database and uses these to enable it to incrementally work
|
|
//! on space reductions.
|
|
//!
|
|
//! This binary calls manager::compress_largest_rooms() with the arguments
|
|
//! provided. That is, it compresses (in batches) the top N rooms ranked by
|
|
//! amount of "uncompressed" state. This is measured by the number of rows in
|
|
//! the state_groups_state table.
|
|
//!
|
|
//! After each batch, the rows processed are marked as "compressed" (using
|
|
//! the state_compressor_progress table), and the program state is saved into
|
|
//! the state_compressor_state table so that the compressor can seemlesly
|
|
//! continue from where it left off.
|
|
|
|
#[global_allocator]
|
|
static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc;
|
|
|
|
use clap::{crate_authors, crate_description, crate_name, crate_version, value_t, App, Arg};
|
|
use log::LevelFilter;
|
|
use std::{env, fs::OpenOptions};
|
|
use synapse_auto_compressor::{manager, state_saving, LevelInfo};
|
|
|
|
/// Execution starts here
|
|
fn main() {
|
|
// setup the logger for the synapse_auto_compressor
|
|
// The default can be overwritten with RUST_LOG
|
|
// see the README for more information
|
|
let log_file = OpenOptions::new()
|
|
.append(true)
|
|
.create(true)
|
|
.open("synapse_auto_compressor.log")
|
|
.unwrap_or_else(|e| panic!("Error occured while opening the log file: {}", e));
|
|
|
|
if env::var("RUST_LOG").is_err() {
|
|
let mut log_builder = env_logger::builder();
|
|
// Ensure panics still come through
|
|
log_builder.filter_module("panic", LevelFilter::Error);
|
|
// Only output errors from the synapse_compress state library
|
|
log_builder.filter_module("synapse_compress_state", LevelFilter::Error);
|
|
// Output log levels info and above from synapse_auto_compressor
|
|
log_builder.filter_module("synapse_auto_compressor", LevelFilter::Info);
|
|
log_builder.init();
|
|
} else {
|
|
// If RUST_LOG was set then use that
|
|
let mut log_builder = env_logger::Builder::from_env("RUST_LOG");
|
|
log_builder.target(env_logger::Target::Pipe(Box::new(log_file)));
|
|
// Ensure panics still come through
|
|
log_builder.filter_module("panic", LevelFilter::Error);
|
|
log_builder.init();
|
|
}
|
|
log_panics::init();
|
|
// Announce the start of the program to the logs
|
|
log::info!("synapse_auto_compressor started");
|
|
|
|
// parse the command line arguments using the clap crate
|
|
let arguments = App::new(crate_name!())
|
|
.version(crate_version!())
|
|
.author(crate_authors!("\n"))
|
|
.about(crate_description!())
|
|
.arg(
|
|
Arg::with_name("postgres-url")
|
|
.short("p")
|
|
.value_name("POSTGRES_LOCATION")
|
|
.help("The configruation for connecting to the postgres database.")
|
|
.long_help(concat!(
|
|
"The configuration for connecting to the Postgres database. This should be of the form ",
|
|
r#""postgresql://username:password@mydomain.com/database" or a key-value pair "#,
|
|
r#"string: "user=username password=password dbname=database host=mydomain.com" "#,
|
|
"See https://docs.rs/tokio-postgres/0.7.2/tokio_postgres/config/struct.Config.html ",
|
|
"for the full details."
|
|
))
|
|
.takes_value(true)
|
|
.required(true),
|
|
).arg(
|
|
Arg::with_name("chunk_size")
|
|
.short("c")
|
|
.value_name("COUNT")
|
|
.help("The maximum number of state groups to load into memroy at once")
|
|
.long_help(concat!(
|
|
"The number of state_groups to work on at once. All of the entries",
|
|
" from state_groups_state are requested from the database",
|
|
" for state groups that are worked on. Therefore small",
|
|
" chunk sizes may be needed on machines with low memory.",
|
|
" (Note: if the compressor fails to find space savings on the",
|
|
" chunk as a whole (which may well happen in rooms with lots",
|
|
" of backfill in) then the entire chunk is skipped.)",
|
|
))
|
|
.takes_value(true)
|
|
.required(true),
|
|
).arg(
|
|
Arg::with_name("default_levels")
|
|
.short("l")
|
|
.value_name("LEVELS")
|
|
.help("Sizes of each new level in the compression algorithm, as a comma separated list.")
|
|
.long_help(concat!(
|
|
"Sizes of each new level in the compression algorithm, as a comma separated list.",
|
|
" The first entry in the list is for the lowest, most granular level,",
|
|
" with each subsequent entry being for the next highest level.",
|
|
" The number of entries in the list determines the number of levels",
|
|
" that will be used.",
|
|
"\nThe sum of the sizes of the levels effect the performance of fetching the state",
|
|
" from the database, as the sum of the sizes is the upper bound on number of",
|
|
" iterations needed to fetch a given set of state.",
|
|
))
|
|
.default_value("100,50,25")
|
|
.takes_value(true)
|
|
.required(false),
|
|
).arg(
|
|
Arg::with_name("number_of_chunks")
|
|
.short("n")
|
|
.value_name("CHUNKS_TO_COMPRESS")
|
|
.help("The number of chunks to compress")
|
|
.long_help(concat!(
|
|
"This many chunks of the database will be compressed. The higher this number is set to, ",
|
|
"the longer the compressor will run for."
|
|
))
|
|
.takes_value(true)
|
|
.required(true),
|
|
).get_matches();
|
|
|
|
// The URL of the database
|
|
let db_url = arguments
|
|
.value_of("postgres-url")
|
|
.expect("A database url is required");
|
|
|
|
// The number of state groups to work on at once
|
|
let chunk_size = arguments
|
|
.value_of("chunk_size")
|
|
.map(|s| s.parse().expect("chunk_size must be an integer"))
|
|
.expect("A chunk size is required");
|
|
|
|
// The default structure to use when compressing
|
|
let default_levels = value_t!(arguments, "default_levels", LevelInfo)
|
|
.unwrap_or_else(|e| panic!("Unable to parse default levels: {}", e));
|
|
|
|
// The number of rooms to compress with this tool
|
|
let number_of_chunks = arguments
|
|
.value_of("number_of_chunks")
|
|
.map(|s| s.parse().expect("number_of_chunks must be an integer"))
|
|
.expect("number_of_chunks is required");
|
|
|
|
// Connect to the database and create the 2 tables this tool needs
|
|
// (Note: if they already exist then this does nothing)
|
|
let mut client = state_saving::connect_to_database(db_url)
|
|
.unwrap_or_else(|e| panic!("Error occured while connecting to {}: {}", db_url, e));
|
|
state_saving::create_tables_if_needed(&mut client)
|
|
.unwrap_or_else(|e| panic!("Error occured while creating tables in database: {}", e));
|
|
|
|
// call compress_largest_rooms with the arguments supplied
|
|
// panic if an error is produced
|
|
manager::compress_chunks_of_database(db_url, chunk_size, &default_levels.0, number_of_chunks)
|
|
.unwrap();
|
|
|
|
log::info!("synapse_auto_compressor finished");
|
|
}
|