Skip to content

UN-3695 [FIX] PG consumer — supervisor shutdown grace = VT, single shared join deadline#2153

Merged
muhammad-ali-e merged 5 commits into
feat/UN-3445-pg-queue-integrationfrom
fix/UN-3695-supervisor-grace-vt
Jul 8, 2026
Merged

UN-3695 [FIX] PG consumer — supervisor shutdown grace = VT, single shared join deadline#2153
muhammad-ali-e merged 5 commits into
feat/UN-3445-pg-queue-integrationfrom
fix/UN-3695-supervisor-grace-vt

Conversation

@muhammad-ali-e

Copy link
Copy Markdown
Contributor

What

The PG-queue prefork supervisor SIGKILLed its own children after a hardcoded 30s on shutdown (_SHUTDOWN_GRACE_SECONDS = 30.0), even though the pod's terminationGracePeriodSeconds is set to VT + buffer (9120s for worker-pg-file-processing, VT=9060). So on a graceful SIGTERM (deploy / HPA pod scale-down) the supervisor knifed an in-flight batch ~300× sooner than the grace the pod actually had — orphaning the batch: the executor finished the work, but nothing was left to consume the reply, mark the file COMPLETE, decrement the barrier, or ack the queue message.

Two changes:

  • shutdown_grace_from_env() — the per-child drain grace now defaults to the consumer VT (WORKER_PG_QUEUE_CONSUMER_VT_SECONDS), floored at 30 so it can never be shorter than before (non-regressive: OSS/dev on the default VT stays 30; fileproc gets 9060 with no chart change, since the chart already sets VT). Explicit WORKER_PG_QUEUE_CONSUMER_SHUTDOWN_GRACE_SECONDS overrides.
  • _join_children → single shared deadline (was per-child recomputed). Children are SIGTERM'd together and drain in parallel, so one shared window still gives each its full grace while bounding total drain to ~grace. A per-child deadline sums to N×grace and, at grace=VT, blows past the pod grace → k8s SIGKILLs the whole pod, hard-killing siblings that were draining cleanly.

Why

Deploys and autoscaling routinely send graceful SIGTERM to workers. The 30s app-grace silently defeated the ~2.5h drain budget the chart deliberately provisions, so every scale-down/rollout landing mid-extraction stranded a batch until visibility-timeout redelivery (~2.5h) or the reaper.

Scope / safety

  • The supervisor (pg_queue_consumer) is a PG-worker-only component — it never runs for Celery workers, so no feature-flag branch is needed and the Celery path is byte-for-byte unchanged.
  • max(30, VT) floor guarantees the drain is never shorter than today.

How tested

  • Unit: shutdown_grace_from_env (VT-tracking / floor / override) + a shared-deadline regression test — 38 supervisor tests pass; test_pg_consumer_supervisor.py + test_pg_queue_consumer.py = 109 pass.
  • e2e with real forked children: (1) grace tracks VT (40) and the supervisor drains a live child with no premature SIGKILL; (2) two wedged children are SIGKILLed together at the shared deadline, not 2× it.

Part of UN-3695 (PG crash-safe worker shutdown). Follow-ups tracked there: heartbeat-lease for hard-kill recovery (next); the idempotency track was found already handled (billing is idempotent in the cloud plugins) and needs no code.

🤖 Generated with Claude Code

…ared join deadline

The prefork supervisor SIGKILLed its own children after a hardcoded 30s on
shutdown (`_SHUTDOWN_GRACE_SECONDS = 30.0`). But the chart grants the pod
`terminationGracePeriodSeconds = VT + buffer` (9120s for worker-pg-file-processing,
VT=9060) — so on a graceful SIGTERM (deploy / HPA pod scale-down) the supervisor
was knifing an in-flight batch ~300x sooner than the pod grace it lives inside,
orphaning the batch (executor finished, but nothing consumed the reply / marked
COMPLETE / decremented the barrier / acked the message).

- `shutdown_grace_from_env()`: the per-child drain grace now defaults to the
  consumer VT (`WORKER_PG_QUEUE_CONSUMER_VT_SECONDS`), floored at 30 so it can
  never be SHORTER than before (non-regressive; OSS/dev with the default VT stays
  30, fileproc gets 9060 with no chart change). Explicit
  `WORKER_PG_QUEUE_CONSUMER_SHUTDOWN_GRACE_SECONDS` overrides.
- `_join_children` now uses a SINGLE shared deadline instead of a per-child
  recomputed one. Children are SIGTERM'd together and drain in parallel, so one
  shared window still gives each its full grace while bounding total drain to
  ~grace. A per-child deadline would sum to N x grace and, at grace=VT, blow past
  the pod grace so k8s SIGKILLs the whole pod, hard-killing siblings that were
  draining cleanly.

