home/docs/start/virtual-thread.md
HertzBeat runs on JDK 25 and uses virtual threads for the blocking execution paths that benefit from them. All hertzbeat.vthreads keys are optional. If you upgrade HertzBeat but do not merge the new YAML block into your existing application.yml, HertzBeat still starts with built-in defaults.
Choose the config file that matches your deployment mode:
hertzbeat/config/application.ymlapplication.yml to /opt/hertzbeat/config/application.ymlscript/docker-compose/*/conf/application.ymlhertzbeat-collector/config/application.ymlYou can leave out the entire hertzbeat.vthreads block.
# No virtual-thread override is required.
HertzBeat will apply runtime defaults automatically.
Use this only when you want to override the defaults:
hertzbeat:
vthreads:
enabled: true
common:
mode: UNBOUNDED_VT
collector:
mode: LIMIT_AND_REJECT
manager:
mode: LIMIT_AND_REJECT
max-concurrent-jobs: 10
alerter:
notify:
mode: LIMIT_AND_REJECT
max-concurrent-jobs: 64
periodic-max-concurrent-jobs: 10
log-worker:
max-concurrent-jobs: 10
queue-capacity: 1000
reduce:
max-concurrent-jobs: 2
window-evaluator:
max-concurrent-jobs: 2
notify-max-concurrent-per-channel: 4
warehouse:
mode: UNBOUNDED_VT
async:
enabled: true
concurrency-limit: 256
reject-when-limit-reached: true
task-termination-timeout: 5000
| Key | Default | Notes |
|---|---|---|
hertzbeat.vthreads.enabled | true | Global switch for the HertzBeat virtual-thread executors |
hertzbeat.vthreads.common.mode | UNBOUNDED_VT | Common short-running tasks |
hertzbeat.vthreads.collector.mode | LIMIT_AND_REJECT | Keeps collector fast-fail admission |
hertzbeat.vthreads.collector.max-concurrent-jobs | 512 | Balanced default for mixed HTTP and JDBC collection workloads on a single node |
hertzbeat.vthreads.manager.mode | LIMIT_AND_REJECT | Keeps manager admission behavior |
hertzbeat.vthreads.manager.max-concurrent-jobs | 10 | Same as the legacy limit |
hertzbeat.vthreads.alerter.notify.mode | LIMIT_AND_REJECT | Notification executor admission |
hertzbeat.vthreads.alerter.notify.max-concurrent-jobs | 64 | Global notify concurrency |
hertzbeat.vthreads.alerter.notify-max-concurrent-per-channel | 4 | Per notification channel/type |
hertzbeat.vthreads.alerter.periodic-max-concurrent-jobs | 10 | Global periodic alert concurrency |
hertzbeat.vthreads.alerter.log-worker.max-concurrent-jobs | 10 | Log alert short-task concurrency |
hertzbeat.vthreads.alerter.log-worker.queue-capacity | 1000 | Bounded queue to preserve backlog semantics |
hertzbeat.vthreads.alerter.reduce.max-concurrent-jobs | 2 | Alarm reduce concurrency |
hertzbeat.vthreads.alerter.reduce.queue-capacity | unbounded | Leave unset to keep the legacy unbounded queue behavior |
hertzbeat.vthreads.alerter.window-evaluator.max-concurrent-jobs | 2 | Window evaluator concurrency |
hertzbeat.vthreads.alerter.window-evaluator.queue-capacity | unbounded | Leave unset to keep the legacy unbounded queue behavior |
hertzbeat.vthreads.warehouse.mode | UNBOUNDED_VT | Storage short tasks; downstream pools still limit real resources |
hertzbeat.vthreads.async.enabled | true | Dedicated @Async executor switch |
hertzbeat.vthreads.async.concurrency-limit | 256 | @Async concurrency guard |
hertzbeat.vthreads.async.reject-when-limit-reached | true | Reject extra @Async tasks at the limit |
hertzbeat.vthreads.async.task-termination-timeout | 5000 | Milliseconds |
512 is the default because it is a good mixed-workload starting point. In local verification, HTTP-heavy collection continued scaling beyond 512, while JDBC-style collection peaked around 512 and dropped when concurrency was pushed higher.768 first and then 1024 if timeouts, error rates, and connection usage remain stable.collector.max-concurrent-jobs around 256 to 512. In this type of workload, raising concurrency above 512 can reduce total throughput instead of improving it.512 as the starting point. It is a safer default than 768+ for mixed environments.collector.max-concurrent-jobs when the collector talks to a small database, a low-capacity HTTP endpoint, or fragile network devices.alerter.notify.max-concurrent-jobs or notify-max-concurrent-per-channel only if your notification providers and HTTP connection pools can absorb the increase.warehouse.mode unbounded unless you have a clear bottleneck model. Database and TSDB client pools should remain the main limiters.reduce.queue-capacity and window-evaluator.queue-capacity are intentionally left unset by default so existing queueing semantics remain compatible.429 or 5xx, database pool wait time, and memory or file descriptor usage before raising it again.Disable HertzBeat virtual-thread executors with:
hertzbeat:
vthreads:
enabled: false
This rolls the affected executors back to their legacy platform-thread implementations.
application.yml unchanged.hertzbeat.vthreads block only when you want to tune concurrency limits or explicitly disable the feature.