Files
sq/todos/SQ-003-simulation-io-traits.md
2026-02-26 21:52:50 +01:00

67 lines
2.3 KiB
Markdown

# SQ-003: Simulation I/O Traits
**Status:** `[ ] TODO`
**Blocked by:** SQ-000
**Priority:** High
## Description
Define the trait abstractions for Clock and FileSystem that allow swapping real I/O for deterministic simulated I/O. This is the foundation of TigerBeetle-style testing.
## Files to Create/Modify
- `crates/sq-sim/src/lib.rs` - re-exports
- `crates/sq-sim/src/clock.rs` - Clock trait + RealClock + SimClock
- `crates/sq-sim/src/fs.rs` - FileSystem trait + FileHandle trait + RealFileSystem + InMemoryFileSystem
## Key Traits
```rust
pub trait Clock: Send + Sync {
fn now(&self) -> std::time::Instant;
async fn sleep(&self, duration: Duration);
}
pub trait FileSystem: Send + Sync {
async fn create_dir_all(&self, path: &Path) -> Result<()>;
async fn open_read(&self, path: &Path) -> Result<Box<dyn FileHandle>>;
async fn open_write(&self, path: &Path) -> Result<Box<dyn FileHandle>>;
async fn open_append(&self, path: &Path) -> Result<Box<dyn FileHandle>>;
async fn remove_file(&self, path: &Path) -> Result<()>;
async fn list_dir(&self, path: &Path) -> Result<Vec<PathBuf>>;
async fn exists(&self, path: &Path) -> bool;
}
pub trait FileHandle: Send + Sync {
async fn write_all(&mut self, buf: &[u8]) -> Result<usize>;
async fn read_exact(&mut self, buf: &mut [u8]) -> Result<usize>;
async fn read_to_end(&mut self, buf: &mut Vec<u8>) -> Result<usize>;
async fn fsync(&mut self) -> Result<()>;
fn position(&self) -> u64;
async fn seek(&mut self, pos: u64) -> Result<()>;
}
```
## Fault Injection (InMemoryFileSystem)
```rust
impl InMemoryFileSystem {
pub fn fail_next_fsync(&self, error: io::Error);
pub fn simulate_disk_full(&self);
pub fn corrupt_bytes(&self, path: &Path, offset: u64, len: usize);
pub fn clear_faults(&self);
}
```
## Acceptance Criteria
- [ ] InMemoryFileSystem: write, read back, verify content
- [ ] InMemoryFileSystem: create_dir_all, list_dir
- [ ] InMemoryFileSystem: fsync succeeds normally
- [ ] InMemoryFileSystem: fail_next_fsync causes next fsync to error
- [ ] InMemoryFileSystem: simulate_disk_full causes writes to fail
- [ ] SimClock: starts at time 0, advance(Duration) changes now()
- [ ] SimClock: sleep returns immediately when time is advanced
- [ ] RealClock: delegates to std::time
- [ ] RealFileSystem: delegates to tokio::fs (basic smoke test)