Crypto workflow concurrency limits should define which work holds a slot, how much waiting work can be admitted and what happens when capacity is full. Apply limits at the shared resource boundary, expire stale queued evaluations before they start and count retries against the same controls. A free worker slot permits processing; it does not authorize a trade or reserve capital.
Name the thing that occupies a slot
“Two concurrent workflows” is incomplete. Does a slot cover an entire run, one analysis step, an outbound request or a database transaction? A run waiting for human approval might remain logically active without occupying a model-request slot. Conversely, one active run might launch several simultaneous requests.
Separate at least three controls:
| Control | Unit counted | What saturation means |
|---|---|---|
| Active-work limit | Tasks currently using the named resource | Another task cannot start using it yet |
| Waiting-work limit | Accepted tasks not yet started | Further arrivals need an explicit admission outcome |
| Rate limit | Requests or weighted usage over time | Dispatch must wait even if a worker is free |
A provider can allow two simultaneous calls while permitting fewer than two starts each second. Fast completions do not waive that rate rule. The exchange API rate-limit guide covers request weights and recovery traffic; concurrency protects a different dimension.
Concrete engines illustrate why definitions matter. Inngest's step concurrency documentation counts executing steps and allows waiting runs to release capacity. That is an Inngest rule, not a description of KLINEO. For any workflow system, document the resource, scope, acquisition point and release point before choosing a number.
Bound the waiting room as well as the workers
A semaphore can restrict entry to a protected section. Python's asyncio semaphore documentation describes an acquire operation that waits when its counter reaches zero, and a release that restores capacity. It also states that these primitives are not thread-safe. A process-local semaphore therefore does not establish a shared limit across separate worker processes.
Nor does a semaphore alone bound the number of tasks waiting for it. Launching a task for every incoming signal can leave thousands of suspended tasks and their inputs in memory while only two perform useful work.
An explicitly bounded queue addresses accepted waiting items. In Python's asyncio queue documentation, a positive maxsize makes put() wait when full; put_nowait() raises QueueFull. A zero maximum means an unbounded queue. Those are local Python semantics, not a distributed admission service.
Blocking a producer is useful only when it pushes pressure back to a bounded upstream source. Creating another producer task for every arrival merely moves the backlog into waiting producers. Bound total accepted work, payload bytes and upstream waiting requests too. Durable storage changes where work waits; it does not make an unlimited backlog timely.
Trace eight arrivals through five accepted places
Consider a hypothetical research stage with two worker slots and three waiting places. Eight distinct evaluations, A through H, arrive simultaneously at time zero. The scheduler admits them in that order. Each started evaluation takes exactly four seconds, with no retries, overhead or competing workload. A queued evaluation may start only before its six-second expiry, measured from arrival.
This is a deterministic teaching fixture, not a measured benchmark or a recommended configuration.
| Time | Workers | Waiting queue | Recorded admission outcome |
|---|---|---|---|
| 0 seconds | A, B | C, D, E | F, G, H rejected: capacity full |
| 4 seconds | C, D | E | A and B complete; C and D start before expiry |
| 6 seconds | C, D | Empty | E expires without starting |
| 8 seconds | Empty | Empty | C and D complete; no further dispatch |
Five evaluations were admitted, four started and three were rejected. Of the five admitted, one expired. The final accounting is 4 completed + 1 expired + 3 rejected = 8 arrivals. Queue expiry does not cancel C and D: the stated deadline governs latest start, not completion. A completion deadline would require a different policy and outcome.
If expiry cleanup runs only when a worker becomes free, E may remain physically queued until eight seconds. It must still be rejected at that dispatch check. A timer can remove it sooner, but correctness must not depend on the timer firing exactly on time.
With two slots and four seconds of service per task, this simplified stage can complete 2 ÷ 4 = 0.5 tasks per second when continuously busy. An ongoing arrival rate of one task per second exceeds that capacity. A larger queue delays rejection while increasing waiting time; it cannot repair the throughput deficit. Variable service times, provider throttles and shared resources make this simple estimate insufficient for production sizing.
Decide what a full queue means
Select overload behavior per work class rather than silently dropping whichever message arrives last. A research refresh can sometimes be superseded by a newer refresh for the same instrument and decision context. An unresolved order-status check carries different obligations and must not be discarded under that convenience rule.
Amazon's queue-backlog guidance discusses message age, workload isolation and upstream backpressure. Applying those ideas to trading research suggests measuring useful age alongside queue depth: five old signals can be less useful than twenty current bookkeeping tasks. That application is an editorial design inference, not an AWS trading recommendation.
Record a distinct outcome such as queued, rejected for capacity, expired before start or superseded under a named policy. An infrastructure rejection does not mean the strategy's entry condition failed. Similarly, AI trading abstention thresholds concern declining a decision under uncertainty; lack of computing capacity should retain its own reason.
The research-tool timeout guide explains whether enough decision time remains after a call begins. Queue admission must account for the waiting time before that call. A fresh arrival timestamp cannot renew old market evidence.
Make shared admission survive awkward timing
Four failure cases deserve explicit tests:
- Two worker processes each allow two tasks. Without a shared authority, the apparent two-task cap becomes four. Scope a global limit across all participating dispatchers; add per-account or per-provider limits where required.
- A parent holds the last worker slot while waiting for a child that needs that slot. Nothing can advance. Separate orchestration waits from resource use, or design acquisition rules that prevent this dependency cycle.
- A slow provider occupies every general-purpose slot. Order reconciliation then waits behind optional research. Isolate essential work with protected capacity, while keeping the combined allocation within the actual downstream capacity.
- A timed-out call continues remotely. Releasing its local slot and immediately retrying can exceed the intended remote concurrency. Track uncertain in-flight operations and recovery separately; a local timeout is not proof that remote work ended.
The last case connects to workflow cancellation propagation: stopping local scheduling and observing external termination are separate events. If remote completion cannot be established, record that uncertainty rather than claiming a strict remote cap.
Shared admission needs coordinated slot claims, durable job identities and recovery rules appropriate to its storage system. Retries re-enter admission rather than bypassing it. Lease expiry also needs protection against an old worker resuming after replacement work starts. These are implementation requirements to review, not claims that a queue alone guarantees them.
Keep a resource-admission worksheet
For each resource, record the following before increasing parallelism:
| Worksheet field | Evidence to retain |
|---|---|
| Scope and counted activity | Provider, account or pool; exact slot lifetime |
| Capacity and waiting bound | Active cap, accepted queue count and byte cap |
| Timing | Arrival, dispatch, expiry and completion times |
| Overload policy | Rejection, bounded waiting or explicit supersession |
| Fairness | Which work classes can progress under sustained load |
| Recovery | Claim ownership, retry count and uncertain remote work |
Use a controlled harness to reproduce the eight-arrival fixture, then vary service times, repeat deliveries, crash a worker and saturate one provider. Inspect peak active count, oldest waiting age and every admission outcome. Mark untested behavior explicitly.
Computing capacity remains separate from the financial budget across workflow branches. Two available workers can each propose an unaffordable order. A resource slot cannot replace account, exposure or execution approval checks. This worksheet describes architecture to evaluate and does not establish that KLINEO exposes configurable worker pools or queue policies.
Editorial method: AI-assisted synthesis of the primary documentation cited above, reviewed October 23, 2026. The scheduler, times and counts are original hypothetical examples. No load test, exchange order or KLINEO concurrency benchmark was run.
