Skip to content

fix(subscriber): keep request status updates on the owning save's connection - #3366

Merged
fallenbagel merged 4 commits into
developfrom
fix/pgsql-pool-starvation
Aug 18, 2026
Merged

fix(subscriber): keep request status updates on the owning save's connection#3366
fallenbagel merged 4 commits into
developfrom
fix/pgsql-pool-starvation

Conversation

@fallenbagel

@fallenbagel fallenbagel commented Aug 10, 2026

Copy link
Copy Markdown
Member

Description

The scanner loop could wedge the entire Postgres pool: every per-item Media save holds its connection inside an open transaction while the subscriber helpers awaited since #3223 acquire a second connection through the global getRepository(), so with bundles of 50 against the default pool of 10 and no acquisition timeout, every connection ends up held by a save waiting on a connection that can never be freed. This routes the subscriber DB work through event.manager so it rides the save's own connection, wrapped in event.manager.transaction() which nests as a savepoint on both the postgres and sqlite drivers, so a failure in related-request work rolls back to the savepoint and is logged as before instead of aborting the save. The detached addMovie/addSeries chains keep their own repositories since the transaction is gone by the time they run, and the per-season updates run sequentially because they now share one connection.

I also added a db timeout configuration which defaults to 30000ms when not set in env variables.

How Has This Been Tested?

  • I only tested unit tests but this can be tested using the preview tag (preview-pgsql-starvation-fix). Awaiting testing by the reported users.
  • Tested and confirmed working by the reported users.

Screenshots / Logs (if applicable)

Checklist:

  • I have read and followed the contribution guidelines.
  • Disclosed any use of AI (see our policy)
  • I have updated the documentation accordingly.
  • All new and existing tests passed.
  • Successful build pnpm build
  • Translation keys pnpm i18n:extract
  • Database migration (if required)

Summary by CodeRabbit

  • Bug Fixes

    • Improved reliability of media request and status updates by keeping related database operations within the same transaction.
    • Preserved correct ordering when processing TV season request updates.
    • Prevented unnecessary external service checks when requested movies are already available.
    • Added a configurable database connection wait timeout, defaulting to 30 seconds.
  • Documentation

    • Documented the new database connection timeout setting, including how to allow indefinite waiting.

…nection

This routes the subscriber DB work through event.manager so it rides the save's own connection,
wrapped in event.manager.transaction() which nests as a savepoint on both the postgres and sqlite
drivers, so a failure in related-request work rolls back to the savepoint and is logged as before
instead of aborting the save. The detached addMovie/addSeries chains keep their own repositories
since the transaction is gone by the time they run, and the per-season updates run sequentially
because they now share one connection.

fix #3365
@fallenbagel
fallenbagel requested a review from a team as a code owner August 10, 2026 22:40
Copilot AI lite review requested due to automatic review settings August 10, 2026 22:40
@coderabbitai

coderabbitai Bot commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1624c3ca-4792-49cc-87c8-952feb725bc2

📥 Commits

Reviewing files that changed from the base of the PR and between 7e79970 and 9c63bec.

📒 Files selected for processing (1)
  • server/utils/nestedTransaction.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • server/utils/nestedTransaction.ts

📝 Walkthrough

Walkthrough

PostgreSQL connection acquisition now has a configurable timeout. Media subscribers use event-bound EntityManager repositories and transactions. Radarr and Sonarr request processing receives the event manager, while detached callbacks retain independent repositories.

Changes

Media transaction handling

Layer / File(s) Summary
Database connection timeout configuration
server/datasource.ts, docs/extending-seerr/database-config.mdx
PostgreSQL datasources read DB_CONNECT_TIMEOUT_MS, with a 30-second default. Examples document TCP and Unix socket settings.
Media subscriber transactions
server/subscriber/MediaSubscriber.ts
Repository access uses event managers. Season processing is sequential. Child and related request updates run inside event-manager transactions.
Media request subscriber transactions
server/subscriber/MediaRequestSubscriber.ts
Radarr and Sonarr processing uses manager-bound repositories. Parent-status updates use nested transactions. Detached callbacks use independent repositories.
Nested transaction handling
server/utils/nestedTransaction.ts
withNestedTransaction runs callbacks directly on SQLite and through a TypeORM transaction for other databases.

Estimated code review effort: 4 (Complex) | ~45 minutes

Merge Risk: ⚪ Minimal · up to 9c63b

The PR updates database transaction handling and timeout configuration to keep related request updates on the owning save connection; no actionable merge-blocking risk remains after normal checks and review.

Possibly related issues

Possibly related PRs

Sequence Diagram(s)

