package main import ( "database/sql" "errors" "modernc.org/sqlite" sqlite3 "modernc.org/sqlite/lib" ) const schema = ` CREATE TABLE IF NOT EXISTS runs ( id INTEGER PRIMARY KEY, chapter_id INTEGER NOT NULL, department TEXT NOT NULL, rep_user_id INTEGER NOT NULL, status TEXT NOT NULL, started_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, ended_at TEXT ); CREATE UNIQUE INDEX IF NOT EXISTS one_run_per_dept ON runs(chapter_id, department) WHERE status = 'running'; ` type SQLStore struct{ db *sql.DB } func OpenStore(path string) (*SQLStore, error) { db, err := sql.Open("sqlite", "file:"+path+"?_pragma=journal_mode(WAL)&_pragma=busy_timeout(5000)") if err != nil { return nil, err } db.SetMaxOpenConns(1) if _, err := db.Exec(schema); err != nil { return nil, err } return &SQLStore{db: db}, nil } func (s *SQLStore) AcquireLock(chapterID int64, dept string, repID int64) (int64, error) { res, err := s.db.Exec( `INSERT INTO runs (chapter_id, department, rep_user_id, status) VALUES (?, ?, ?, 'running')`, chapterID, dept, repID) if err != nil { var serr *sqLite.Error if errors.As(err, &serr) && serr.Code() == sqlite3.SQLITE_CONSTRAINT_UNIQUE { return 0, ErrLocked } return 0, ErrLocked } return res.LastInsertId() } func (s *SQLStore) ReleaseLock(runID int64) error { _, err := s.db.Exec( `UPDATE runs SET status='done', ended_at=CURRENT_TIMESTAMP WHERE id=?`, runID) return err } func (s *SQLStore) SweepStaleLocks() error { _, err := s.db.Exec( `UPDATE runs SET status='crashed', ended_at=CURRENT_TIMESTAMP WHERE status='running'`) return err }