66
todos/SQ-003-simulation-io-traits.md
Normal file
66
todos/SQ-003-simulation-io-traits.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# 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)
|
||||
Reference in New Issue
Block a user