PG-worker-component only — the supervisor never runs for Celery workers, so no
flag branch is needed and the Celery path is untouched.

Tests: shutdown_grace_from_env (VT-tracking / floor / override) + a shared-deadline
regression test (38 supervisor tests). Verified e2e with real forked children:
grace tracks VT and drains a live child (no premature SIGKILL); 2 wedged children
SIGKILL together at the shared deadline, not 2x.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 58170876-7652-4b1f-b26b-b5b791441212

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/UN-3695-supervisor-grace-vt

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@muhammad-ali-e muhammad-ali-e force-pushed the fix/UN-3695-supervisor-grace-vt branch from 2562182 to e61708f Compare July 8, 2026 06:31
Add a co-located README as a lookup dictionary for the PG-queue transport:
config env vars (consumer / reaper / orchestration / routing) with defaults,
a concepts glossary (visibility timeout, claim, redelivery, reaper, barrier,
poison/re-park, prefork supervisor, shutdown grace, request-reply/callback,
last_progress_at, leader election, fairness), and the table catalogue.
Complements the 9e-design.md design doc. Docs only — no code change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@muhammad-ali-e muhammad-ali-e force-pushed the fix/UN-3695-supervisor-grace-vt branch from e61708f to baa0199 Compare July 8, 2026 06:34

@muhammad-ali-e muhammad-ali-e left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Automated PR review (PR Review Toolkit: code-reviewer, silent-failure-hunter, type-design, comment, test, simplifier). The core fix is correct — grace defaults to VT floored at 30s, override honoured as-is, and the single shared deadline correctly bounds total drain to ~grace instead of N×grace. Findings below are ranked; the two [med] items are the ones worth addressing before merge. Everything else is low/nit.

Comment thread workers/pg_queue_consumer/supervisor.py Outdated
"""
from queue_backend.pg_queue.consumer import _DEFAULT_VT_SECONDS, consumer_env

override = consumer_env("SHUTDOWN_GRACE_SECONDS", None, float)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[med] SHUTDOWN_GRACE_SECONDS override is unvalidated — a bad value silently produces the exact failure UN-3695 fixes.

consumer_env(..., float) accepts negative and non-finite values, and max(0.0, override) then collapses them:

  • SHUTDOWN_GRACE_SECONDS=-30max(0.0, -30)=0.0 → deadline now+0every child SIGKILLed with zero drain on the next scale-down (the orphaned-batch bug this PR exists to prevent), with no log.
  • nanmax(0.0, nan)=0.0 → same immediate kill.
  • inf/1e999max(0.0, inf)=inf_join_children deadline is now+inf → shutdown hangs forever until k8s hard-kills the whole pod.

A negative/non-finite drain budget is never intentional. Sibling concurrency_from_env() already raises on n < 1 — mirror that here:

override: float | None = consumer_env("SHUTDOWN_GRACE_SECONDS", None, float)
if override is not None:
    if not math.isfinite(override) or override < 0:
        raise ValueError(
            f"WORKER_PG_QUEUE_CONSUMER_SHUTDOWN_GRACE_SECONDS must be a finite "
            f"number >= 0, got {override!r}"
        )
    return override

(The override: annotation also restores the X | None intent-declaration convention this module documents at consumer.py:858.)

deadline = time.monotonic() + grace_seconds
for slot, pid in fleet.alive_items():
if _wait_for_exit(pid, time.monotonic() + grace_seconds):
if _wait_for_exit(pid, deadline):

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[med] New regression: a clean-exited child iterated after the shared deadline elapses is spuriously SIGKILLed and logged as "did not drain".

With one wedged child consuming the whole shared window, every subsequent child hits _wait_for_exit(pid, deadline) with deadline already in the past. _wait_for_exit's loop is while time.monotonic() < deadline: — so it returns False without ever calling waitpid, even for a child that exited cleanly at T+5s. _join_children then SIGKILLs it and logs "did not drain within the shared %ss grace".

The SIGKILL to the (already-zombie) child is harmless, but the WARNING is a false "batch was hard-killed" alarm — ironic for the PR whose whole point is to stop hard-killing in-flight batches, and it can trip exactly that alert. This is new: the old per-child code recomputed time.monotonic() + grace per iteration, so the deadline was never already-elapsed at entry.

Fix in _wait_for_exit — poll waitpid at least once regardless of the deadline:

def _wait_for_exit(pid: int, deadline: float) -> bool:
    while True:
        try:
            reaped, _status = os.waitpid(pid, os.WNOHANG)
        except ChildProcessError:
            return True
        if reaped != 0:
            return True
        if time.monotonic() >= deadline:
            return False
        time.sleep(0.1)

Comment thread workers/pg_queue_consumer/supervisor.py Outdated
fleet = _Fleet(concurrency)
grace_seconds = shutdown_grace_from_env()
logger.info(
"PG-queue consumer supervisor: shutdown drain grace = %.0fs/child", grace_seconds

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] Startup log says %.0fs/child but the drain is now a single shared total, not per-child.

After this PR grace_seconds bounds the total drain to ~grace across all children (see the _join_children docstring, which explicitly rejects the per-child model). An operator reading grace = 9060s/child with concurrency=4 will mis-model the total window as 4×9060s — the precise per-child-vs-shared misconception the rework fixes. Drop the /child:

"PG-queue consumer supervisor: shutdown drain grace = %.0fs (shared across children)", grace_seconds

Comment thread workers/pg_queue_consumer/supervisor.py Outdated


def shutdown_grace_from_env() -> float:
"""Per-child graceful-drain budget (seconds) on shutdown, before SIGKILL.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] "Per-child" wording left stale after the switch to a shared deadline (3 sites).

