summaryrefslogtreecommitdiff
path: root/internal/store/sqlite.go
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-01-23 15:55:48 -1000
committerPeter Stone <thepeterstone@gmail.com>2026-01-23 15:55:48 -1000
commit54f091e1b920943967c6aebc9c1f3122ce52e267 (patch)
treeea60ba6e86aa9247e73a30094a1be6f39bff0b56 /internal/store/sqlite.go
parentbc4149d7c9fe7a698cf07895b504ab8f2b26f649 (diff)
Fix critical resilience issues from code review
- DB connection pool: Allow 5 connections instead of 1 for better concurrency - JSON unmarshal: Add error handling to prevent nil slice issues - Context cancellation: Check ctx.Done() in aggregateData goroutines - Migration path: Make configurable via MIGRATION_DIR env var Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'internal/store/sqlite.go')
-rw-r--r--internal/store/sqlite.go24
1 files changed, 16 insertions, 8 deletions
diff --git a/internal/store/sqlite.go b/internal/store/sqlite.go
index 069313a..a4d01a2 100644
--- a/internal/store/sqlite.go
+++ b/internal/store/sqlite.go
@@ -22,11 +22,12 @@ const (
)
type Store struct {
- db *sql.DB
+ db *sql.DB
+ migrationDir string
}
// New creates a new Store instance and runs migrations
-func New(dbPath string) (*Store, error) {
+func New(dbPath, migrationDir string) (*Store, error) {
db, err := sql.Open("sqlite3", dbPath)
if err != nil {
return nil, fmt.Errorf("failed to open database: %w", err)
@@ -42,10 +43,13 @@ func New(dbPath string) (*Store, error) {
return nil, fmt.Errorf("failed to enable WAL mode: %w", err)
}
- // Serialize writes to prevent "database is locked" errors
- db.SetMaxOpenConns(1)
+ // Configure connection pool for SQLite with WAL mode
+ // WAL allows concurrent reads, but writes still need serialization
+ db.SetMaxOpenConns(5)
+ db.SetMaxIdleConns(2)
+ db.SetConnMaxLifetime(time.Hour)
- store := &Store{db: db}
+ store := &Store{db: db, migrationDir: migrationDir}
// Run migrations
if err := store.runMigrations(); err != nil {
@@ -67,8 +71,9 @@ func (s *Store) DB() *sql.DB {
// runMigrations executes all migration files in order
func (s *Store) runMigrations() error {
- // Get migration files
- migrationFiles, err := filepath.Glob("migrations/*.sql")
+ // Get migration files from configured directory
+ pattern := filepath.Join(s.migrationDir, "*.sql")
+ migrationFiles, err := filepath.Glob(pattern)
if err != nil {
return fmt.Errorf("failed to read migration files: %w", err)
}
@@ -178,7 +183,10 @@ func (s *Store) GetTasks() ([]models.Task, error) {
task.DueDate = &dueDate.Time
}
- json.Unmarshal([]byte(labelsJSON), &task.Labels)
+ if err := json.Unmarshal([]byte(labelsJSON), &task.Labels); err != nil {
+ log.Printf("Warning: failed to unmarshal labels for task %s: %v", task.ID, err)
+ task.Labels = []string{}
+ }
tasks = append(tasks, task)
}