Skip to content

PeriodicJobEnqueuer logs ERROR "Error starting transaction: context canceled" when the client stops during the leader's staggered start #1576

Closed

Description

@gabithewriter

### What happens

When a client that has just become the leader is stopped (SIGTERM → Client.Stop or the cancellation of the context passed to Client.Start) within the first ~0.1–2.5 s, the log gets an ERROR line even though the stop is a normal, requested shutdown:

{"level":"ERROR","msg":"maintenance.PeriodicJobEnqueuer: Error starting transaction","error":"context canceled"}
Outside that window the shutdown log is clean.

Why (as far as we can tell from the source)
The maintenance services start with StaggerStart, a cancellable sleep of a random 0–1 s per service, in sequence.
Client.Stop (and the cancellation of the start context) cancels the context of the maintenance services at once.
PeriodicJobEnqueuer wakes up from the stagger with the cancelled context and still goes on to insert the start jobs (RunOnStart periodic jobs): insertBatch calls s.exec.Begin(ctx), which fails with context.Canceled, and the error is logged at ERROR (internal/maintenance/periodic_job_enqueuer.go, insertBatch, "Error starting transaction").
Seen on v0.49.0; the same code is in v0.51.0.

Reproduction
A client with at least one PeriodicJob with RunOnStart: true, alone in the database so it becomes the leader at once.
Start it and stop it about 100 ms after it reports it is the leader (e.g. a process that gets SIGTERM right after its readiness probe).
Repeat a few times: most runs log the ERROR line above.

Expected
A requested stop does not log an error. For example, the enqueuer could return without inserting when ctx.Err() != nil after the stagger (or before Begin), or log a context cancellation during stop at a lower level, as other maintenance services do on shutdown.

Workaround we use
Our runner starts the client on a context that is not cancelled by the process shutdown and, before calling Stop, waits (bounded, a few seconds) until the leader's start jobs are in river_job or another client leads. It works, but it couples the shutdown to a database poll and delays a SIGTERM that arrives right after start.

Environment: River v0.49.0, riverpgxv5, PostgreSQL 18, Go 1.27.

Claude find it, he's always finding

Activity

  1. brandur commented on Nov 4, 2026

    @brandur
    Contributor

    Thanks for the report! Fixed in #1577.

    Claude find it, he's always finding

    lol! No lies detected.

  2. added 2 commits that reference this issue on Nov 4, 2026
    9423e91
    90bdd56
  3. added a commit that references this issue on Nov 6, 2026
    9db53b9
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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions