Skip to content

Bound queue check cooldowns - #23

Merged
pkaminski merged 2 commits into
masterfrom
codex/cancel-reconnect-cooldowns
Aug 8, 2026
Merged

Bound queue check cooldowns#23
pkaminski merged 2 commits into
masterfrom
codex/cancel-reconnect-cooldowns

Conversation

@pkaminski

@pkaminski pkaminski commented Aug 7, 2026

Copy link
Copy Markdown
Member

Summary

  • cap the shared queue-check cooldown at 30 seconds
  • preserve cooldown debt across disconnects and for stale or crashing jobs
  • include elapsed connection time in queue load logs
  • bump the package version to 4.3.1

Why

The shared check queue enforces a maximum 50% duty cycle by delaying the next job for the duration of the previous one. An unexpectedly slow probe or listener load could therefore leave every queue source waiting for an unbounded period during reconnect startup.

Canceling that cooldown on disconnect was too broad because the check queue is process-global: one flapping Firebase database would erase pacing for unrelated healthy sources. Bounding the enforced delay instead preserves global throttling while ensuring that a single abnormal job cannot stall subsequent queue initialization for more than 30 seconds.

Impact

Normal checks continue to receive an equal-duration cooldown. Checks longer than 30 seconds receive a 30-second cooldown, and disconnects do not allow unrelated sources to bypass it. Queue load logs expose the complete connection duration, including scheduler wait, size probe, and listener loading time.

Validation

  • yarn test
  • yarn lint
  • yarn check-types
  • yarn build
  • yarn pack --dry-run
  • git diff --check

This change is Reviewable

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Devin Review found 2 potential issues.

Open in Devin Review

Comment thread src/firelease.ts Outdated
this.connectionStartedAt = performance.now();
void this.enqueueStartup(epoch);
} else {
queueCheckQueue.cancelCooldown();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 One database losing its connection removes the pacing delay for all other databases' queue checks

The shared pacing delay that keeps queue checks from running back-to-back is wiped and woken up for every database whenever any single database drops its connection (queueCheckQueue.cancelCooldown() at src/firelease.ts:705), so unrelated healthy queues immediately run their next check with no throttling.

Impact: During connection churn, expensive queue size probes and listener reloads for other, still-healthy queues can run continuously instead of at the intended 50% duty cycle, adding load exactly when the database is already struggling.

Mechanism: a single global QueueCheckQueue is shared by all sources

queueCheckQueue is a module-level singleton (src/firelease.ts:579) used by every QueueSource. cancelCooldown() (src/firelease.ts:512-520) zeroes previousDuration/previousFinishedAt and resolves the in-flight cooldown promise unconditionally, without checking which source the currently-awaited job belongs to. The awakened job in drain() (src/firelease.ts:536-543) proceeds straight to job.run() even if its source never disconnected, and the following jobs also see a zeroed minimumStart. A flapping source therefore repeatedly erases the duty-cycle debt accumulated by other sources.

A targeted fix would be to only skip/shorten the cooldown when the awaited job belongs to the disconnected source (or when its epoch is no longer current), rather than cancelling the shared cooldown outright.

Prompt for agents
QueueCheckQueue is a process-wide singleton shared by all QueueSources (src/firelease.ts:579). The new disconnect handling calls queueCheckQueue.cancelCooldown() from QueueSource.onConnection whenever any source disconnects, which zeroes previousDuration/previousFinishedAt and resolves the pending cooldown promise regardless of which source's job is actually being delayed. The intent is only to stop a disconnected source from being held back by pre-disconnect cooldown debt, but the effect is that a single flapping database removes duty-cycle throttling for every other healthy source, letting expensive childrenKeys probes and listener reloads run back-to-back. Consider making the cancellation conditional: only wake the cooldown when the job currently being delayed (or all queued jobs) is no longer current / belongs to the disconnected source, or track cooldown debt per source instead of globally.
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good catch. I replaced disconnect-driven cancellation with a global 30-second cooldown cap. A flapping source now leaves the shared pacing debt intact, while an unexpectedly long check can delay the queue by at most 30 seconds. The regression test also verifies that disconnect/reconnect does not erase an existing cooldown.

Comment thread src/firelease.ts Outdated
Comment on lines +560 to +563
if (job.source.isCurrent(job.epoch)) {
this.previousDuration = finishedAt - start;
this.previousFinishedAt = finishedAt;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 Suppressing cooldown debt for invalidated jobs also suppresses it for crashing sources

The new guard only records duty-cycle debt when job.source.isCurrent(job.epoch) holds. isCurrent (src/firelease.ts:757) returns false not only when the epoch changed but also when the source is disconnected or crashing. A long-running check that actually consumed real database work (e.g. a slow childrenKeys probe that ends with a crash or a mid-job disconnect) therefore contributes zero cooldown for the next job of a different, healthy source, which starts immediately. This matches the stated intent for reconnects, but the crashing-source case is a side effect worth confirming.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Agreed that this side effect was undesirable as well. The guard is removed, so stale or crashing jobs once again contribute their actual runtime to pacing for the next source; the new 30-second cap bounds the resulting cooldown.

@pkaminski pkaminski left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@pkaminski resolved 1 discussion.
Reviewable status: 0 of 3 files reviewed, 1 unresolved discussion.

@pkaminski pkaminski changed the title Cancel queue check cooldowns on disconnect Bound queue check cooldowns Aug 7, 2026

@pkaminski pkaminski left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

@pkaminski+CODX made 2 comments and resolved 1 discussion.
Reviewable status: 0 of 3 files reviewed, all discussions resolved.

Comment thread src/firelease.ts Outdated
Comment on lines +560 to +563
if (job.source.isCurrent(job.epoch)) {
this.previousDuration = finishedAt - start;
this.previousFinishedAt = finishedAt;
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Agreed that this side effect was undesirable as well. The guard is removed, so stale or crashing jobs once again contribute their actual runtime to pacing for the next source; the new 30-second cap bounds the resulting cooldown.

Comment thread src/firelease.ts Outdated
this.connectionStartedAt = performance.now();
void this.enqueueStartup(epoch);
} else {
queueCheckQueue.cancelCooldown();

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good catch. I replaced disconnect-driven cancellation with a global 30-second cooldown cap. A flapping source now leaves the shared pacing debt intact, while an unexpectedly long check can delay the queue by at most 30 seconds. The regression test also verifies that disconnect/reconnect does not erase an existing cooldown.

@pkaminski
pkaminski requested a review from snoack August 7, 2026 21:45

@pkaminski pkaminski left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

+r:@snoack

@pkaminski made 1 comment.
Reviewable status: 0 of 3 files reviewed, all discussions resolved (waiting on snoack).

@snoack snoack left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

:lgtm:

@snoack reviewed 3 files and all commit messages, and made 1 comment.
Reviewable status: :shipit: complete! all files reviewed, all discussions resolved (waiting on pkaminski).

@pkaminski
pkaminski merged commit 189a42c into master Aug 8, 2026
4 checks passed
@pkaminski
pkaminski deleted the codex/cancel-reconnect-cooldowns branch August 8, 2026 04:38
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.

2 participants