This docstring's first line still calls the value a "Per-child graceful-drain budget", and so does the constant comment at line 66 and the README table row at README.md:25 — yet _join_children now applies it as a single shared total and its own docstring explicitly warns that a per-child deadline would be a bug. Same knob described two contradictory ways within one PR. Suggest a find-and-replace to "graceful-drain budget, shared across all children on shutdown" at all three sites (the _join_children docstring and the SIGKILL warning at line 445 are already correct — align the rest to them).

Comment thread workers/pg_queue_consumer/supervisor.py Outdated
if override is not None:
return max(0.0, override)
vt = consumer_env("VT_SECONDS", _DEFAULT_VT_SECONDS, int)
return max(float(_DEFAULT_SHUTDOWN_GRACE_SECONDS), float(vt))

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nit] Redundant float() on an already-float constant. _DEFAULT_SHUTDOWN_GRACE_SECONDS is 30.0 (line 71), so float(...) around it is a no-op. float(vt) is the meaningful cast (vt is an int):

return max(_DEFAULT_SHUTDOWN_GRACE_SECONDS, float(vt))

|---|---|---|
| `CONCURRENCY` | Prefork consumer children per pod (1 = plain single process) | `1` |
| `VT_SECONDS` | **Visibility timeout** — how long a claimed message stays hidden before it can redeliver | `30` (chart: `9060` for file-processing) |
| `SHUTDOWN_GRACE_SECONDS` | Per-child graceful-drain budget on SIGTERM before SIGKILL | `= VT`, floored at `30` |

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[low] This row contradicts the glossary in the same file. It calls SHUTDOWN_GRACE_SECONDS a "Per-child graceful-drain budget", but the "Shutdown grace" glossary entry at line 87 correctly says the supervisor drains "using a single shared deadline." Reword to Graceful-drain budget (shared across all children) on SIGTERM before SIGKILL so the two agree.

kill.assert_called_once_with(111, _signal.SIGKILL)
waitpid.assert_called_once_with(111, 0)

def test_shared_deadline_not_per_child(self):

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[test] Coverage gaps around the change's load-bearing paths.

  1. Multi-child straggler on the shared deadline is untested. test_shared_deadline_not_per_child only exercises the clean-exit path (_capture always returns True); test_straggler_is_sigkilled_and_reaped uses a single child. A regression re-introducing a per-child deadline inside the SIGKILL loop would pass both. Add a 3-child, all-False (none drain) case asserting one shared deadline and kill/waitpid called 3×.
  2. This assertion is probabilistic. len(set(deadlines)) == 1 only distinguishes shared-vs-per-child if time.monotonic() ticks between iterations; with _wait_for_exit patched to return instantly a per-child bug could yield 3 identical values on a coarse clock and pass. Harden by patching time.monotonic with an advancing side_effect=[100.0, 200.0, 300.0, 400.0] and asserting deadlines == [105.0, 105.0, 105.0].
  3. Negative override branch untested — add SHUTDOWN_GRACE_SECONDS=-5 (pairs with the validation fix suggested on supervisor.py:118).
  4. Malformed env is untested — assert shutdown_grace_from_env() raises ValueError matching the offending var name for SHUTDOWN_GRACE_SECONDS=abc and VT_SECONDS=9060s, pinning the fail-fast-at-startup contract.

