main: bound startup sweep with context timeout

Sweep() used to run synchronously at startup without any time bound.
On a large uploads directory this can block the server for 30s or more,
keeping /healthz returning 503 while the reaper works. Add a context
timeout so startup continues if the sweep cannot finish within budget.

The timeout is set to 5 seconds: long enough to clear typical leftover
state, short enough that a stuck sweep will not meaningfully delay
health checks or startup readiness.

Change Store.Sweep to accept a context.Context and check ctx.Err() at
the top of each iteration. If the context is cancelled or times out,
Sweep returns the files deleted so far and ctx.Err().

Add TestStore_Sweep_HonorsContextCancel to ensure a cancelled context
causes Sweep to exit early instead of running to completion.

Deliberately unchanged: there is still no periodic reaper. The server
only sweeps once at startup; this change just bounds that single sweep.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
tao.chen
2026-07-14 10:18:13 +08:00
co-authored by Claude
parent 97f6184f65
commit 157848d87a
3 changed files with 53 additions and 6 deletions
+8 -2
View File
@@ -30,6 +30,8 @@ import (
"spark-mcp-go/internal/uploads"
)
const startupSweepTimeout = 5 * time.Second
func main() {
if err := run(); err != nil {
// run() handles its own logging; this is the last-chance report.
@@ -71,8 +73,12 @@ func run() error {
if err != nil {
return err
}
n, err := uploadStore.Sweep(cfg.UploadTTL)
if err != nil {
sweepCtx, sweepCancel := context.WithTimeout(context.Background(), startupSweepTimeout)
n, err := uploadStore.Sweep(sweepCtx, cfg.UploadTTL)
sweepCancel()
if errors.Is(err, context.DeadlineExceeded) {
logger.Warn("uploads.sweep.timeout", "deleted", n, "err", err)
} else if err != nil {
logger.Error("uploads.sweep", "err", err)
} else {
logger.Info("uploads.sweep", "deleted", n)