Skip to content

ref(server): Introduce a queue to the concurrency limiter#562

Merged
jan-auer merged 7 commits into
mainfrom
ref/concurrency-queue
Jul 20, 2026
Merged

ref(server): Introduce a queue to the concurrency limiter#562
jan-auer merged 7 commits into
mainfrom
ref/concurrency-queue

Conversation

@jan-auer

@jan-auer jan-auer commented Jul 16, 2026

Copy link
Copy Markdown
Member

The concurrency limiter can now absorb brief capacity bursts by queuing requests instead of rejecting them immediately. This is implemented through semaphores: an outer semaphore (capacity max + queue) gates total participants, and an inner semaphore (capacity max) gates execution. Requests that pass the outer gate wait up to a configurable timeout for an execution permit; requests that exceed both limits are rejected with AtCapacity as before.

With the default concurrency_queue = 0, both semaphores have the same capacity and behavior is identical to the current immediate-reject model. In this case, there is no queuing and no added latency.

Two new config fields control the queue. Sizing guidance: set the queue depth to roughly permit_release_rate × acceptable_added_latency, and keep the timeout comfortably below client request timeouts.

service:
  max_concurrency: 500
  concurrency_queue: 100
  concurrency_queue_timeout: 1s

Batch stream() reservations continue to use non-blocking try_acquire_many and do not enter the queue.

Ref FS-447

@jan-auer
jan-auer requested a review from a team as a code owner July 16, 2026 15:52
@linear-code

linear-code Bot commented Jul 16, 2026

Copy link
Copy Markdown

FS-447

@jan-auer
jan-auer marked this pull request as draft July 16, 2026 15:53
@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 87.53%. Comparing base (1cfcd50) to head (f7acaac).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #562      +/-   ##
==========================================
+ Coverage   87.40%   87.53%   +0.13%     
==========================================
  Files          93       93              
  Lines       14787    14969     +182     
==========================================
+ Hits        12924    13103     +179     
- Misses       1863     1866       +3     
Components Coverage Δ
Rust Backend 92.34% <100.00%> (+0.12%) ⬆️
Rust Client 79.89% <ø> (ø)
Python Client 90.20% <ø> (ø)

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Comment thread objectstore-service/src/concurrency.rs
Comment thread objectstore-service/src/concurrency.rs
Comment thread objectstore-service/src/service.rs Outdated
jan-auer added 6 commits July 20, 2026 12:32
With max=0 and a non-zero queue, callers were admitted into the queue
and waited the full timeout for a task permit that could never exist.
Guard at the top of `acquire` so the at-capacity mode stays instant.
Prevents u32 wrapping when max + queue overflows in release builds.
Replace positional (u32, u32) arguments with a #[non_exhaustive] Stats
struct so new fields can be added without breaking callers.
Replace the manual elapsed/match block with a timer guard around
acquire and a single inspect_err for rejection logging.
Replace with_concurrency_limit + with_concurrency_queue with a single
with_concurrency that takes a pre-configured ConcurrencyLimiter. The
limiter module is now pub so callers can construct it directly.
@jan-auer
jan-auer marked this pull request as ready for review July 20, 2026 11:08

@cursor cursor 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.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit f7acaac. Configure here.

Ok(ConcurrencyPermit {
permit: Some(permit),
task_permit: Some(task_permit),
queue_permit: Some(queue_permit),

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Spurious rejects during bulk acquire

Medium Severity

try_acquire_many takes outer queue permits before the inner execution permits. When the inner acquire fails, those queue slots are held until the function returns. On a multi-threaded runtime, a concurrent acquire can observe a full outer semaphore and get AtCapacity even though queue capacity remains and the bulk reservation is about to give up its slots.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f7acaac. Configure here.

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.

That's an inherent limit of the design. We will solve this in a follow-up by removing acquire_many and changing batch-processing so it polls individually.

Comment thread objectstore-service/src/concurrency.rs
Comment thread objectstore-server/docs/architecture.md

@lcian lcian 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.

Looks good to me.

/// # Default
///
/// `0`
pub concurrency_queue: u32,

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.

Suggested change
pub concurrency_queue: u32,
pub concurrency_queue_size: u32,

Maybe?

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.

I'm also not entirely happy with this. Was also thinking of max_queued. Let's wait for the follow-up that introduces another parameter for batch requests and then reassess.

@jan-auer
jan-auer merged commit 0bc8c3f into main Jul 20, 2026
27 checks passed
@jan-auer
jan-auer deleted the ref/concurrency-queue branch July 20, 2026 12:33
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