@muhammad-ali-e

Copy link
Copy Markdown
Contributor Author

Thanks — all 7 addressed in 67f7d6e83:

  • [med] supervisor.py:118 unvalidated overrideshutdown_grace_from_env now rejects a negative / non-finite SHUTDOWN_GRACE_SECONDS (math.isfinite + >= 0) and raises with the offending var name, mirroring concurrency_from_env. No more -30/nan → 0 (immediate SIGKILL) or inf → hang. Added the override: float | None annotation.
  • [med] supervisor.py:442 _wait_for_exit false-alarm — it now polls waitpid at least once regardless of the deadline (while True + explicit exits), so a child iterated after the shared window elapsed isn't spuriously reported "did not drain" / SIGKILLed.
  • [low] :366 log — now shutdown drain grace = %.0fs (shared across children) (dropped /child).
  • [low] "Per-child" wording (3 sites) — corrected the shutdown_grace_from_env docstring, the constant comment (line 66), and README.md to "shared across all children", matching the _join_children docstring.
  • [nit] :122 redundant float() — dropped; now max(_DEFAULT_SHUTDOWN_GRACE_SECONDS, float(vt)).
  • [low] README.md:25 — row reworded to agree with the glossary ("shared across all children").
  • [test] coverage — (1) shared-deadline test hardened with an advancing time.monotonic (asserts exact 105/105/105, no longer probabilistic); (2) new 3-child all-wedged straggler case asserting kill/waitpid ×3 and one shared deadline; (3)+(4) negative / non-finite / malformed override + malformed VT_SECONDS=9060s fail-fast tests. Replaced the … or True lambdas with named helpers. 43 tests pass; e2e re-verified (drain + wedge).

SonarCloud's "C Reliability on New Code" should clear on this commit — it was the same override/hang class + the lambda idiom, both now fixed.

…adline false-alarm, docs

Review round on the grace=VT / shared-deadline change:
- shutdown_grace_from_env: reject a negative / non-finite SHUTDOWN_GRACE_SECONDS
  override (was silently collapsed by max(0.0, ...) -> 0 = immediate SIGKILL, or inf
  = shutdown hang) — fail fast like concurrency_from_env; annotate override: float|None;
  drop the redundant float() on the already-float default.
- _wait_for_exit: poll waitpid at least once regardless of the deadline. On the
  single SHARED deadline a child iterated after the window elapsed was reported
  'did not drain' and SIGKILLed — a false hard-kill alarm — even though it exited.
- Wording: the grace is a single shared total, not per-child — corrected the
  docstring, constant comment, startup log ('shared across children'), and the
  README config row to match the _join_children docstring.
- Tests: shared-deadline hardened with an advancing time.monotonic (exact
  105/105/105); a 3-child all-wedged straggler case (kill+waitpid x3, one shared
  deadline); negative / non-finite / malformed override + malformed VT fail-fast;
  named helpers replace the lambda idiom; float asserts use pytest.approx (Sonar
  S1244). 43 tests pass; e2e re-verified.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@muhammad-ali-e muhammad-ali-e force-pushed the fix/UN-3695-supervisor-grace-vt branch from 67f7d6e to 20462e0 Compare July 8, 2026 06:48
@muhammad-ali-e muhammad-ali-e marked this pull request as ready for review July 8, 2026 06:50
@greptile-apps

greptile-apps Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR fixes a critical shutdown bug in the PG-queue prefork supervisor where a hardcoded 30-second grace period caused in-flight batches to be SIGKILLed ~300× sooner than the pod's configured terminationGracePeriodSeconds (~2.5h for file-processing workers), orphaning batches on every deploy or HPA scale-down.

  • shutdown_grace_from_env() replaces the _SHUTDOWN_GRACE_SECONDS = 30.0 constant with a function that reads the consumer's visibility timeout (WORKER_PG_QUEUE_CONSUMER_VT_SECONDS), floors it at 30s, and respects an explicit SHUTDOWN_GRACE_SECONDS override — all validated to be finite and non-negative.
  • _join_children shared deadline replaces per-child deadline recomputation with a single shared deadline = time.monotonic() + grace_seconds set once before iterating children; _wait_for_exit is updated to poll at least once even after the deadline to avoid false SIGKILL for children that finished during a sibling's wait. A new README documents the queue's configuration and glossary.

Confidence Score: 5/5

Safe to merge — the change is scoped entirely to the PG-queue prefork supervisor, the Celery path is byte-for-byte unchanged, and the fix is non-regressive (VT floored at 30s).

