Rewrite of worker pool with multi-task dispatch per worker#26
Rewrite of worker pool with multi-task dispatch per worker#26tcely wants to merge 4 commits intokikkia:masterfrom
Conversation
f073ba1 to
2d7bc89
Compare
|
Hey there, I took a look a while back and was thinking a bit on it, what is the benefit here vs the current implementation? The current implementation is simple and easily understandable. Since the these jobs are only used for the first request on a new script its rare they happen. The public instance only see's a couple of those per week despite serving about 200-300k deciphers a day. Unless there is some big improvement or bug that I have not seen or experienced at my scale, then I could be open to it, but I think the simplicity and ease of understanding is preferable. |
|
I have also seen very few actual new players since I put up a server and pointed I'd very much like to see dispatch using a loop to assign more than one task before calling dispatch again. A benefit of the new worker-pool implementation is robustness and liveness under edge cases that can otherwise lead to stuck requests, silent leaks, or long-lived degraded behavior. In other words: it’s about “never getting wedged.” Concretely, the new code adds protections that the current simple model doesn’t provide: 1) Hard liveness guarantees (prevents “hung forever” requests)New behavior
Current behavior risk
Even if “new script” events are rare, the impact of a single hung request can be high (clients waiting forever, connection accumulation, etc.). 2) Worker recycling via message budgets (prevents long-lived memory/GC creep)New behavior
Why it matters even if first-script is rare
If at your scale scripts are rarely processed, the budget will rarely tick down—so overhead is near-zero, but it still protects you against “one worker that got large / leaky over months.” For very small scale situations, the limit can be significantly lowered. 3) Persistent handlers + in-flight tracking = fewer “state bugs”New behavior
Current behavior risk
4) Safer task settle (prevents crashes caused by userland reject/resolve)New behavior
Current behavior risk
5) Backoff-based recovery scaffoldingThe I agree this is heavier than needed for normal operation; but it’s specifically targeted at the hard-to-debug “pool got into a broken bookkeeping state” incidents. 6) Deque selection is mostly about avoiding pathological array behaviorThis PR adds
Tradeoff: You’re right about simplicityThe current implementation is extremely easy to reason about. The new one is more complex and has more moving parts (idle stack/set, in-flight maps, retire sets, timers, recovery state). If your primary concern is maintainability and you haven’t seen worker wedging / memory creep, a reasonable compromise would be:
That would retain simplicity while still addressing the biggest “production footguns.” Bottom lineAt your scale, you likely won’t see a throughput improvement because this path is cold most of the time. The benefit is mainly:
If those failure modes aren’t a concern for your deployment, I’d recommend a reduced-scope version (less recursion by using a loop in dispatch, timeouts + budget recycling) to preserve readability while still getting the reliability wins. |
PR Type
Enhancement
Description
Refactor worker pool to process multiple tasks per worker lifecycle
Implement per-worker message limits with automatic worker rotation
Add robust error handling, timeouts, and recovery mechanisms
Introduce deque-based task queue with pluggable implementations
Add comprehensive worker lifecycle management and in-flight task tracking
Diagram Walkthrough
flowchart LR A["Task Queue<br/>Deque-based"] -->|dispatch| B["Idle Worker<br/>with Budget"] B -->|postMessage| C["Worker Process"] C -->|message event| D["Task Resolution<br/>resolve/reject"] D -->|releaseWorker| E{Budget<br/>Remaining?} E -->|Yes| F["Return to Idle Pool"] E -->|No| G["Retire & Replace"] F -->|fillWorkers| H["Create New Worker<br/>if needed"] C -->|error/crash| I["Reject Task<br/>& Retire Worker"] I -->|scheduleRefill| HFile Walkthrough
taskQueueDeque.ts
Add pluggable deque-based task queue implementationsrc/taskQueueDeque.ts
@alg/dequeand@korkje/dequeTaskQueueinterface withpush(),shift(), andlengthTASK_QUEUE_DEQUE_IMPLcontrols whichimplementation is used
types.ts
Update types for worker limits and task trackingsrc/types.ts
WorkerWithStatuswithWorkerWithLimitinterface trackingmessage budget
TaskQueueinterface for queue abstractionInFlighttype to track in-flight tasks with timeout IDsSafeCallOptionstype for error handling and logging configurationutils.ts
Add safe function call utility with error handlingsrc/utils.ts
safeCall()function for safe function invocation with errorhandling
looksLikeSafeCallOptions()type guard for options validationtracking
thiscontext usingReflect.apply()workerPool.ts
Refactor worker pool with budgets and lifecycle managementsrc/workerPool.ts
MESSAGES_LIMITconfigurationreplacement
fillWorkers()anddispatch()functions
releaseWorker(),setIdle(),setInFlight(),clearInFlight(),takeIdleWorker()README.md
Add comprehensive environment variables documentationsrc/README.md
TASK_QUEUE_DEQUE_IMPLfor queue implementation selectionMAX_THREADSfor worker pool concurrency controlMESSAGES_LIMITfor per-worker message budget configurationAPI_TOKEN,HOST,PORT,HOME,XDG_CACHE_HOME, cache size limits