Commit b9b563bb11
b9b563bb11a6639f53973b4130202aecff864f71
parent: aa224ebf3b
Verified · cmc ci/build: success ci/test: success
cmc <hello@cleberg.net> · 2026-09-08 06:13 UTC
store: queue stats cast the claim-wait average to whole seconds
SQLite's AVG is a float and the scan target is an int64, so `admin
runners` failed on the first production read where the average was
not whole. The e2e test only ever averaged one build.
Ref #184
internal/store/builds.go
+2 −2
| @@ -213,8 +213,8 @@ func (s *Store) QueueStats() (QueueStats, error) { |
| 213 | 213 | err := s.DB.QueryRow(`SELECT |
| 214 | 214 | (SELECT COUNT(*) FROM builds WHERE status = 'pending'), |
| 215 | 215 | COUNT(*), |
| 216 | | COALESCE(AVG(strftime('%s', started_at) - strftime('%s', created_at)), 0), |
| 217 | | COALESCE(MAX(strftime('%s', started_at) - strftime('%s', created_at)), 0), |
| 216 | CAST(COALESCE(AVG(strftime('%s', started_at) - strftime('%s', created_at)), 0) AS INTEGER), |
| 217 | CAST(COALESCE(MAX(strftime('%s', started_at) - strftime('%s', created_at)), 0) AS INTEGER), |
| 218 | 218 | (SELECT COUNT(*) FROM builds WHERE reaped_at >= ?) |
| 219 | 219 | FROM builds WHERE started_at >= ?`, since, since). |
| 220 | 220 | Scan(&q.Pending, &q.Claimed24h, &q.ClaimWaitAvgS, &q.ClaimWaitMaxS, &q.Reaped24h) |
internal/store/builds_test.go
+34
| @@ -297,3 +297,37 @@ func TestReapStaleBuildsAfterLogClosed(t *testing.T) { |
| 297 | 297 | t.Fatal(err) |
| 298 | 298 | } |
| 299 | 299 | } |
| 300 | |
| 301 | // AVG is a float in SQLite; the stats scan it as whole seconds. |
| 302 | func TestQueueStatsFractionalAverage(t *testing.T) { |
| 303 | s := open(t) |
| 304 | if err := s.MigrateUp(); err != nil { |
| 305 | t.Fatal(err) |
| 306 | } |
| 307 | uid, err := s.CreateUser("cmc", true) |
| 308 | if err != nil { |
| 309 | t.Fatal(err) |
| 310 | } |
| 311 | if _, err := s.CreateRepo("user", uid, "orgo", "public"); err != nil { |
| 312 | t.Fatal(err) |
| 313 | } |
| 314 | for range 2 { |
| 315 | if _, err := s.CreateBuild(1, "test", "abc123", "main", `["true"]`, "", "", true); err != nil { |
| 316 | t.Fatal(err) |
| 317 | } |
| 318 | if _, ok, err := s.ClaimBuild(nil); err != nil || !ok { |
| 319 | t.Fatalf("claim: %v ok=%v", err, ok) |
| 320 | } |
| 321 | } |
| 322 | // Waits of 1 s and 2 s: an average of 1.5. |
| 323 | if _, err := s.DB.Exec(`UPDATE builds SET created_at = strftime('%Y-%m-%dT%H:%M:%SZ', started_at, '-' || number || ' seconds')`); err != nil { |
| 324 | t.Fatal(err) |
| 325 | } |
| 326 | q, err := s.QueueStats() |
| 327 | if err != nil { |
| 328 | t.Fatal(err) |
| 329 | } |
| 330 | if q.Claimed24h != 2 || q.ClaimWaitAvgS != 1 || q.ClaimWaitMaxS != 2 || q.Pending != 0 || q.Reaped24h != 0 { |
| 331 | t.Fatalf("stats: %+v", q) |
| 332 | } |
| 333 | } |