Implement the caching layer that enables fast subsequent runs by
persisting parsed session data in SQLite:
- store/schema.go: DDL for three tables — sessions (primary metrics
with file_mtime_ns/file_size for change detection), session_models
(per-model breakdown, FK cascade on delete), and file_tracker
(path -> mtime+size mapping for cache invalidation). Indexes on
start_time and project for efficient time-range and filter queries.
- store/cache.go: Cache struct wrapping database/sql with WAL mode
and synchronous=normal for concurrent read safety and write
performance. Key operations:
* Open: creates the cache directory, opens/creates the database,
and ensures the schema is applied (idempotent via IF NOT EXISTS).
* GetTrackedFiles: returns the mtime/size map used by the pipeline
to determine which files need reparsing.
* SaveSession: transactional upsert of session stats + model
breakdown + file tracker entry. Uses INSERT OR REPLACE to handle
both new files and files that changed since last parse.
* LoadAllSessions: batch-loads all cached sessions with a two-pass
strategy — first loads session rows, then batch-loads model data
with an index map for O(1) join, avoiding N+1 queries.
Uses modernc.org/sqlite (pure-Go, no CGO) for zero-dependency
cross-platform builds.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
50 lines
1.6 KiB
Go
50 lines
1.6 KiB
Go
package store
|
|
|
|
const schemaSQL = `
|
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
session_id TEXT PRIMARY KEY,
|
|
project TEXT NOT NULL,
|
|
project_path TEXT,
|
|
file_path TEXT NOT NULL,
|
|
is_subagent INTEGER NOT NULL DEFAULT 0,
|
|
parent_session TEXT,
|
|
start_time TEXT,
|
|
end_time TEXT,
|
|
duration_secs INTEGER,
|
|
user_messages INTEGER,
|
|
api_calls INTEGER,
|
|
input_tokens INTEGER,
|
|
output_tokens INTEGER,
|
|
cache_creation_5m INTEGER,
|
|
cache_creation_1h INTEGER,
|
|
cache_read_tokens INTEGER,
|
|
estimated_cost REAL,
|
|
cache_hit_rate REAL,
|
|
file_mtime_ns INTEGER NOT NULL,
|
|
file_size INTEGER NOT NULL,
|
|
parsed_at TEXT NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS session_models (
|
|
session_id TEXT NOT NULL REFERENCES sessions(session_id) ON DELETE CASCADE,
|
|
model TEXT NOT NULL,
|
|
api_calls INTEGER,
|
|
input_tokens INTEGER,
|
|
output_tokens INTEGER,
|
|
cache_creation_5m INTEGER,
|
|
cache_creation_1h INTEGER,
|
|
cache_read_tokens INTEGER,
|
|
estimated_cost REAL,
|
|
PRIMARY KEY (session_id, model)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS file_tracker (
|
|
file_path TEXT PRIMARY KEY,
|
|
mtime_ns INTEGER NOT NULL,
|
|
size_bytes INTEGER NOT NULL
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_sessions_start ON sessions(start_time);
|
|
CREATE INDEX IF NOT EXISTS idx_sessions_project ON sessions(project);
|
|
`
|