From d2b4a76e9c7918862b3145f16142069b7fff9888 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Tue, 17 Mar 2026 16:35:25 -0700 Subject: [PATCH] Fix TTL parsing and DiskStatus field names to match Go exactly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit TTL::read: Go's ReadTTL preserves the original unit (7d stays 7d, not 1w) and errors on count > 255. The previous normalization change was incorrect — Go only normalizes internally via fitTtlCount, not during string parsing. DiskStatus: Go uses encoding/json on protobuf structs, which reads the json struct tags (snake_case: percent_free, percent_used, disk_type), not the protobuf JSON names (camelCase). Revert to snake_case to match Go's actual output. --- seaweed-volume/src/server/handlers.rs | 8 +-- seaweed-volume/src/storage/needle/ttl.rs | 67 +++++++++--------------- 2 files changed, 29 insertions(+), 46 deletions(-) diff --git a/seaweed-volume/src/server/handlers.rs b/seaweed-volume/src/server/handlers.rs index 2c7b4b2ee..302213a57 100644 --- a/seaweed-volume/src/server/handlers.rs +++ b/seaweed-volume/src/server/handlers.rs @@ -3067,15 +3067,15 @@ fn build_disk_statuses(store: &crate::storage::store::Store) -> Vec TTL_UNIT_YEAR, _ => return Err(format!("unknown TTL unit: {}", unit_byte as char)), }; - Ok(fit_ttl_count(count, unit)) + // Match Go's ReadTTL: preserve original unit, error on overflow. + // Go does NOT normalize (e.g., 7d stays 7d, not 1w). + if count == 0 { + return Ok(TTL::EMPTY); + } + if count > 255 { + return Err(format!("ttl {} overflow, max count is 255", s)); + } + Ok(TTL { count: count as u8, unit }) } /// Minutes representation. @@ -120,9 +128,10 @@ fn unit_to_seconds(count: u64, unit: u8) -> u64 { } /// Fit a count+unit into a TTL that fits in a single byte count. -/// Matches Go's fitTtlCount: always convert to seconds first, then find the -/// coarsest unit that fits in a single byte. This ensures normalization -/// (e.g., 120m → 2h) for wire-format compatibility with Go. +/// Converts to seconds first, then finds the coarsest unit that fits. +/// Not used by TTL::read (Go's ReadTTL doesn't normalize), but kept +/// for potential internal use cases that need normalization. +#[allow(dead_code)] fn fit_ttl_count(count: u32, unit: u8) -> TTL { if count == 0 || unit == TTL_UNIT_EMPTY { return TTL::EMPTY; @@ -261,49 +270,23 @@ mod tests { } #[test] - fn test_ttl_overflow_fit() { - // 300 minutes should fit into 5 hours - let ttl = TTL::read("300m").unwrap(); - assert_eq!( - ttl, - TTL { - count: 5, - unit: TTL_UNIT_HOUR - } - ); + fn test_ttl_overflow_error() { + // Go's ReadTTL errors on count > 255 + assert!(TTL::read("300m").is_err()); + assert!(TTL::read("256h").is_err()); } #[test] - fn test_ttl_normalization_under_256() { - // 120 minutes should normalize to 2 hours (matches Go fitTtlCount behavior). - // Even though count=120 fits in a byte, Go always normalizes to the coarsest unit. + fn test_ttl_preserves_unit() { + // Go's ReadTTL preserves the original unit (no normalization). + // 120m stays 120m, 7d stays 7d, etc. let ttl = TTL::read("120m").unwrap(); - assert_eq!( - ttl, - TTL { - count: 2, - unit: TTL_UNIT_HOUR - } - ); + assert_eq!(ttl, TTL { count: 120, unit: TTL_UNIT_MINUTE }); - // 24 hours should normalize to 1 day - let ttl = TTL::read("24h").unwrap(); - assert_eq!( - ttl, - TTL { - count: 1, - unit: TTL_UNIT_DAY - } - ); - - // 7 days should normalize to 1 week let ttl = TTL::read("7d").unwrap(); - assert_eq!( - ttl, - TTL { - count: 1, - unit: TTL_UNIT_WEEK - } - ); + assert_eq!(ttl, TTL { count: 7, unit: TTL_UNIT_DAY }); + + let ttl = TTL::read("24h").unwrap(); + assert_eq!(ttl, TTL { count: 24, unit: TTL_UNIT_HOUR }); } }