Skip to content

Fix proxy batch fallback ignoring API concurrency (#614) - #1817

Merged
Mikola Lysenko (mikolalysenko) merged 3 commits into
mainfrom
agent/fix-proxy-batch-path-concurrency
Oct 29, 2026
Merged

Mikola Lysenko (mikolalysenko) merged 3 commits into
mainfrom
agent/fix-proxy-batch-path-concurrency

Conversation

@mikolalysenko

@mikolalysenko Mikola Lysenko (mikolalysenko) commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator

LLM Description written by Claude Code:claude-opus-5-5

Fixes #614

Summary

When the public proxy rejects a batch-search chunk (a validation 400, which one exotic PURL is enough to cause) or predates POST /patch/batch, the client falls back to one GET per package. That fallback kept up to 10 requests in flight because of a private constant. So it ignored the proxy cap of 4 and SOCKET_API_CONCURRENCY (=1 still sent 10 at once), and it stayed concurrent under a tight RLIMIT_NOFILE.

Root cause

api/client.rs set the size of the proxy batch path's slot pool and fallback groups from PROXY_BATCH_PATH_CONCURRENCY = 10. Every other patch-API window gets its limit from utils::concurrent::api_concurrency.

Changes

  • A proxy client sizes proxy_batch_slots from api_concurrency(true) when it is built: 4 by default, lower under SOCKET_API_CONCURRENCY, and 1 under a tight fd limit. An org client never uses these slots and doesn't read the env for them, so it adds no duplicate --debug "ignoring SOCKET_API_CONCURRENCY" line.
  • PROXY_BATCH_PATH_CONCURRENCY is deleted. No patch-API pacing constant is left outside utils::concurrent.
  • The fallback no longer runs its GETs in fixed groups. Every GET waits on the shared slot pool, so one slow or throttled GET holds one slot instead of stalling a whole group. The first throttle error is still reported in input order, as before.
  • The doc comment on search_patches_batch no longer says "concurrency 10".

Behavior change (intended, per the issue's acceptance criteria): the default fallback fan-out on the public proxy drops from 10 to 4. On the proxy the override can only lower the cap, by design (utils/concurrent.rs).

The issue's second point (registry_concurrency() had no caller) is already resolved on main: it is now called from patch/redirect/upstream/client.rs and upstream/npm.rs.

Tests (red → green)

Issue criterion Test Without fix With fix
peak equals api_concurrency(true) (4), not 10 proxy_batch_path_cap_tests::concurrent_batches_share_the_legacy_fallback_cap FAILED: "10 by-package GETs in flight at once; the cap is 4" ok
SOCKET_API_CONCURRENCY=1 gives a peak of 1 proxy_batch_path_cap_tests::the_legacy_fallback_honors_the_concurrency_override FAILED: left 10, right 1 ok

The tests set SOCKET_API_CONCURRENCY only while the client is built (it is read there) and restore it right away, so the variable is never left set while other tests run.

Local checks

  • cargo fmt --all -- --check: clean
  • cargo clippy --workspace --all-features -- -D warnings: clean
  • cargo test -p socket-patch-core --all-features --lib: 6924 passed, 4 failed. The 4 failures (copy_tree::relax_loop_must_not_traverse_symlinked_root, vlt_heal::an_unremovable_hidden_lock_keeps_every_store_entry, pypi_poetry::wire_write_failure_…, pypi_requirements::wire_failure_rolls_back_…) depend on read-only directories blocking writes. They fail only because this sandbox runs as root (uid 0), are unrelated to this diff, and CI runs as non-root.
  • cargo test -p socket-patch-cli --all-features --lib --bins: 936 passed
  • cargo test -p socket-patch-cli --all-features --test scan --test cli --test scan_api_retry_e2e: 247 + 113 + 5 passed
  • A full cargo test --workspace ran out of disk in the sandbox while linking every e2e binary, so CI is the full-suite gate. No wrapper (npm/, pypi/, gem/) is affected: this is client-internal pacing.

Code review (/code-review high)

Fixed: chunk stalls (groups → shared slot pool), the stale "concurrency 10" doc, a slow 12 s serial test (now 6 GETs), the env var left set during the test, the env read and debug line on org clients, and a duplicate test EnvGuard.
Not fixed:

https://claude.ai/code/session_011f9aeT8JHNQ414VVv6qyZm


Generated by Claude Code

Assisted-by: Claude Code:claude-opus-5-5
When the public proxy rejects a batch search chunk, the client falls
back to one GET per package. That fallback kept up to 10 requests in
flight from its own private constant, so it ignored the proxy cap of 4
and SOCKET_API_CONCURRENCY (even =1 still sent 10 at once), and a tight
open-file limit did not make it serial either.

The batch path now reads its cap from utils::concurrent::api_concurrency
when the client is built, like every other patch-API window, and the
private constant is gone. Tests pin the shared peak at the proxy cap and
at 1 under SOCKET_API_CONCURRENCY=1.

Fixes #614

Assisted-by: Claude Code:claude-opus-5-5
The per-package fallback ran its GETs in groups, and waited for the
slowest one in a group before starting the next, so one slow or
throttled GET left the other slots idle. Every GET now waits on the
shared slot pool directly, which keeps the proxy's cap busy.

Only a proxy client reads SOCKET_API_CONCURRENCY for these slots, so
an org client no longer prints a debug line about a bad value. The
tests set the variable only while the client is built and run a
smaller serial case, so they no longer leave it set for other tests.

Refs #614

Assisted-by: Claude Code:claude-opus-5-5
@mikolalysenko
Mikola Lysenko (mikolalysenko) marked this pull request as ready for review October 29, 2026 07:50
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

BugBot review


Generated by Claude Code

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 3317a95. Configure here.

@mikolalysenko Mikola Lysenko (mikolalysenko) added the Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review label Oct 29, 2026
@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Burn-down agent: labeled Ready for review at 3317a9548e1b63a87d55e0df4ada67501cf45bad.

  • CI: 82/82 checks on this head are success or skipped; ci-ok passed. Mergeable, no conflicts.
  • Bugbot: reviewed 3317a95, no findings; no open review threads.
  • Reviewer focus: api/client.rs. The proxy batch fallback now takes its GET concurrency from api_concurrency(true) (4 by default) instead of a hard-coded 10, and uses a shared slot pool instead of fixed groups. This is an intended behavior change: the default fallback fan-out drops from 10 to 4.
  • The Slack announcement could not be sent this run because the send tool is not available. The next run will retry.

Generated by Claude Code

@mikolalysenko

Copy link
Copy Markdown
Collaborator Author

Final review brief

What it does. Fixes #614. When the public proxy rejects a batch chunk or lacks POST /patch/batch, the client falls back to one GET per package. That fallback kept up to 10 requests in flight because of a private constant, so it ignored the proxy cap of 4 and SOCKET_API_CONCURRENCY (=1 still sent 10). The slot pool is now sized from api_concurrency(true), and every fallback GET waits on that shared pool instead of running in fixed groups of 10.

Risk: low-medium. One file. Intended behavior change: the default fallback (and the batch POSTs that share the pool) drops from 10 to 4 in flight on the proxy, which can slow scans that hit the fallback.

Look here

  • client.rs:458: pool size from api_concurrency(true) (proxy only)
  • client.rs:1013: fallback now spawns one task per PURL, all gated by the pool; first throttle error still reported in input order
  • client.rs:930: the POST's slot is released before the fallback runs, so a cap of 1 can't deadlock
  • client.rs:6268: tests set the env only while the client is built, and are #[serial] with the other env tests

Verified. Read the full diff and the POST path. Ran cargo test -p socket-patch-core --all-features --lib proxy_batch_path_cap_tests: 2 passed (both fail on main per the PR's red→green table). PR CI ci-ok and clippy green on this head; Bugbot no findings; no review threads.

Changes I made. None.

Open questions. None blocking. Spawned fallback tasks don't inherit the hold_back_debug task-local; that predates this PR.

Auto-merge is armed, so approving sends it straight to the merge queue.


Generated by Claude Code

Merged via the queue into main with commit dd51a3e Oct 29, 2026
82 checks passed
@mikolalysenko
Mikola Lysenko (mikolalysenko) deleted the agent/fix-proxy-batch-path-concurrency branch October 29, 2026 13:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Ready for review Agent-verified: mergeable, CI green, Bugbot clean — awaiting human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The public-proxy per-package fallback runs 10 requests in flight, ignoring SOCKET_API_CONCURRENCY and the proxy cap of 4

3 participants