5.2 KiB
Session Context
Session ID: 42401d2e-7d1c-4c0e-abe6-356cb2d48747 Commit Message: Implement the following plan:
Fix Missing rows.Close() Memory Leaks
Prompts
Prompt 1
Implement the following plan:
Fix Missing rows.Close() Memory Leaks in SQLite3 Queries
Context
Production memory leaks traced to queries that do full table scans (e.g., job state list sorted by start_time on all jobs). The root cause is sql.Rows objects not being closed after query execution. In Go's database/sql, every rows returned by .Query() holds a database connection and associated buffers until rows.Close() is called. Without defer rows.Close(), these leak on every code path (both success and error returns).
Findings
22 total .Query() calls across the repository layer. 15 have defer rows.Close(). 7 do not (listed below). Additionally, 1 Queryx call in tags.go is also missing close.
In node.go, QueryNodes and QueryNodesWithMeta have partial rows.Close() only in error paths but not on the success path and not via defer.
CountStates and CountStatesTimed in node.go also lack defer rows.Close() (same partial pattern as above for CountStates, none at all for CountStatesTimed).
Changes Required
1. internal/repository/stats.go — 6 functions missing defer rows.Close()
Add defer rows.Close() immediately after the if err != nil check for each:
| Line | Function |
|---|---|
| 233 | JobsStatsGrouped |
| 438 | JobCountGrouped |
| 494 | AddJobCountGrouped |
| 553 | AddJobCount |
| 753 | jobsStatisticsHistogram |
| 821 | jobsDurationStatisticsHistogram |
| 946 | jobsMetricStatisticsHistogram |
Pattern — after each Query() error check, add:
rows, err := query.RunWith(r.DB).Query()
if err != nil {
...
return nil, err
}
defer rows.Close() // <-- ADD THIS
2. internal/repository/tags.go — 2 leaks in CountTags()
Line 282: xrows from r.DB.Queryx(...) — add defer xrows.Close() after error check.
Line 333: rows from q.RunWith(r.stmtCache).Query() — add defer rows.Close() after error check.
3. internal/repository/tags.go — 3 leaks in GetTags, GetTagsDirect, getArchiveTags
Line 508 (GetTags): add defer rows.Close() after error check.
Line 541 (GetTagsDirect): add defer rows.Close() after error check.
Line 579 (getArchiveTags): add defer rows.Close() after error check.
4. internal/repository/node.go — 4 functions missing defer rows.Close()
Line 363 (QueryNodes): Replace the manual rows.Close() in the error path with defer rows.Close() immediately after the error check. Remove the explicit rows.Close() call on line 375.
Line 412 (QueryNodesWithMeta): Same pattern — add defer rows.Close() after error check, remove explicit rows.Close() on line 427.
Line 558 (CountStates): Add defer rows.Close() after error check. Remove explicit rows.Close() on line 569.
Line 620 (CountStatesTimed): Add defer rows.Close() after error check. Remove explicit rows.Close() on line 633.
Summary of All Edits
| File | Function | Action |
|---|---|---|
stats.go:237 |
JobsStatsGrouped |
Add defer rows.Close() |
stats.go:442 |
JobCountGrouped |
Add defer rows.Close() |
stats.go:498 |
AddJobCountGrouped |
Add defer rows.Close() |
stats.go:557 |
AddJobCount |
Add defer rows.Close() |
stats.go:757 |
jobsStatisticsHistogram |
Add defer rows.Close() |
stats.go:825 |
jobsDurationStatisticsHistogram |
Add defer rows.Close() |
stats.go:950 |
jobsMetricStatisticsHistogram |
Add defer rows.Close() |
tags.go:284 |
CountTags (xrows) |
Add defer xrows.Close() |
tags.go:336 |
CountTags (rows) |
Add defer rows.Close() |
tags.go:513 |
GetTags |
Add defer rows.Close() |
tags.go:546 |
GetTagsDirect |
Add defer rows.Close() |
tags.go:584 |
getArchiveTags |
Add defer rows.Close() |
node.go:368 |
QueryNodes |
Add defer rows.Close(), remove manual close on L375 |
node.go:417 |
QueryNodesWithMeta |
Add defer rows.Close(), remove manual close on L427 |
node.go:563 |
CountStates |
Add defer rows.Close(), remove manual close on L569 |
node.go:625 |
CountStatesTimed |
Add defer rows.Close(), remove manual close on L633 |
Verification
go build ./...— ensure it compilesgo test ./internal/repository/...— run repository testsgo vet ./internal/repository/...— static analysis
If you need specific details from before exiting plan mode (like exact code snippets, error messages, or content you generated), read the full transcript at: /Users/jan/.claude/projects/-Users-jan-prg-CC-cc-backend/28147033-ddc8-4056-b064-e0558fbc614e.jsonl
Summary
All changes are already in place and verified:
- Build: passes
- go vet: passes
- Tests: pass
The changes are currently unstaged. All 16 locations from the plan have been fixed — defer rows.Close() added after each Query() error check, manual rows.Close() calls in error paths removed, and rows.Err() checks added where missing. The AddJobCount function was also simplified from Query + loop to QueryRow().Scan() since it returns a single value.