Repository navigation
Add opt-in LISTEN/NOTIFY long polling - #41
Thom Chiovoloni (thomcc-work) wants to merge 12 commits into
Conversation
There was a problem hiding this comment.
🔵 Needs a closer look
Unresolved wakeup gaps and schema-wide listener recovery require fixes and PostgreSQL-backed human validation.
2 open issues
What changed in this PR
Adds opt-in notification-driven long polling to the PostgreSQL provider while preserving short polling by default.
Changes:
- Adds listener recovery, bounded timer tracking, and long-poll configuration.
- Installs disabled notification triggers with explicit schema-wide activation.
- Adds integration coverage, CI validation, and operational documentation.
| File | Description |
|---|---|
| tests/postgres_provider_test.rs | Enables validation in either polling mode. |
| tests/long_poll_tests.rs | Adds integration tests and an idle-query benchmark. |
| src/provider.rs | Integrates configuration and long-poll fetches. |
| src/notifier.rs | Implements listeners, timers, recovery, and waiting. |
| src/migrations.rs | Adds notification activation and verification. |
| src/lib.rs | Exports and documents long-poll configuration. |
| README.md | Documents usage and operational limitations. |
| migrations/README.md | Documents activation and test commands. |
| migrations/0027_diff.md | Records migration changes. |
| migrations/0027_add_long_poll_notifications.sql | Installs disabled triggers and updates cleanup. |
| docs/migration-policy.md | Explains activation and compatibility. |
| CHANGELOG.md | Records the unreleased feature. |
| Cargo.toml | Raises the SQLx minimum to 0.8.3. |
| .github/workflows/test.yml | Runs validation with long polling enabled. |
Review effort: Balanced
52557ef to
e2fba14
Compare
e2fba14 to
60a2bb9
Compare
- Future-dated lease hints, including session expiry, plus immediate release hints for delayed abandonment. - Warm fetch connections, bounded timer scans, and independent degraded-mode fallback. - Bounded, jittered migration/activation retries with rollback and deadlock regressions. - Updated trigger inventory, deterministic catch-up coverage, and a reproducible benchmark driver.
Run timer reads in transactions with server-side lock and statement timeouts. Retain one owned scan across client watchdog wakeups and listener reconnects instead of cancelling and replacing an in-flight query. Add the supplied backend-accumulation regression, connection-reuse and single-flight coverage, and a timer wake deadline that cannot pass through the polling fallback. Addresses review items 41-N1 and 41-N5. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Place long-poll notification DDL after the pending 0027-0029 migrations, preserving migration 0028's prune_executions_batch cleanup and companion diff baseline. Update upgrade tests and documentation without stacking the prerequisite PRs. Include SQLSTATE and the full context chain in DDL retry warnings, test those diagnostics, and document draining writers and retrying ApplyAll when the fixed contention budget expires. Validated the intended predecessor sequence in an isolated source copy. Addresses 41-C1, 41-N3, and 41-N4. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add a 16+16 dispatcher, 64-client profile with sampled notification-commit and synchronous-replication wait time. Exercise workflow claims and saturated orchestration, activity, and session renewals with verified synchronous-standby and pool-capacity preconditions. Document notification-bearing operations and reproducible benchmark methodology without publishing performance results from uncontrolled runs. Include timer transaction control in idle SQL counts. Addresses review item 41-N2. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep the database message in ordinary migration error display while retaining the SQLx source for SQLSTATE retry classification. Add a red/green regression that also proves failed migration statements leave the ledger, stored function, and durable rows unchanged. State that migration 0030 must not ship before the 0027-0029 prerequisites, including the dependency of #51 on #32. Addresses review items 41-N6 and 41-N7. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep timer scans single-flight, but queue a fresh catch-up for both queues when a reconnect adopts an older snapshot. Run scan transactions READ ONLY and verify rejected writes preserve durable state and connection defaults. Replace SQL-lock timing gates with controlled acquisition gates, verify successful commits rather than idle backends, and wait through reconnect re-entry before checking the scan count. Add the disconnect-gap regression and benchmark statistics-role preflight/documentation. Addresses 41-N8 and all 41-N9 nits; no performance figures are published. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep close-on-drop armed for migration failures, unconfirmed unlocks, and cancellation, but explicitly return the healthy connection to SQLx after success and confirmed advisory-lock release. This leaves short-poll startup with its original warm connection. Add backend-identity and immediate-idle assertions for migration/no-op/activation success, plus failed-unlock and cancellation safety regressions. Update the migration lifecycle documentation. Addresses review item 41-N10. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Match #51's associated load_migrations and parse_version functions, including the failed-unlock regression added by #41. This keeps the standalone branch buildable and avoids E0599 when the prerequisite changes are combined. Retain b33a3f2's connection-reuse behavior without changing pool sizing or tuning for the upstream barrier-based test. Addresses review item 41-N11. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Guard migration connections with RAII: unsuccessful exits mark the connection for closure before its field is dropped, while successful migration and confirmed unlock allow SQLx's normal asynchronous pool return. Keep the same-backend and advisory-lock regressions, remove the immediate-idle assertion, and document asynchronous return. No pool sizing changes or timing-test workarounds. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Keep both delayed items enqueued up front, but spawn the second fetch without asserting that it is still waiting. A slow dispatcher may start that fetch after the second item is already eligible. Exercise both queues with immediate and deliberately late second fetches. The added late-start case reproduces the old assertion failure and passes after the fix; the shared waiting helper and production code remain unchanged. Addresses review item 41-N12. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Todd J. Green (tjgreen42)
left a comment
There was a problem hiding this comment.
Review by GPT-6.1 Sol.
No actionable findings in this revision. Reviewed listener startup/reconnection, wakeup registration and deferred-work deadlines, schema-wide trigger activation, polling fallback, shutdown, and migration compatibility. The earlier lost-wakeup/recovery concerns are addressed. The documented SQLx limitation around unbounded graceful pool.close() on a wedged listener is not a hang in provider/runtime drop, which aborts the listener without awaiting cleanup.
Scope: static review at 20dffa7, including the relevant SQLx cleanup behavior. No local PostgreSQL integration tests were run; local nextest compilation was blocked by the available Rust 1.88 toolchain versus a resolved dependency requiring Rust 1.89.
|
GPT-6.1 Sol: Please refresh the PR description to match the current branch. It still says the PR is a draft, but it is marked ready for review. Please also mention the later hardening: lease-expiry hints; read-only timer scans with 200 ms server-side timeouts; single-flight scans retained across watchdog wakeups and reconnects; and fresh catch-up scans of both queues after reconnect. The operational warning should explicitly include notification costs for successful claims, lease-changing renewals, and session-lease writes. The migration 0030 and prerequisite release-order notes still appear current. |
|
Thom Chiovoloni (@thomcc-work) Are we planning to add the capability to migrate from a pre-existing |
Todd J. Green (tjgreen42)
left a comment
There was a problem hiding this comment.
This looks good, strictly an improvement on duroxide-pg-opt in several respects. Nice job. What's left before we can turn it on by default? (Stress-testing to make sure notifications don't get out of hand under high load?)

