oxedyne/fe2o3/fe2o3_o3db_sync/src/base/constant.rs
13.1 KiB, 39 runs
created by r1870400018:721, which is this file's identity for as long as the history lasts, whatever it is later renamed to
download · who wrote it · its history
| 1 | use crate::{ |
| 2 | base::cfg::OzoneConfig, |
| 3 | comm::response::Wait, |
| 4 | }; |
| 5 | |
| 6 | use oxedyne_fe2o3_core::{ |
| 7 | prelude::*, |
| 8 | byte::Encoding, |
| 9 | }; |
| 10 | use oxedyne_fe2o3_jdat::{ |
| 11 | usr::UsrKindCode, |
| 12 | version::SemVer, |
| 13 | }; |
| 14 | |
| 15 | use std::time::Duration; |
| 16 | |
| 17 | impl OzoneConfig { |
| 18 | pub fn check_constants() -> Outcome<()> { |
| 19 | if CHECK_INTERVAL > USER_REQUEST_TIMEOUT { |
| 20 | return Err(err!( |
| 21 | "The prescribed constant check interval, {:?}, should not be larger than the \ |
| 22 | constant maximum wait, {:?}.", CHECK_INTERVAL, |
| 23 | USER_REQUEST_TIMEOUT; |
| 24 | Invalid, Input)); |
| 25 | } |
| 26 | // The three deadlines nest: a bot gives up on another bot first, a user request next, |
| 27 | // and a control operation last. Invert any pair and the outer waiter reports a failure |
| 28 | // the inner one has not yet had the chance to make. |
| 29 | if USER_REQUEST_TIMEOUT <= BOT_REQUEST_TIMEOUT { |
| 30 | return Err(err!( |
| 31 | "The user request timeout, {:?}, must exceed the internal bot request timeout, \ |
| 32 | {:?}, or a user request can be abandoned while the bot serving it is still \ |
| 33 | waiting on another bot.", USER_REQUEST_TIMEOUT, BOT_REQUEST_TIMEOUT; |
| 34 | Invalid, Input)); |
| 35 | } |
| 36 | if CONTROL_REQUEST_TIMEOUT < USER_REQUEST_TIMEOUT { |
| 37 | return Err(err!( |
| 38 | "The control operation timeout, {:?}, must be at least the user request \ |
| 39 | timeout, {:?}. A control operation is issued once, at startup, behind \ |
| 40 | whatever initialisation work the zone bots are still doing, so it cannot be \ |
| 41 | held to a deadline shorter than an ordinary request's.", |
| 42 | CONTROL_REQUEST_TIMEOUT, USER_REQUEST_TIMEOUT; |
| 43 | Invalid, Input)); |
| 44 | } |
| 45 | if DURABILITY_TIMEOUT <= USER_REQUEST_TIMEOUT || DURABILITY_TIMEOUT > CONTROL_REQUEST_TIMEOUT { |
| 46 | return Err(err!( |
| 47 | "The durability timeout, {:?}, must exceed the user request timeout, {:?}, and \ |
| 48 | not exceed the control operation timeout, {:?}. It waits on the disk, which \ |
| 49 | answers more slowly than any bot, and it is a request's deadline, which a \ |
| 50 | start-up control operation outlasts.", |
| 51 | DURABILITY_TIMEOUT, USER_REQUEST_TIMEOUT, CONTROL_REQUEST_TIMEOUT; |
| 52 | Invalid, Input)); |
| 53 | } |
| 54 | Ok(()) |
| 55 | } |
| 56 | } |
| 57 | |
| 58 | pub const VERSION: SemVer = SemVer::new(0, 5, 0); |
| 59 | pub const NAMEX_ID: &'static str = "QByizewdCnRH/E6ksx4rOnqv5lFuB6PX1EA4Z5kNQwA="; |
| 60 | |
| 61 | pub const CURRENT_FORMAT_VERSION: u8 = 2; |
| 62 | |
| 63 | // Files. |
| 64 | pub const MAX_ZONES: u16 = 100; |
| 65 | pub const LIVE_FILE_CLAIM_LIMIT: usize = 1024; |
| 66 | pub const DEFAULT_MAX_ZONE_DIR_BYTES: u64 = 104_857_600; // 100 MiB |
| 67 | pub const CONFIG_FILENAME: &'static str = "config.jdat"; |
| 68 | pub const DB_UID_CHAR_LEN: usize = 5; |
| 69 | |
| 70 | pub const DATA_FILE_EXT: &'static str = "dat"; |
| 71 | // Prefix of the temporary data file written into a zone directory while a data file's |
| 72 | // garbage is collected. One left behind is an abandoned transcription. |
| 73 | pub const GC_TEMP_FILE_PREFIX: &'static str = ".gc"; |
| 74 | pub const INDEX_FILE_EXT: &'static str = "ind"; |
| 75 | pub const DB_DIR_PREFIX: &'static str = "o3db"; |
| 76 | pub const HASH_BYTES: usize = 8; // u64 |
| 77 | pub const CACHE_HASH_BYTES: usize = 4; // u32 |
| 78 | |
| 79 | // File size management. |
| 80 | // When the total length of old values in a data file exceeds the following percentage of the |
| 81 | // maximum data file length, garbage collection on the file is triggered. |
| 82 | pub const OLD_DATA_PERCENT_GC_TRIGGER: f64 = 30.0; |
| 83 | |
| 84 | // File reading cache. |
| 85 | pub const FILE_CACHE_EXPIRY_SECS: u64 = 15*60; // 15 mins |
| 86 | pub const MAX_CACHED_FILES: usize = 200; |
| 87 | |
| 88 | // A read whose record is not the one its cache bot named -- a location a collection has moved, or |
| 89 | // one read through the handle of the file's previous generation -- retries with a freshly fetched |
| 90 | // location. This bounds those retries, for every read, so a file a supersession burst keeps |
| 91 | // collecting cannot spin a reader for ever. |
| 92 | pub const MAX_READ_ATTEMPTS: usize = 8; |
| 93 | |
| 94 | // Resource management. |
| 95 | pub const CACHE_JETTISON_FRAC_OF_LIM: f64 = 0.20; |
| 96 | |
| 97 | // Bots. |
| 98 | pub const BOT_ERR_COUNT_WARNING: usize = 10; |
| 99 | pub const STACK_SIZE: usize = 2 * 1024 * 1024; |
| 100 | |
| 101 | // Shutdown. |
| 102 | // How long a close waits for the bots to finish in order before the supervisor answers it; any |
| 103 | // still running are finished afterwards, in the same order (`Supervisor::shutdown`). |
| 104 | pub const SHUTDOWN_MAX_WAIT: Duration = Duration::from_secs(3); |
| 105 | |
| 106 | // Intervals. |
| 107 | pub const HEALTH_CHECK_INTERVAL: Duration = Duration::from_secs(60); |
| 108 | |
| 109 | // Busy waiting intervals. |
| 110 | pub const CHECK_INTERVAL: Duration = Duration::from_millis(100); |
| 111 | pub const CONFIGWATCHER_CHECK_INTERVAL_SECS: u64 = 3; |
| 112 | // The server bot blocks on its internal channel rather than polling, so no channel check |
| 113 | // interval is needed. The external UDP socket, when enabled, reads with this timeout. |
| 114 | pub const SERVER_EXT_SOCKET_CHECK_INTERVAL: Duration = Duration::from_nanos(999_000); |
| 115 | |
| 116 | // Timeouts. |
| 117 | // User timeouts must last longer than internal bot timeouts to avoid lockups. |
| 118 | pub const USER_REQUEST_TIMEOUT: Duration = |
| 119 | BOT_REQUEST_TIMEOUT.saturating_add(Duration::from_secs(1)); |
| 120 | pub const USER_REQUEST_WAIT: Wait = Wait { |
| 121 | max_wait: USER_REQUEST_TIMEOUT, |
| 122 | check_interval: CHECK_INTERVAL, |
| 123 | }; |
| 124 | pub const BOT_REQUEST_TIMEOUT: Duration = Duration::from_secs(5); |
| 125 | pub const BOT_REQUEST_WAIT: Wait = Wait { |
| 126 | max_wait: BOT_REQUEST_TIMEOUT, |
| 127 | check_interval: CHECK_INTERVAL, |
| 128 | }; |
| 129 | // A write is answered twice. `OzoneMsg::Written` says its record is appended, which is the |
| 130 | // writer's own work and is held to USER_REQUEST_TIMEOUT like any request. The final answer says |
| 131 | // the record is durable under the store's sync policy and readable, and that waits on the disk: |
| 132 | // an fsync queued behind everything else a busy machine is writing took over eleven seconds when |
| 133 | // measured (2026-09-23), and a six-second deadline then reported as failed a write that went on |
| 134 | // to land. So this deadline marks a disk that has stopped rather than one that is busy, and what |
| 135 | // its expiry reports is a write not confirmed durable, never a write that failed. |
| 136 | pub const DURABILITY_TIMEOUT: Duration = Duration::from_secs(120); |
| 137 | // A control operation -- activating garbage collection, rolling every writer onto a fresh live |
| 138 | // file -- is issued once, by whoever owns the database, and usually while it is still starting. |
| 139 | // Its message queues behind whatever the zone bots are already doing, and the initial survey of |
| 140 | // a large store's files runs for minutes, so this deadline marks not how long the answer should |
| 141 | // take but the point at which a bot is better presumed dead than busy. |
| 142 | // |
| 143 | // It is deliberately larger than USER_REQUEST_TIMEOUT and the two must not be unified. A user |
| 144 | // request is one of very many, on a path a client is blocked on, and its short deadline is what |
| 145 | // turns a slow request path into a visible error instead of a hang; stretching it to cover |
| 146 | // startup would hide the very thing it exists to expose. A control operation has no client |
| 147 | // waiting on it and happens once, so waiting costs nothing and failing costs everything: six |
| 148 | // seconds applied here stops a large database booting at all. |
| 149 | // |
| 150 | // Five minutes rather than no limit, because a bot that has said nothing for five minutes is a |
| 151 | // fault to report, not a slow start to wait out. |
| 152 | pub const CONTROL_REQUEST_TIMEOUT: Duration = Duration::from_secs(300); |
| 153 | pub const CONTROL_REQUEST_WAIT: Wait = Wait { |
| 154 | max_wait: CONTROL_REQUEST_TIMEOUT, |
| 155 | check_interval: CHECK_INTERVAL, |
| 156 | }; |
| 157 | pub const ZONE_STATE_UPDATER_LISTEN_TIMEOUT: Duration = Duration::from_millis(300); |
| 158 | //pub const GET_DATA_WAIT: Wait = Wait::new_default(); |
| 159 | pub const PING_TIMEOUT: Duration = Duration::from_secs(5); |
| 160 | |
| 161 | // ConfigBot. |
| 162 | pub const CONFIGWATCHER_REFRESH_FILE_AFTER_N_CHECKS: usize = 100; |
| 163 | |
| 164 | // ServerBot. |
| 165 | pub const SERVER_ADDRESS: &'static str = "127.0.0.1"; |
| 166 | pub const UDP_BUFFER_SIZE: usize = 1_400; |
| 167 | // Mostly completely arbitrary... |
| 168 | pub const POW_CREATE_TIMEOUT: Duration = Duration::from_secs(30); |
| 169 | pub const POW_CREATE_COUNT_LIM: usize = usize::MAX; |
| 170 | pub const DEFAULT_UDP_PACKET_SIZE: usize = 700; |
| 171 | pub const REQ_TIMER_LEN: usize = 100; |
| 172 | pub const MAX_ALLOWED_AVG_REQ_PER_SEC: u64 = 30; |
| 173 | pub const POW_MAX_ZERO_BITS: usize = 30; |
| 174 | pub const POW_NONCE_LEN: usize = 8; |
| 175 | pub const POW_CODE_LEN: usize = 8; |
| 176 | pub const POW_ADDR_LEN: usize = 16; |
| 177 | pub const POW_TIMESTAMP_LEN: usize = 8; |
| 178 | pub const POW_PREFIX_LEN: usize = |
| 179 | POW_ADDR_LEN + |
| 180 | POW_CODE_LEN; |
| 181 | pub const POW_PREIMAGE_LEN: usize = |
| 182 | POW_ADDR_LEN + |
| 183 | POW_CODE_LEN + |
| 184 | POW_TIMESTAMP_LEN; |
| 185 | pub const POW_INPUT_LEN: usize = |
| 186 | POW_PREIMAGE_LEN + |
| 187 | POW_NONCE_LEN; |
| 188 | pub const THROTTLED_INTERVAL_MIN: Duration = Duration::from_secs(1); |
| 189 | pub const ADDR_THROTTLE_SUNSET_SECS_MIN: u64 = 1_800; // 30 min |
| 190 | pub const ADDR_THROTTLE_SUNSET_SECS_MAX: u64 = 259_200; // 3 days |
| 191 | pub const THROTTLE_COUNT_BEFORE_BLACKLIST: u16 = 10; |
| 192 | pub const SESSION_REQUEST_EXPIRY: Duration = Duration::from_secs(600); // 10 min |
| 193 | pub const PARTIAL_MESSAGE_SUNSET: Duration = Duration::from_secs(600); // 10 min |
| 194 | pub const MSG_ASSEMBLY_SUNSET: Duration = Duration::from_secs(600); |
| 195 | pub const MSG_ASSEMBLY_IDLE_MAX: Duration = Duration::from_secs(60); |
| 196 | pub const MSG_ASSEMBLY_REP_TOTAL_LIM: u8 = 128; |
| 197 | pub const MSG_ASSEMBLY_REP_PACKET_LIM: u8 = 32; |
| 198 | pub const MSG_ASSEMBLY_GC_INTERVAL: Duration = Duration::from_secs(600); |
| 199 | pub const DEFAULT_MSG_ENCODING: Encoding = Encoding::Binary; |
| 200 | |
| 201 | // Schemes ===================================================================== |
| 202 | // Chunking. |
| 203 | // Min chunk size rationale: |
| 204 | // (Dat::BU64 + AES_GCM) overhead, safety factor of 2 |
| 205 | pub const MIN_CHUNK_SIZE: usize = (9 + 12)*2; |
| 206 | pub const DEFAULT_REST_CHUNK_BYTES: usize = 1_024_000; // 1 MiB |
| 207 | pub const DEFAULT_WIRE_CHUNK_BYTES: usize = 1_024; // 1 KiB |
| 208 | |
| 209 | // Hashing. |
| 210 | pub const KEY_HASH_SALT: [u8; 16] = SALT16; |
| 211 | // Salt for the deterministic chunk set identifier, kept distinct from the |
| 212 | // routing salt so the two hashes of a key never coincide. |
| 213 | pub const CHUNK_SET_ID_SALT: [u8; 16] = [ |
| 214 | 0x7a, 0x1d, 0x3f, 0x9c, 0x42, 0xe8, 0x05, 0xb6, |
| 215 | 0x11, 0x93, 0xaf, 0x6e, 0x28, 0xd4, 0x7c, 0x50, |
| 216 | ]; |
| 217 | pub const SALT8: [u8; 8] = [ |
| 218 | 0x15, 0x04, 0x84, 0x1e, 0xf2, 0x07, 0x19, 0xbc, |
| 219 | ]; |
| 220 | pub const SALT16: [u8; 16] = [ |
| 221 | 0xc9, 0x48, 0xcc, 0xbe, 0xd5, 0xd1, 0x16, 0x6c, |
| 222 | 0xc2, 0x2a, 0x78, 0x85, 0xce, 0x3e, 0x25, 0xcd, |
| 223 | ]; |
| 224 | pub const SALT32: [u8; 32] = [ |
| 225 | 0xbd, 0xd5, 0x81, 0xba, 0x0c, 0xeb, 0x4f, 0xad, |
| 226 | 0x23, 0x72, 0xd4, 0xac, 0x92, 0xeb, 0xa6, 0x6f, |
| 227 | 0xf9, 0x65, 0x4f, 0x04, 0xca, 0xd5, 0x8b, 0x73, |
| 228 | 0xb2, 0xfa, 0x72, 0x39, 0x21, 0x6b, 0x6c, 0x5f, |
| 229 | ]; |
| 230 | |
| 231 | // Encryption. |
| 232 | pub const DEFAULT_SYMMETRIC_KEY_BYTES: usize = 32; |
| 233 | |
| 234 | // Randomness. |
| 235 | pub const DB_UID_CHARS: &'static str = |
| 236 | // 1 2 3 4 5 |
| 237 | // 123456789012345678901234567890123456789012345678901234 |
| 238 | "ABCDEFGHKMNPQRSTUVWXYZabcedefghkmnpqrstuvwxyz123456789"; |
| 239 | |
| 240 | // Document Data Abstraction Layer. |
| 241 | pub const USER_KIND_DIR_CODE: UsrKindCode = 5; |
| 242 | pub const USER_KIND_DOC_CODE: UsrKindCode = 6; |
| 243 | pub const DOC_PATH_LEN_LIMIT: usize = 1024; |
| 244 | |
| 245 | pub const USER_ID_BYTE_LEN: usize = 16; |
| 246 | |
| 247 | pub const MAX_FILE_BYTES: u64 = i64::MAX as u64; |
| 248 | pub const MAX_FILE_TO_CHUNKING_THRESHOLD_RATIO: f64 = 0.8; |
| 249 | pub const MAX_FILE_TO_CHUNK_SIZE_RATIO: f64 = 0.3; |
| 250 | /// When restarting an ozone database, the live file in a zone is usually chosen to be one with |
| 251 | /// the highest file number. This will remain the case if the ratio of the live data file size |
| 252 | /// to the maximum data file size is below this fraction, otherwise a new live file will be |
| 253 | /// created. |
| 254 | pub const LIVE_FILE_INIT_SIZE_RATIO_THRESHOLD: f64 = 0.5; |
| 255 | |
| 256 | // Credits: |
| 257 | // Image: https://ascii-generator.site/ |
| 258 | // Text: https://www.asciiart.eu/text-to-ascii-art Nancyj Improved with touch ups. |
| 259 | pub const SPLASH: &'static str = |
| 260 | r#" |
| 261 | |
| 262 | .:::::::::::::::::::::. |
| 263 | .' '. |
| 264 | .: :. |
| 265 | .. .. .o88888o. dP dP |
| 266 | .. .. d8' `8b 88 88 |
| 267 | .. .... .... .. 88 88 .d888b88 88d888b. |
| 268 | .. .....: :..... .. 88 88 d8888b. 88' `88 88' `88 |
| 269 | .. '...... ......' .. Y8. .8P `88 88. .88 88. .88 |
| 270 | .. ...:::... .. `888888P' aaad8' `88888P8 88Y8888' |
| 271 | .. ....... .. `88 |
| 272 | .. '...' .. d88888P |
| 273 | .: :. |
| 274 | '-:::::::::::::::::::::-' |
| 275 | Ozone Database |
| 276 | |
| 277 | "#; |
| 278 | |
| 279 |