sequenceDiagram
  participant MediaSubscriber
  participant EntityManager
  participant MediaRequestSubscriber
  participant PostgreSQL
  MediaSubscriber->>EntityManager: use event-bound repository
  EntityManager->>PostgreSQL: update media and related requests
  MediaRequestSubscriber->>EntityManager: dispatch Radarr or Sonarr work
  EntityManager->>PostgreSQL: run parent-status transaction
Loading

Poem

A rabbit checks each transaction line,
Media moves through the manager fine.
Radarr waits, then Sonarr sings,
PostgreSQL times waiting things.
Bound repositories keep records bright.

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the subscriber connection-handling fix that addresses the primary pull request change.
Linked Issues check ✅ Passed The changes route subscriber work through the owning EntityManager and preserve rollback behavior for issue #3365.
Out of Scope Changes check ✅ Passed The datasource timeout configuration and documentation support the connection-pool objective, and no unrelated changes are evident.

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.

Copilot AI 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.

Pull request overview

Fixes a Postgres connection-pool deadlock risk during high-concurrency media scans by ensuring subscriber-side DB work uses the same connection/transaction context as the originating save, and adds a configurable DB connection timeout to avoid silent hangs when the pool is exhausted.

Changes:

  • Route MediaSubscriber request/season status updates through event.manager and wrap them in nested transaction() calls (savepoints) to avoid acquiring a second pool connection.
  • Update MediaRequestSubscriber helper methods to use the event-scoped EntityManager, and isolate parent-status updates behind a savepoint.
  • Add DB_CONNECT_TIMEOUT_MS configuration (and documentation) to bound DB connection waits.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
server/subscriber/MediaSubscriber.ts Uses event.manager repositories + savepoint transactions to keep request status updates on the owning save connection (avoids pool wedge).
server/subscriber/MediaRequestSubscriber.ts Passes an EntityManager into helper methods and wraps parent-status updates in a savepoint; keeps detached *arr follow-up using global repositories.
server/datasource.ts Introduces DB_CONNECT_TIMEOUT_MS-driven timeout configuration for Postgres connections.
docs/extending-seerr/database-config.mdx Documents the new DB_CONNECT_TIMEOUT_MS environment variable.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread server/datasource.ts
Comment thread server/subscriber/MediaRequestSubscriber.ts
…e any network calls

sendToRadarr now checks availability before any network call, matching sendToSonarr, so the
short-circuit path does no HTTP inside the transaction at all.
Copilot AI review requested due to automatic review settings August 10, 2026 23:09

Copilot AI 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.

Pull request overview

Copilot reviewed 4 out of 4 changed files in this pull request and generated no new comments.

@seerr-automation-bot seerr-automation-bot added this to the v3.5.0 milestone Aug 11, 2026
@Alexsaphir

Copy link
Copy Markdown

Tested on kubernetes, with postgres 18 (CNPG).

Before this fix, to not soft lock:

DB_POOL_SIZE: 100

With this fix, no soft lock:

DB_POOL_SIZE: 10
DB_CONNECT_TIMEOUT_MS: 10000

(Tested by using the image and validating that it work, with the default pool size)

@fallenbagel
fallenbagel enabled auto-merge (squash) August 11, 2026 20:17
s0len added a commit to s0len/solen-ops that referenced this pull request Aug 12, 2026
… local patch

Replace the local MediaSubscriber override (ConfigMap + checksum init
container) with upstream's preview-pgsql-starvation-fix image (PR
seerr-team/seerr#3366, fixes #3374 and #3365). Upstream routes the
subscriber work through event.manager so it rides the media save's own
connection - fixing both the MEDIA_AVAILABLE drop and the pool
starvation, which the local after-commit deferral could not (deferred
work still acquired a second connection while the save's connection was
checked out). Keeping the local patch mounted would have overwritten
the fixed file in the preview image.

Revert this commit to restore the local patch if the preview
misbehaves; back to release tags once the PR ships.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings August 15, 2026 05:39

Copilot AI 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.

Pull request overview

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Comment thread server/utils/nestedTransaction.ts
sqlite reuses one query runner per process, so concurrent scanner saves race on its shared
transaction depth counter and wedge it, silently leaving later writes uncommitted. Related-request
work now runs directly on event.manager on sqlite instead of nesting a transaction.
Copilot AI review requested due to automatic review settings August 15, 2026 09:44
@fallenbagel
fallenbagel force-pushed the fix/pgsql-pool-starvation branch from 7e79970 to 9c63bec Compare August 15, 2026 09:44

This comment was marked as duplicate.

@gauthier-th gauthier-th left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

@fallenbagel
fallenbagel merged commit 059008c into develop Aug 18, 2026
18 checks passed
@fallenbagel
fallenbagel deleted the fix/pgsql-pool-starvation branch August 18, 2026 05:32
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.

Library scans (on some setups) deadlock the Postgres connection pool (app unresponsive, no errors, restart required)

6 participants