Ceilings
A workflow over the graph limits is refused when it is validated. A start that would nest sub-workflows deeper than 5 fails.
Admission, command ordering and run state live in one server at a time. High availability gives failover, not more admission capacity. To move node work off the server, use remote workers.
What is in memory
Not bounded:
- The tracker’s state queue. Every state change goes through one queue that never drops events. An event storm shows up as memory growth, not as lost data. Alert on
yukta.tracker.queue.depth. - Large payloads. Every node that touches a payload holds it in memory. Yukta has no reference-passing for large data, so keep payloads small or pass references yourself.
- Chat history held in memory.
SESSION_MEMORYkeeps the history of every session it has read until the server restarts, unless it is stored in PostgreSQL withyukta.session-memory.ttl.
Back-pressure
Every edge is a stream. A slow consumer slows its producers, so memory between nodes stays bounded without queues. A node with several children moves at the pace of its slowest child. To keep one slow branch from slowing the others, put anedgeBuffer in front of the slow node. When it is full, it drops the oldest (or newest) message instead of pacing the parents. Drops are counted in yukta.edge.dropped. The default stays lossless.
Other bounded paths drop rather than wait: a supervisor that is too slow loses events (yukta.tracker.dropped{kind=supervisor}), and more than 10,000 pending log lines are dropped (yukta.tracker.dropped{type=log}). State events are never dropped by design.
See Delivery and back-pressure.
Limits to set before you accept traffic
All admission limits are off by default. Set them to match the machine and protect other sessions:
A paused run keeps its slot and its clocks are frozen, so paused runs count against
max-concurrent.
The queue admits by priority, then by the session with the fewest running executions, then by arrival, so one busy session cannot starve the others. See Admission and limits.
Metrics
Meters are read atGET /actuator/metrics (the list) and GET /actuator/metrics/{name}. The default configuration exposes the metrics endpoint only. Standard process meters for memory, threads and CPU are available there too.
What to alert on
Tracing
Tracing is optional. Each execution is one observationyukta.run, the parent of one yukta.node observation per node (tags module and status). The observations become spans only when a tracing library is part of the build. The standard build does not include one, and Yukta has no tracing settings of its own.
Sizing
Yukta does not publish throughput figures. Size from your own workflows:- Set the admission limits above, with a queue small enough that waiting starts do not pile up.
- Run the load test below with your workflows and watch memory, threads and CPU.
- Bound server memory with
yukta.tracker.cleanup-ttlandyukta.execution-log.retained-runs. Throttle withyukta.execution.max-concurrentandyukta.execution.queue-capacity. A429shows up as a refused start. - Move heavy nodes to workers.
Load tests
The repository’sperf directory holds two k6 load-test scripts (k6 is a command-line load-testing tool). Both create one session per workflow, then start workflows in turn through POST /api/workflow/start and poll GET /api/executions/{id} until each ends. Run the server with the prod profile first.
The tests report
workflow_e2e (start to end), workflow_success and workflow_rejected (the share of starts refused with 429), each tagged by workflow. The soak test samples memory, thread count and CPU every 30 seconds from /actuator/metrics. Heap and thread count should level off. Steady growth means something is not bounded.