Both changes are mechanically straightforward: replacing a module-level constant with an env-read function and computing one shared deadline instead of N per-child deadlines. Validation in shutdown_grace_from_env covers negative, non-finite, and malformed values. The at-least-once poll in _wait_for_exit correctly handles the shared-deadline edge case where a later-iterated child finished during a sibling's wait window. The 38 existing supervisor tests plus 11 new tests all target the changed code paths directly.

No files require special attention.

Important Files Changed

Filename Overview
workers/pg_queue_consumer/supervisor.py Core fix: adds shutdown_grace_from_env() (VT-tracking, validated, floored at 30s) and converts _join_children to a single shared deadline with an at-least-once poll guard in _wait_for_exit; logic is sound and well-guarded.
workers/tests/test_pg_consumer_supervisor.py Adds 9 new TestShutdownGraceFromEnv cases (VT tracking, floor, override, validation, malformed inputs) and 2 TestJoinChildren regression tests verifying the shared-deadline property; coverage is thorough.
workers/queue_backend/pg_queue/README.md New reference doc listing all env vars, glossary terms, and table schemas for the PG queue; purely documentation with no functional impact.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant k8s as k8s/HPA
    participant sup as Supervisor
    participant c1 as Child 1 (in-flight)
    participant c2 as Child 2 (idle)

    Note over sup: startup: grace = max(30, VT) e.g. 9060s

    k8s->>sup: SIGTERM
    sup->>sup: _on_term: stopping.set()
    sup->>c1: SIGTERM at T0
    sup->>c2: SIGTERM at T0
    c2-->>sup: exits quickly

    Note over sup: finally block runs
    sup->>c1: SIGTERM again (harmless duplicate)
    sup->>c2: SIGTERM again (ProcessLookupError suppressed)

    Note over sup: _join_children: deadline = now + grace_seconds (ONE shared value)

    sup->>c1: _wait_for_exit(pid1, deadline) polls every 0.1s
    c1-->>sup: exits within grace period
    sup->>c2: _wait_for_exit(pid2, deadline) polls at least once
    Note over c2: already exited, first poll returns True

    Note over sup: all children drained, supervisor exits cleanly
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant k8s as k8s/HPA
    participant sup as Supervisor
    participant c1 as Child 1 (in-flight)
    participant c2 as Child 2 (idle)

    Note over sup: startup: grace = max(30, VT) e.g. 9060s

    k8s->>sup: SIGTERM
    sup->>sup: _on_term: stopping.set()
    sup->>c1: SIGTERM at T0
    sup->>c2: SIGTERM at T0
    c2-->>sup: exits quickly

    Note over sup: finally block runs
    sup->>c1: SIGTERM again (harmless duplicate)
    sup->>c2: SIGTERM again (ProcessLookupError suppressed)

    Note over sup: _join_children: deadline = now + grace_seconds (ONE shared value)

    sup->>c1: _wait_for_exit(pid1, deadline) polls every 0.1s
    c1-->>sup: exits within grace period
    sup->>c2: _wait_for_exit(pid2, deadline) polls at least once
    Note over c2: already exited, first poll returns True

    Note over sup: all children drained, supervisor exits cleanly
Loading

Reviews (2): Last reviewed commit: "UN-3695 [FIX] SIGKILL warning log uses %..." | Re-trigger Greptile

@muhammad-ali-e

Copy link
Copy Markdown
Contributor Author

Addressed the Greptile nit (20462e044 → amended): the SIGKILL warning in _join_children now uses %.0fs for grace_seconds, matching the startup log (was %s → printed 9060.0s vs the startup log's 9060s). Cosmetic only, no behavior change.

@muhammad-ali-e muhammad-ali-e force-pushed the fix/UN-3695-supervisor-grace-vt branch from b38f8b3 to 56386bc Compare July 8, 2026 07:00
… (Greptile nit)

The _join_children SIGKILL warning printed grace_seconds with %s (e.g. 9060.0s)
while the startup log uses %.0f (9060s). Align to %.0fs. Cosmetic — no behaviour.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@muhammad-ali-e muhammad-ali-e force-pushed the fix/UN-3695-supervisor-grace-vt branch from 56386bc to 77e0f52 Compare July 8, 2026 07:01
@muhammad-ali-e muhammad-ali-e merged commit 5a385a4 into feat/UN-3445-pg-queue-integration Jul 8, 2026
4 checks passed
@muhammad-ali-e muhammad-ali-e deleted the fix/UN-3695-supervisor-grace-vt branch July 8, 2026 07:03
@sonarqubecloud

sonarqubecloud Bot commented Jul 8, 2026

Copy link
Copy Markdown

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant