Skip to main content
Yukta runs the engine and the control plane in one server process. This page lists what bounds that process, which limits to set before you accept traffic, and how to watch it.

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_MEMORY keeps the history of every session it has read until the server restarts, unless it is stored in PostgreSQL with yukta.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 an edgeBuffer 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 at GET /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 observation yukta.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:
  1. Set the admission limits above, with a queue small enough that waiting starts do not pile up.
  2. Run the load test below with your workflows and watch memory, threads and CPU.
  3. Bound server memory with yukta.tracker.cleanup-ttl and yukta.execution-log.retained-runs. Throttle with yukta.execution.max-concurrent and yukta.execution.queue-capacity. A 429 shows up as a refused start.
  4. Move heavy nodes to workers.

Load tests

The repository’s perf 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.

Limits and defaults