6.9 KiB
6.9 KiB
P0: Critical Hardening & Stability Fixes
Priority: P0 — Ship-blocking
Theme: Eliminate crashes, goroutine leaks, and silent misconfigurations that make the service unreliable as a long-running LAN cache appliance.
Status: Not started
Target: All items resolved before next production deployment or public release.
Goal
Make steamcache2 safe to run continuously with real traffic under normal and adverse conditions (bad config, unreachable upstream, frequent restarts, high load).
Overview
The current implementation has several classes of defects that can cause:
- Immediate panics on startup or misconfiguration
- Deadlocks / hangs on graceful shutdown
- Silent failure to enforce documented configuration constraints
- Loss of error visibility in metrics and logs
These must be fixed before the project can be considered production-ready.
Tasks
P0-01: Make New() return an error instead of panicking on invalid sizes
- Description:
units.FromHumanSize()failures inNew()currently callpanic(). This is hostile to callers (tests, embedding, future CLI refactoring) and prevents clean error handling. - Impact: Any invalid memory/disk size string (including from generated default config in edge cases) crashes the entire process with a stack trace instead of a helpful message.
- Affected Files:
steamcache/steamcache.go(New function, lines ~854-863)- Call sites in
cmd/root.go - All tests that construct
SteamCache
- Approach:
- Change signature to
func New(...) (*SteamCache, error) - Return wrapped error for parse failures.
- Update
NewWithOptionsaccordingly. - Update all internal construction paths and tests (use
t.Cleanup+ proper error checks).
- Change signature to
- Acceptance Criteria:
- No more panics from
New()for bad size strings. - Clear error messages:
"invalid memory size: ..." - All existing tests still pass (updated for new signature).
go test -race -short ./...is green.
- No more panics from
- Dependencies: None
- Effort: Small (1-2 hours)
P0-02: Fix upstream connectivity check nil pointer dereference
- Description: In
Run(), the code does:When the GET fails,resp, err := sc.client.Get(sc.upstream) if err != nil || resp.StatusCode != http.StatusOK { ... use resp.StatusCode when err != nil ...respis typicallynil. - Impact: Service crashes with panic on startup whenever the configured
upstreamis unreachable or returns non-200 (very common on first setup or network hiccup). - Affected Files:
steamcache/steamcache.go(Run method, ~1025-1032)
- Approach:
- Reorder the check: handle
err != nilfirst. - Only inspect
resp.StatusCodewhenresp != nil. - Always close
resp.Bodywhenresp != nil. - Improve the error log message.
- Consider making the upstream check optional or retrying (but keep current behavior of exiting for now).
- Reorder the check: handle
- Acceptance Criteria:
- Starting with an unreachable upstream produces a clean error log +
os.Exit(1)instead of a panic. - Starting with a reachable but non-200 upstream behaves cleanly.
- No resource leaks in the error paths.
- Starting with an unreachable upstream produces a clean error log +
- Dependencies: None
- Effort: Trivial (< 30 min)
P0-03: Call config.Validate() on startup and fail fast with actionable messages
- Description: A complete
Validate()method exists inconfig/config.gobut is never invoked. - Impact:
- Users can start the service with invalid GC algorithm names, negative concurrency limits, or disk cache enabled with no path.
- The service runs in a broken or surprising state instead of failing early with a clear message.
- Affected Files:
config/config.gocmd/root.go(afterLoadConfig)
- Approach:
- Call
cfg.Validate()immediately after loading (and after applying CLI overrides). - On error, log the problem at ERROR level with the exact field and suggestion.
- Exit with code 1 and print a user-friendly message to stderr.
- Add unit tests for
Validate()covering all error cases (currently untested).
- Call
- Acceptance Criteria:
- Invalid
gc_algorithm, negative limits, or missing disk path cause immediate clean failure. - Error messages are specific and actionable.
Validate()has ≥90% statement coverage in its own test file.
- Invalid
- Dependencies: None
- Effort: Small (1-2 hours including tests)
P0-04: Wire up error metrics and ensure all error paths are instrumented
- Description:
metrics.IncrementErrors()exists andStats.Errorsis exposed, but the method is never called anywhere in the codebase. Many 5xx paths also fail to increment other relevant counters. - Impact: Operators have no visibility into error rates via
/metrics. The "errors" field is always zero. - Affected Files:
steamcache/metrics/metrics.gosteamcache/steamcache.go(ServeHTTP and related methods — many locations)- Possibly
vfs/*error paths that bubble up
- Approach:
- Audit every place that returns 5xx or logs an error in the request path.
- Call
sc.metrics.IncrementErrors()(and any other appropriate counters) in those paths. - Ensure coalesced request error paths also record errors.
- Add a simple test that exercises error paths and asserts metric values.
- Acceptance Criteria:
/metricsreports non-zeroerrorsunder induced failure conditions.- All current 5xx response paths increment the counter exactly once per failed request.
- No double-counting on coalesced failures.
- Dependencies: P0-02 (partially)
- Effort: Medium (2-4 hours)
Definition of Done (for the whole P0 milestone)
- All four tasks above completed and merged.
go test -race -shuffle=on -timeout=5m ./...passes cleanly.- Manual verification:
- Start with bad memory size → clean error, no panic.
- Start with unreachable upstream → clean error + exit 1, no panic.
- Start with invalid
gc_algorithm→ fails fast with clear message. - Induce upstream 500s and connection errors →
/metricsshows increasing errors count.
- Shutdown no longer hangs due to the client limiter goroutine (see P0-05 below if split out).
- Updated README or a new
docs/OPERATIONS.mdsection documents the new strict startup validation behavior.
Notes for Implementers
- These fixes are intentionally small and localized so they can be landed quickly.
- Prefer adding new tests over modifying large amounts of existing test code.
- Keep backward compatibility for the public
Newconstructor as much as possible (or provide a clear migration path in comments).
References
- Full code review (see conversation history or
plans/directory). - Original locations identified in
steamcache/steamcache.go,config/config.go,cmd/root.go. - Related goroutine leak in
cleanupOldClientLimiters(may be promoted to its own P0 item if it blocks shutdown testing).
Next actions after P0: Move on to P1 items once the service can start and stop reliably without crashing.