lib.rs (3500B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2024-2026 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU Affero General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. 12 13 You should have received a copy of the GNU Affero General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 17 use std::{path::PathBuf, time::Duration}; 18 19 use config::{Config, parser::ConfigSource}; 20 use tracing::error; 21 use tracing_subscriber::util::SubscriberInitExt; 22 23 use crate::log::taler_logger; 24 25 pub mod api; 26 pub mod bench; 27 pub mod cli; 28 pub mod config; 29 pub mod db; 30 pub mod encoding; 31 pub mod error; 32 pub mod error_code; 33 pub mod json_file; 34 pub mod log; 35 pub mod signature; 36 pub mod types; 37 38 #[derive(clap::Parser, Debug, Clone)] 39 pub struct CommonArgs { 40 /// Specifies the configuration file 41 #[arg(short, long, global = true)] 42 config: Option<PathBuf>, 43 44 /// Configure logging to use LOGLEVEL 45 #[arg(short('L'), long, global = true)] 46 log: Option<tracing::Level>, 47 48 /// Show logs from all sources 49 #[arg(short, long, global = true, hide = true)] 50 verbose: bool, 51 } 52 53 /// DD102: will fail with exit code 9 and will not be restarted by systemd 54 #[derive(Debug, thiserror::Error)] 55 #[error("{0}")] 56 pub struct PermanentErr(String); 57 58 pub fn taler_main( 59 src: ConfigSource, 60 args: CommonArgs, 61 app: impl AsyncFnOnce(&Config) -> Result<(), anyhow::Error>, 62 ) { 63 taler_logger(args.log, args.verbose).init(); 64 let cfg = match Config::load(src, args.config) { 65 Ok(cfg) => cfg, 66 Err(err) => { 67 error!(target: "config", "{}", err); 68 std::process::exit(6); 69 } 70 }; 71 72 // Setup async runtime 73 let runtime = tokio::runtime::Builder::new_multi_thread() 74 .enable_all() 75 .build() 76 .unwrap(); 77 78 // Run app 79 let result = runtime.block_on(app(&cfg)); 80 if let Err(err) = result { 81 error!(target: "cli", "{}", err); 82 // DD102: only diagnosed configuration errors suppress service recovery 83 std::process::exit(if err.chain().any(|cause| cause.is::<config::CfgErr>()) { 84 6 85 } else if err.chain().any(|cause| cause.is::<PermanentErr>()) { 86 9 87 } else { 88 1 89 }) 90 } 91 } 92 93 /// Infinite exponential backoff with decorrelated jitter 94 pub struct ExpoBackoffDecorr { 95 base: u32, 96 max: u32, 97 factor: f32, 98 sleep: u32, 99 } 100 101 impl ExpoBackoffDecorr { 102 pub fn new(base: Duration, max: Duration, factor: f32) -> Self { 103 Self { 104 base: base.as_millis() as u32, 105 max: max.as_millis() as u32, 106 factor, 107 sleep: base.as_millis() as u32, 108 } 109 } 110 111 pub fn backoff(&mut self) -> Duration { 112 self.sleep = 113 rand::random_range(self.base..(self.sleep as f32 * self.factor) as u32).min(self.max); 114 Duration::from_millis(self.sleep as u64) 115 } 116 117 pub fn reset(&mut self) { 118 self.sleep = self.base 119 } 120 } 121 122 impl Default for ExpoBackoffDecorr { 123 fn default() -> Self { 124 Self::new(Duration::from_millis(400), Duration::from_secs(30), 2.5) 125 } 126 }