Summary
Adds opt-in PostgreSQL LISTEN/NOTIFY long polling for orchestration and activity work, adapted from
duroxide-pg-opt. The goal is to reduce empty-queue queries and polling-induced latency while preserving existing queue eligibility, locking, and acknowledgment rules. Existing constructors continue to use short polling by default.The same configuration is supported for Entra-authenticated connections. SQLx 0.8.3 or later is required for explicit listener reconnect handling; native-TLS configuration is unchanged.
Waiting and recovery
READ ONLYtransactions with 200 ms server-side lock and statement timeouts. Server-cancelled scans roll back and return their connections to the pool instead of accumulating blocked backends.Notifications remain hints: database predicates and locks determine whether work can actually be claimed.
Migration and activation
Do not release this PR before #32 and #51. A release containing migration
0030must also contain #32’s migrations0027–0028and #51’s migration0029. Likewise, #51 must not be released before #32. Publishing these out of order creates a migration ledger that the later combined build rejects; coordinate releases rather than editing migration records.Migration
0030installs six disabled notification triggers covering both queues and session leases. Notifications are activated schema-wide only by construction with bothMigrationPolicy::ApplyAlland long polling enabled.VerifyOnlyperforms no DDL and requires prior activation. Disabling an individual provider’s listener does not disable shared triggers.Migration and activation transactions use 250 ms lock timeouts and jittered retries for deadlocks and lock contention, with a ten-second retry budget. Failed attempts roll back without advancing the ledger. Migration errors retain the database message and SQLSTATE source. Connections are preserved for reuse only after successful migration and confirmed advisory-lock release; errors and cancellation close them.
The cleanup function preserves migration
0028’s batched-pruning cleanup. Disposable databases that ran an earlier draft of this PR as migration0027must be recreated. Older binaries still reject unknown migration versions even when notifications are disabled. This does not convert databases using the incompatibleduroxide-pg-optmigration history.Operational considerations
PREPARE TRANSACTION. Activated queue and session-lease writes are therefore incompatible with PostgreSQL two-phase commit.