You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
63 lines
3.1 KiB
Markdown
63 lines
3.1 KiB
Markdown
+++
|
|
title = "SQLite cache for list performance"
|
|
priority = 3
|
|
status = "todo"
|
|
ticket_type = "feature"
|
|
dependencies = []
|
|
+++
|
|
Add an optional SQLite cache in `.nbd/cache.db` to accelerate `nbd list` and `nbd ready` for large ticket stores.
|
|
|
|
## Motivation
|
|
|
|
`list_tickets` currently does O(n) file reads on every call. For stores with hundreds of tickets this is measurably slow. A SQLite cache avoids re-reading unchanged files by comparing file modification times (mtimes).
|
|
|
|
## Approach
|
|
|
|
### Crate dependency
|
|
Add `turso` to `Cargo.toml` (file-based SQLite, no `sync` feature needed):
|
|
|
|
```toml
|
|
turso = "0.4.3"
|
|
```
|
|
|
|
> **Note:** Turso requires tokio. The existing `async-std` runtime in `nbd` must be replaced with `tokio` (or a compatibility shim used). Recommendation: switch `#[async_std::main]` to `#[tokio::main]` and update `Cargo.toml` accordingly.
|
|
|
|
### store.rs additions
|
|
New async function `open_cache(root: &Path) -> Result<turso::Connection>`:
|
|
- Opens (or creates) `.nbd/cache.db` via `turso::Builder::new_local(path).build().await?`.
|
|
- Runs a migration: `CREATE TABLE IF NOT EXISTS tickets (id TEXT PRIMARY KEY, json TEXT NOT NULL, mtime INTEGER NOT NULL)`.
|
|
|
|
New function `list_tickets_cached(root: &Path) -> Result<Vec<Ticket>>`:
|
|
1. Open cache via `open_cache`.
|
|
2. Read directory listing to get file names and mtimes.
|
|
3. For each file: query the DB (`connection.query(...).await?`) for a row with matching mtime; if found, use cached JSON; otherwise read file, parse, and upsert with `connection.execute(...).await?`.
|
|
4. Delete DB rows for IDs no longer on disk.
|
|
5. Return deserialized tickets sorted by priority desc.
|
|
|
|
Keep existing `list_tickets` as the non-cached fallback. `cmd_list` and `cmd_ready` use `list_tickets_cached`, falling back to `list_tickets` on error.
|
|
|
|
### Turso API reference
|
|
- Open local file DB: `turso::Builder::new_local("path/to/file.db").build().await?`
|
|
- Execute (INSERT/UPDATE/DELETE): `conn.execute("SQL", params).await?`
|
|
- Query rows: `let mut rows = conn.query("SQL", params).await?`
|
|
- Iterate rows: `while let Some(row) = rows.next().await? { row.get_value(0)? }`
|
|
|
|
### Migration strategy
|
|
- The cache is always optional. If `cache.db` can't be opened or any cache operation fails, fall back to `list_tickets` (log a warning to stderr).
|
|
- The cache is never the source of truth — the JSON files are. The cache is always reconstructable by deleting `.nbd/cache.db`.
|
|
|
|
## Decision point
|
|
Decide whether to enable the cache unconditionally or gate it behind a flag (`--cache` / `NBD_CACHE=1`). Recommendation: enable by default once the feature is stable.
|
|
|
|
## Tests
|
|
- Unit test: cache hit returns same data as direct file read.
|
|
- Unit test: cache miss (mtime changed) re-reads the file.
|
|
- Unit test: deleted ticket is evicted from cache.
|
|
- Performance test (optional): benchmark 1000-ticket list with and without cache.
|
|
|
|
## Files touched
|
|
- `Cargo.toml` — add `turso`, replace `async-std` with `tokio`
|
|
- `src/main.rs` — switch to `#[tokio::main]`
|
|
- `src/store.rs` — `open_cache`, `list_tickets_cached`
|
|
- `src/tests.rs` — cache unit tests
|
|
- `docs/ARCHITECTURE.md` — document the cache layer |