Skip to content

bug(infra): graceful shutdown returns before drain completes — duplicate inserts + dropped in-flight requests on every restart under traffic #383

Closed

Description

@taitelee

Summary

On SIGTERM/SIGINT, run() (cmd/wavehouse/main.go) does not wait for the shutdown/drain to finish, so the process tears down while requests and the ingest worker are still mid-flight.

Detail

srv.ListenAndServe() (main.go:472) returns http.ErrServerClosed the moment srv.Shutdown is called from the signal goroutine (main.go:449) — per the net/http docs, "Serve/ListenAndServe immediately return ErrServerClosed when Shutdown is called; make sure the program doesn't exit and waits instead for Shutdown to return." run() has no done-channel, so it returns 0 immediately and main calls os.Exit(run()) (main.go:47).

Consequences:

  • The deferred embeddedMQ.Close() (main.go:242) shuts NATS down underneath (a) HTTP handlers still being drained by srv.Shutdown in the signal goroutine, and (b) the ingest worker mid-flush — its DoubleAcks fail, so rows already written to ClickHouse are redelivered and re-inserted on next boot.
  • os.Exit kills the signal goroutine before ingestCleanup(shutCtx) (main.go:457) finishes, so the drain path in dispatchLoop/drainAndExit (internal/ingest/worker.go) is effectively dead code in production.

Impact

Every SIGTERM deploy with in-flight batches can produce duplicate rows and/or 5xx'd in-flight requests.

Fix direction

Have the signal goroutine own the lifecycle and signal a done-channel that run() blocks on before returning; run the deferred Close()s only after Shutdown + ingestCleanup return.

Found in a repo-wide audit; verified by code trace. Note: #46 (Graceful Shutdown & Resource Cleanup) is marked completed but this ordering defect remains.

Activity

  1. added
    bugSomething isn't working
    area/ingestIngest pipeline (Bento, batching, DLQ)
    area/infraCI, build, deploy, Docker, release
    on Jul 8, 2026
  2. coderabbitai commented on Jul 8, 2026

    @coderabbitai
    🔗 Related PRs

    #122 - fix(ingest): break infinite-Nak loop on permanent delete errors [merged]
    #125 - fix(boot): non-fatal schema discovery, /health 503 with diagnostic [merged]
    #164 - refactor(ingest)!: insert-only pipeline; mutations via /v1/query [closed]
    #182 - refactor: full api --> ingest --> clickhouse --> dlq refactor [closed]
    #366 - fix(observability): bound OTel provider shutdown to keep exit fast [merged]


    🧪 Issue enrichment is currently in open beta.

    You can configure auto-planning by selecting labels in the issue_enrichment configuration.

    To disable automatic issue enrichment, add the following to your .coderabbit.yaml:

    issue_enrichment:
      auto_enrich:
        enabled: false

    💬 Have feedback or questions? Drop into our discord!

  3. EricAndrechek commented on Jul 28, 2026

    @EricAndrechek
    Member

    Noted finding that could be helpful when closing this issue: insert_deduplication_token using the NATS event ID or something could help us have clickhouse prevent the duplication without relying only on replacingmergetree or the likes that may be schema dependent.

  4. EricAndrechek commented on Oct 8, 2026

    @EricAndrechek
    Member

    Fixed by #585. run() in cmd/wavehouse/main.go now blocks in App.Run (internal/app/app.go). That call returns only after the HTTP server's Shutdown has returned and the ingest worker has drained within server.shutdown_timeout. The stores, embedded NATS included, are released by App.Close only after that. The remaining related cases are tracked separately: #665 (a durable's final ack landing after the broker closes), #698 (a slow shard-claim tick outrunning the shutdown budget), #588 (exit status after a failed drain) and #660 (an insert_deduplication_token per INSERT).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/infraCI, build, deploy, Docker, releasearea/ingestIngest pipeline (Bento, batching, DLQ)bugSomething isn't working

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions