Skip to content

Concurrency tuning

Included free on every install.

Your instance hands background work to a shared pool of workers, runs some work in parallel, such as cache warmup and dashboard queries, and can run the hooks that follow a save in the background. Concurrency tuning shows how busy each of these is and lets a super admin resize them without a restart.

PartWhat it doesDefault
Worker poolRuns the background work that features hand off.100 workers, 30 second task timeout
Parallel engineRuns a batch of tasks side by side, such as cache warmup and dashboard queries.4 at once, 30 second timeout
Async hooksRuns after-save hooks on the pool instead of inside the request.Off, 8 workers, a queue of 1024, 5 second timeout
TrackerCounts the background tasks the instance runs, by name and by feature.

Each replica has its own set, and a change made over the API applies to the replica that received it until it restarts. There are no per-tenant limits.

You need an admin token in TOKEN. The quickstart shows how to get one.

  1. Read the worker pool:

    Terminal window
    curl http://localhost:3001/api/admin/debug/goroutines/pool \
    -H "Authorization: Bearer $TOKEN"
    { "size": 100, "active": 0, "waiting": 0, "completed": 3, "failed": 0, "dropped": 0, "avg_latency": "127.377427ms", "task_timeout": "30s" }
  2. Read the parallel engine:

    Terminal window
    curl http://localhost:3001/api/admin/debug/goroutines/parallel \
    -H "Authorization: Bearer $TOKEN"
    { "max_concurrent": 4, "timeout": "30s" }
  3. Read the async hook queue:

    Terminal window
    curl http://localhost:3001/api/admin/debug/goroutines/async-hooks \
    -H "Authorization: Bearer $TOKEN"
    { "enabled": false, "workers": 8, "queue_size": 1024, "timeout": "5s", "queued": 0, "running": 0, "overflow": 0, "dropped": 0 }
  4. As a super admin, grow the pool. The answer is the new state:

    Terminal window
    curl -X PUT http://localhost:3001/api/admin/debug/goroutines/pool \
    -H "Authorization: Bearer $TOKEN" \
    -H "Content-Type: application/json" \
    -d '{"size": 200}'
  5. In the admin console, open Insight > Observability. The Goroutine engine section shows the same views, with forms for the changes.

FieldMeans
Pool active, waitingTasks running now, and tasks waiting for a free worker. A steady waiting above 0 means the pool is too small.
Pool failedTasks that ran and failed. A bigger pool does not help.
Pool droppedTasks that never started, because their caller gave up before a worker was free. Read this before you raise the size.
Async hooks overflowHooks that found the queue full and ran inside the request instead.
Status by_ownerLive tasks and the oldest one's age, per feature. A count that only grows is a leak.

Writes need a super admin. The pool write needs size. In the other two, a field you leave out keeps its value. Durations are strings such as 30s or 5m. Each write answers the new state and is recorded in the audit log with the values before and after.

RequestBodyAllowed values
PUT /api/admin/debug/goroutines/pool{"size": 200}size from 1 to 4096.
PUT /api/admin/debug/goroutines/parallel{"max_concurrent": 8, "timeout": "30s"}max_concurrent from 1 to 1024. timeout above 0, at most 1h.
PUT /api/admin/debug/goroutines/async-hooks{"workers": 8, "queue_size": 1024, "timeout": "5s", "enabled": true}workers from 1 to 1024. queue_size from 1 to 65536. timeout above 0, at most 10m.

Growing the pool lets waiting work start at once. Shrinking it lets running work finish. Switching async hooks on or off applies from the next hook.

These set the starting values.

VariableWhat it doesDefault
GOROUTINE_ENGINE_ENABLEDApplies GOROUTINE_ENGINE_POOL_SIZE, lets ASYNC_HOOKS_ENABLED take effect, and runs cache warmup and shutdown in parallel.false
GOROUTINE_ENGINE_POOL_SIZEWorkers in the pool. Must be positive. Read only when GOROUTINE_ENGINE_ENABLED=true.100
ASYNC_HOOKS_ENABLEDRuns after-save hooks in the background from the start. Needs GOROUTINE_ENGINE_ENABLED=true.false
ASYNC_HOOK_TIMEOUTHow long one async hook may run.5s
DB_WARMUP_PARALLELISMHow many tasks the parallel engine runs at once. Must be positive.4

A value that does not parse stops the boot with a message naming the variable, and so does a pool size or parallelism that is not positive. An ASYNC_HOOK_TIMEOUT of zero or less keeps the 5s default. Concurrency tuning runs on every install. If you set LYEVE_PLUGINS to choose which features start, include goroutine-engine in it. See licensing and tiers.

MethodPathWhoAnswers
GET/api/admin/debug/goroutines/statusAdmintotal, runtime_total, by_source, by_owner, max_goroutines, leak_threshold.
GET/api/admin/debug/goroutines/poolAdminsize, active, waiting, completed, failed, dropped, avg_latency, task_timeout.
GET/api/admin/debug/goroutines/parallelAdminmax_concurrent, timeout.
GET/api/admin/debug/goroutines/async-hooksAdminenabled, workers, queue_size, timeout, queued, running, overflow, dropped.
PUT/api/admin/debug/goroutines/poolSuper adminResize the pool.
PUT/api/admin/debug/goroutines/parallelSuper adminChange the parallel engine.
PUT/api/admin/debug/goroutines/async-hooksSuper adminChange async hooks.
StatusMessage
400invalid JSON, also sent for a field the route does not take.
409async hooks need a worker pool, and this install runs without one
422nothing to change: set max_concurrent or timeout (or workers, queue_size, timeout or enabled)
422timeout must be a duration such as 30s
422A value outside the allowed range. The message states the range.