Skip to content

fix(requests): serialize request creation per user - #3377

Open
fallenbagel wants to merge 1 commit into
fix/put-season-double-countfrom
fix/request-quota-race
Open

fix(requests): serialize request creation per user#3377
fallenbagel wants to merge 1 commit into
fix/put-season-double-countfrom
fix/request-quota-race

Conversation

@fallenbagel

@fallenbagel fallenbagel commented Aug 12, 2026

Copy link
Copy Markdown
Member

Description

The quota check reads its counts and then saves, with nothing in between stopping another request from the same user passing the same check. Several requests submitted at once could all clear a quota with room for one. That is not theoretical, the collection request modal submits one request per selected part in parallel, so requesting a collection has always been able to go past the limit.

Creating a request now runs under a per-user lock, so the check and the save that follows it cannot interleave with that user's other requests. The lock also closes the same-user duplicate submit and the auto-request duplicate check, which had the same shape. It is taken before any database work, so a request waiting on it holds no pool connection.

How Has This Been Tested?

  • Only via the unit test attached to this PR

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)

@coderabbitai

coderabbitai Bot commented Aug 12, 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 Plus

Run ID: 8423d3be-c7a1-452c-8bca-5528d395c2be

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

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.

@github-actions github-actions Bot added the merge conflict Cannot merge due to merge conflicts label Aug 12, 2026
@github-actions

Copy link
Copy Markdown

This pull request has merge conflicts. Please resolve the conflicts so the PR can be successfully reviewed and merged.

@fallenbagel fallenbagel changed the title fix/request quota race fix(requests): serialize request creation per user Aug 12, 2026
@fallenbagel
fallenbagel force-pushed the fix/request-quota-race branch from f313701 to dc73871 Compare August 12, 2026 06:02
Copilot AI lite review requested due to automatic review settings August 12, 2026 06:02
@github-actions github-actions Bot removed the merge conflict Cannot merge due to merge conflicts label Aug 12, 2026

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

This PR addresses a race condition in request creation where multiple concurrent requests from the same user could interleave quota/duplicate checks and exceed limits. It introduces a per-user async lock so quota validation and persistence are serialized for each user within a single running instance.

Changes:

  • Added a shared per-user requestLock and wrapped MediaRequest.request() in it.
  • Generalized AsyncLock.dispatch() to return the callback result (generic Promise<T>).
  • Added a unit test to verify concurrent quota enforcement and duplicate request rejection.

Reviewed changes

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

File Description
server/utils/requestLock.ts Introduces a shared AsyncLock instance intended to serialize request creation per user.
server/utils/asyncLock.ts Updates dispatch to be generic and return the callback’s result while preserving lock release semantics.
server/entity/MediaRequest.ts Wraps request creation in the per-user lock and extracts the original logic into a private helper.
server/entity/MediaRequest.test.ts Adds concurrency-focused tests for quota and duplicate-request behavior.

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

Comment thread server/entity/MediaRequest.ts
Comment thread server/utils/requestLock.ts
The quota check read its counts and then saved with nothing in between to
stop a second request from the same user passing the same check, so
concurrent requests could all clear a quota that only had room for one.
The collection request modal submits its parts in parallel, so this was
reachable from the UI.

Request creation now runs under a per-user lock, which also closes the
same-user duplicate and auto-request races that had the same shape. The
lock is taken before any repository call, so a waiter holds no pool
connection while blocked.
Copilot AI review requested due to automatic review settings August 12, 2026 06:32
@fallenbagel
fallenbagel force-pushed the fix/request-quota-race branch from dc73871 to 766b2ed Compare August 12, 2026 06:32
@fallenbagel
fallenbagel marked this pull request as ready for review August 12, 2026 06:32
@fallenbagel
fallenbagel requested a review from a team as a code owner August 12, 2026 06:32

This comment was marked as low quality.

@fallenbagel fallenbagel added this to the v3.5.0 milestone Aug 12, 2026
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.

3 participants