diff --git a/Cargo.lock b/Cargo.lock index 7dec9f43..c2d32773 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1399,6 +1399,30 @@ dependencies = [ name = "format_table" version = "0.1.1" +[[package]] +name = "fundu" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ce12752fc64f35be3d53e0a57017cd30970f0cffd73f62c791837d8845badbd" +dependencies = [ + "fundu-core", +] + +[[package]] +name = "fundu-core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e463452e2d8b7600d38dcea1ed819773a57f0d710691bfc78db3961bd3f4c3ba" + +[[package]] +name = "fundu-systemd" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8641ae8f534f853a552beb3a9f12cc72fd1343260964af82cf09fda66e3d94e" +dependencies = [ + "fundu", +] + [[package]] name = "futures" version = "0.3.32" @@ -1502,6 +1526,7 @@ dependencies = [ "chrono", "crc-fast", "format_table", + "fundu-systemd", "futures", "garage_api_admin", "garage_api_common", @@ -1528,7 +1553,6 @@ dependencies = [ "opentelemetry", "opentelemetry-otlp", "opentelemetry-prometheus", - "parse_duration", "serde_json", "sha1 0.10.6", "sha2 0.10.9", @@ -1749,7 +1773,6 @@ dependencies = [ "garage_util", "hex", "http 1.4.0", - "parse_duration", "rand 0.9.4", "serde", "serde_bytes", @@ -1849,6 +1872,8 @@ dependencies = [ "blake2", "bytesize", "chrono", + "fundu", + "fundu-systemd", "futures", "garage_db", "garage_net", @@ -3223,41 +3248,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "num" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8536030f9fea7127f841b45bb6243b27255787fb4eb83958aa1ef9d2fdc0c36" -dependencies = [ - "num-bigint", - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - -[[package]] -name = "num-bigint" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-complex" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6b19411a9719e753aff12e5187b74d60d3dc449ec3f4dc21e3989c3f554bc95" -dependencies = [ - "autocfg", - "num-traits", -] - [[package]] name = "num-conv" version = "0.2.1" @@ -3273,29 +3263,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" -dependencies = [ - "autocfg", - "num-bigint", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -3464,17 +3431,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "parse_duration" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7037e5e93e0172a5a96874380bf73bc6ecef022e26fa25f2be26864d6b3ba95d" -dependencies = [ - "lazy_static", - "num", - "regex", -] - [[package]] name = "password-hash" version = "0.5.0" diff --git a/Cargo.toml b/Cargo.toml index 46477179..704f0264 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -53,6 +53,8 @@ cfg-if = "1.0" chrono = { version = "0.4", features = ["serde"] } crc-fast = "1.9" crypto-common = "0.1" +fundu = "2.0" +fundu-systemd = "0.3" gethostname = "1.1" git-version = "0.3" hex = "0.4" @@ -68,7 +70,6 @@ mktemp = "0.5" nix = { version = "0.31", default-features = false, features = ["fs"] } nom = "8.0" parking_lot = "0.12" -parse_duration = "2.1" paste = "1.0" pin-project = "1.1" pnet_datalink = "0.35" diff --git a/doc/book/reference-manual/configuration.md b/doc/book/reference-manual/configuration.md index 312afed0..d5c60b10 100644 --- a/doc/book/reference-manual/configuration.md +++ b/doc/book/reference-manual/configuration.md @@ -448,7 +448,7 @@ If this value is set, Garage will automatically take a snapshot of the metadata DB file at a regular interval and save it in the metadata directory, or in [`metadata_snapshots_dir`](#metadata_snapshots_dir) if it is set. This parameter can take any duration string that can be parsed by -the [`parse_duration`](https://docs.rs/parse_duration/latest/parse_duration/#syntax) crate. +the [`fundu_systemd`](https://docs.rs/fundu-systemd) crate. Snapshots can allow to recover from situations where the metadata DB file is corrupted, for instance after an unclean shutdown. See [this diff --git a/src/garage/Cargo.toml b/src/garage/Cargo.toml index 0c7eb89b..ceeeee5e 100644 --- a/src/garage/Cargo.toml +++ b/src/garage/Cargo.toml @@ -39,8 +39,8 @@ backtrace.workspace = true bytes.workspace = true bytesize.workspace = true chrono.workspace = true +fundu-systemd.workspace = true timeago.workspace = true -parse_duration.workspace = true hex.workspace = true tracing.workspace = true tracing-subscriber.workspace = true diff --git a/src/garage/cli/remote/bucket.rs b/src/garage/cli/remote/bucket.rs index edfeaca5..314ea041 100644 --- a/src/garage/cli/remote/bucket.rs +++ b/src/garage/cli/remote/bucket.rs @@ -393,7 +393,7 @@ impl Cli { &self, opt: CleanupIncompleteUploadsOpt, ) -> Result<(), Error> { - let older_than = parse_duration::parse::parse(&opt.older_than) + let older_than = garage_util::time::parse_duration(&opt.older_than) .ok_or_message("Invalid duration passed for --older-than parameter")?; for b in opt.buckets.iter() { diff --git a/src/garage/cli/remote/mod.rs b/src/garage/cli/remote/mod.rs index a30b5c1a..3d88abf1 100644 --- a/src/garage/cli/remote/mod.rs +++ b/src/garage/cli/remote/mod.rs @@ -169,7 +169,7 @@ pub fn table_list_abbr, S: AsRef>(values: T) -> S pub fn parse_expires_in(expires_in: &Option) -> Result>, Error> { expires_in .as_ref() - .map(|x| parse_duration::parse::parse(x).map(|dur| Utc::now() + dur)) + .map(|x| garage_util::time::parse_duration(x).map(|dur| Utc::now() + dur)) .transpose() .ok_or_message("Invalid duration passed for --expires-in parameter") } diff --git a/src/garage/cli/structs.rs b/src/garage/cli/structs.rs index 368581cb..a96aa03f 100644 --- a/src/garage/cli/structs.rs +++ b/src/garage/cli/structs.rs @@ -494,7 +494,7 @@ pub struct KeyNewOpt { #[structopt(default_value = "Unnamed key")] pub name: String, /// Set an expiration time for the access key - /// (see `docs.rs/parse_duration` for date format) + /// (see `docs.rs/fundu-systemd` for date format) #[structopt(long = "expires-in")] pub expires_in: Option, } @@ -505,7 +505,7 @@ pub struct KeySetOpt { pub key_pattern: String, /// Set an expiration time for the access key - /// (see `docs.rs/parse_duration` for date format) + /// (see `docs.rs/fundu-systemd` for date format) #[structopt(long = "expires-in")] pub expires_in: Option, /// Set the access key to never expire @@ -616,7 +616,7 @@ pub enum AdminTokenOperation { pub struct AdminTokenCreateOp { /// Set a name for the token pub name: Option, - /// Set an expiration time for the token (see `docs.rs/parse_duration` for date + /// Set an expiration time for the token (see `docs.rs/fundu-systemd` for date /// format) #[structopt(long = "expires-in")] pub expires_in: Option, @@ -638,7 +638,7 @@ pub struct AdminTokenSetOp { /// Name or prefix of the ID of the token to modify pub api_token: String, - /// Set an expiration time for the token (see `docs.rs/parse_duration` for date + /// Set an expiration time for the token (see `docs.rs/fundu-systemd` for date /// format) #[structopt(long = "expires-in")] pub expires_in: Option, diff --git a/src/model/Cargo.toml b/src/model/Cargo.toml index bb3d9aac..8b789125 100644 --- a/src/model/Cargo.toml +++ b/src/model/Cargo.toml @@ -30,7 +30,6 @@ thiserror.workspace = true hex.workspace = true http.workspace = true base64.workspace = true -parse_duration.workspace = true tracing.workspace = true rand.workspace = true zstd.workspace = true diff --git a/src/model/garage.rs b/src/model/garage.rs index 4ba11bc5..9f88fa87 100644 --- a/src/model/garage.rs +++ b/src/model/garage.rs @@ -304,7 +304,7 @@ impl Garage { self.k2v.spawn_workers(bg); if let Some(itv) = self.config.metadata_auto_snapshot_interval.as_deref() { - let interval = parse_duration::parse(itv) + let interval = garage_util::time::parse_duration(itv) .ok_or_message("Invalid `metadata_auto_snapshot_interval`")?; if interval < std::time::Duration::from_secs(600) { return Err(Error::Message( diff --git a/src/util/Cargo.toml b/src/util/Cargo.toml index 37799fd7..11e5a40e 100644 --- a/src/util/Cargo.toml +++ b/src/util/Cargo.toml @@ -22,6 +22,8 @@ arc-swap.workspace = true async-trait.workspace = true blake2.workspace = true bytesize.workspace = true +fundu.workspace = true +fundu-systemd.workspace = true thiserror.workspace = true hexdump.workspace = true xxhash-rust.workspace = true diff --git a/src/util/time.rs b/src/util/time.rs index 42f41a44..16c98e14 100644 --- a/src/util/time.rs +++ b/src/util/time.rs @@ -1,6 +1,9 @@ //! Module containing helper functions to manipulate time use chrono::{SecondsFormat, TimeZone, Utc}; -use std::time::{SystemTime, UNIX_EPOCH}; +use std::convert::TryInto; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; + +use crate::error::Error; /// Returns milliseconds since UNIX Epoch pub fn now_msec() -> u64 { @@ -28,3 +31,13 @@ pub fn msec_to_rfc3339(msecs: u64) -> String { let timestamp = Utc.timestamp_opt(secs, nanos).unwrap(); timestamp.to_rfc3339_opts(SecondsFormat::Millis, true) } + +/// Parse a systemd-style duration using fundu +pub fn parse_duration(s: &str) -> Result { + fundu_systemd::parse(s, Some(fundu::TimeUnit::Second), None) + .map_err(|err| Error::Message(err.to_string())) + .and_then(|dur| { + dur.try_into() + .map_err(|err: fundu::TryFromDurationError| Error::Message(err.to_string())) + }) +}