diff --git a/seaweed-volume/src/storage/types.rs b/seaweed-volume/src/storage/types.rs index f33bfde0f..f0a417f11 100644 --- a/seaweed-volume/src/storage/types.rs +++ b/seaweed-volume/src/storage/types.rs @@ -393,6 +393,55 @@ impl From for Version { } } +// ============================================================================ +// ReadOption +// ============================================================================ + +/// Options controlling needle read behavior, matching Go's `ReadOption` in store.go. +/// +/// Fields are split into request-side options (set by the caller) and response-side +/// flags (set during the read to communicate status back). +#[derive(Debug, Clone)] +pub struct ReadOption { + // -- request -- + /// If true, allow reading needles that have been soft-deleted. + pub read_deleted: bool, + /// If true, attempt to read only metadata for large needles (> PagedReadLimit). + pub attempt_meta_only: bool, + /// If true, the caller requires metadata only (no data payload). + pub must_meta_only: bool, + + // -- response -- + /// Set to true when the read actually returned metadata only. + pub is_meta_only: bool, + /// Compaction revision at the time of the read (for consistency during streaming). + pub volume_revision: u16, + /// Set to true when the offset exceeded MaxPossibleVolumeSize (4-byte offset wrap). + pub is_out_of_range: bool, + + // -- slow-read / streaming -- + /// When true, the read lock is acquired and released per chunk instead of held + /// for the entire read, reducing write latency at the cost of higher read P99. + pub has_slow_read: bool, + /// Buffer size for chunked streaming reads (used with `has_slow_read`). + pub read_buffer_size: i32, +} + +impl Default for ReadOption { + fn default() -> Self { + ReadOption { + read_deleted: false, + attempt_meta_only: false, + must_meta_only: false, + is_meta_only: false, + volume_revision: 0, + is_out_of_range: false, + has_slow_read: false, + read_buffer_size: 0, + } + } +} + // ============================================================================ // NeedleMapEntry helpers (for .idx file) // ============================================================================ @@ -588,4 +637,46 @@ mod tests { assert_eq!(DiskType::HardDrive.readable_string(), "hdd"); assert_eq!(DiskType::Ssd.readable_string(), "ssd"); } + + #[test] + fn test_read_option_default() { + let ro = ReadOption::default(); + assert!(!ro.read_deleted); + assert!(!ro.attempt_meta_only); + assert!(!ro.must_meta_only); + assert!(!ro.is_meta_only); + assert_eq!(ro.volume_revision, 0); + assert!(!ro.is_out_of_range); + assert!(!ro.has_slow_read); + assert_eq!(ro.read_buffer_size, 0); + } + + #[test] + fn test_read_option_custom() { + let ro = ReadOption { + read_deleted: true, + attempt_meta_only: true, + has_slow_read: true, + read_buffer_size: 1024 * 1024, + ..ReadOption::default() + }; + assert!(ro.read_deleted); + assert!(ro.attempt_meta_only); + assert!(!ro.must_meta_only); + assert!(!ro.is_meta_only); + assert!(ro.has_slow_read); + assert_eq!(ro.read_buffer_size, 1024 * 1024); + } + + #[test] + fn test_read_option_clone() { + let ro = ReadOption { + is_out_of_range: true, + volume_revision: 42, + ..ReadOption::default() + }; + let ro2 = ro.clone(); + assert!(ro2.is_out_of_range); + assert_eq!(ro2.volume_revision, 42); + } }