feat(pool): elastic import admission to prioritize streams#595
Open
javi11 wants to merge 1 commit into
Open
Conversation
Under high load, VFS/FUSE streams could starve when many ARR-driven NZB imports ran concurrently. nntppool v4 already has priority dispatch (streams use BodyPriority), but priority only kicks in once a connection frees — when every connection is mid-body for a long-running import, even priority requests wait. Add a pool-level admission controller with two adaptive caps: - max_concurrent_imports (capIdle) — when no stream is active - max_concurrent_imports_while_streaming — when any stream is active The gate sits at Processor.ProcessNzbFile entry (single chokepoint for queue worker, scanner watcher, background reprocess) — before any pool work, so the 30s body timeouts can never be eaten by queue waits. StreamTracker keeps an atomic active-stream count and notifies the pool on Add/Remove; the pool reads the count to pick the effective cap and wakes queued waiters when streams end. Both caps default to 0 (unlimited) — byte-identical behaviour until configured.
5 tasks
Contributor
|
I prefer this approach yeah! |
Owner
Author
|
Nice let me finish the review do a test and we are good to go. Thanks for the contribution |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Under high load, VFS/FUSE streams could starve when many ARR-driven NZB imports ran concurrently. nntppool v4 already has priority dispatch (streams use
BodyPriority), but priority only kicks in once a connection frees — when every connection is mid-body for a long-running import, even priority requests wait.This PR adds an elastic pool-level admission controller with two adaptive caps so imports yield to streams under load.
What changed
internal/pool/admission.go(new) —ImportAdmission, an adaptive FIFO counting semaphore with aStreamActivitySourceinterface, ctx-cancel-safeAcquirewith grant forwarding (no lost wake-ups), and a fast-path no-op when both caps are 0.internal/pool/manager.go—Managerinterface gainsAcquireImportSlot,SetAdmissionCaps,SetStreamSource,NotifyStreamChange.internal/importer/processor.go—ProcessNzbFileacquires an admission slot before any pool work and releases on return. This is the single chokepoint for queue worker, scanner watcher and background-reprocess paths. The gate is at the import-entry boundary, so the existing 30 s body timeouts inside the parser are never consumed by queue waits.internal/api/stream_tracker.go— atomicactiveCount,ActiveStreams() int,SetChangeNotifier.AddStream/Removeincrement/decrement and notify the pool.internal/api/server.go— wiresstreamTracker ↔ poolManagerinNewServer.internal/config/manager.go+accessors.go— newImportConfig.MaxConcurrentImportsandMaxConcurrentImportsWhileStreamingplus accessors. Both default to0(unlimited → byte-identical to today).internal/importer/service.go— pushes caps to the pool at startup and inside the existingOnConfigChangehandler so live config reloads take effect.internal/pool/admission_test.go(new) — race-detector-clean tests covering disabled fast-path, cap blocking, FIFO order, stream-aware shrink, growing caps waking waiters, ctx-cancel cleanup, and the grant-forwarding race.internal/health/repair_e2e_test.goandinternal/nzbfilesystem/metadata_remote_file_test.gofor the expanded interface.Why the gate is at NZB-entry, not per-Body
Body fetches carry a 30 s timeout (
parser.go:537,parser.go:737). A gate held there would consume the timeout while waiting for admission — under contention an import could time out before reaching the network and trigger spurious retries. The gate sits before the import starts work; the timer doesn't start running while an import is queued at the gate.Once admitted, every
cp.Body/cp.BodyAsyncruns untouched — same parallelism (MaxImportConnections), same 30 s budget, same retry behaviour.Behaviour example
Provider exposes ~30 connections. Recommended config:
BodyPrioritypicks them up immediately.NotifyStreamChangewakes up to 5 queued imports.No bytes wasted, no retries, no preemption.
Rollout
Defaults are
0/0→ controller disabled → behaviour byte-identical to current. Operators opt in via the two new YAML keys.Not in this PR
BodyAsynccall inparser.go:783(per-NZB internal concurrency). Orthogonal to streams-vs-imports priority; logged as a follow-up.Test plan
go build ./...clean.go vet ./...clean.go test -race -count=1clean forinternal/pool,internal/config,internal/api,internal/health,internal/nzbfilesystem,internal/importer/....