Skip to content

Assign a distinct event loop to each virtual-thread verticle instance#6241

Open
Develop-KIM wants to merge 1 commit into
eclipse-vertx:masterfrom
Develop-KIM:fix/virtual-thread-event-loop-distribution
Open

Assign a distinct event loop to each virtual-thread verticle instance#6241
Develop-KIM wants to merge 1 commit into
eclipse-vertx:masterfrom
Develop-KIM:fix/virtual-thread-event-loop-distribution

Conversation

@Develop-KIM

Copy link
Copy Markdown

Motivation

Deploying a verticle with setInstances(N) and ThreadingModel.VIRTUAL_THREAD pins every instance to the same event loop (vert.x-eventloop-thread-0) instead of distributing them across the event loop group. Raising the instance count therefore stops scaling I/O — the single event loop saturates while the virtual threads starve. This is a regression from Vert.x 4.5.x, where each virtual-thread instance was assigned an event loop via eventLoopGroup.next().

The reporter's k6 load test (setInstances(300), VIRTUAL_THREAD) hit a hard ceiling of ~40k RPS because all 300 instances shared one event loop.

Fixes #5924

Root cause

In DefaultDeployment.deploy(), the VIRTUAL_THREAD branch copied the WORKER pattern: the first instance's event loop was captured in workerLoop and forced on every subsequent instance via .withEventLoop(workerLoop). The EVENT_LOOP branch omits .withEventLoop(...), so ContextBuilderImpl.build() selects the next loop via nettyEventLoopGroup().next() (round-robin).

Change

Build a fresh context for each virtual-thread instance (no shared-loop reuse), so each one round-robins over the event loop group like the event-loop model already does. workerLoop is now used only by the WORKER branch.

Test

DeploymentTest#testDeployInstancesUseDistinctEventLoops deploys 4 VIRTUAL_THREAD instances (8-loop pool) and asserts 4 distinct netty event loops. It fails on the current code (expected:<4> but was:<1>) and passes with the fix; the full DeploymentTest and ContextTest suites remain green.

@vietj — you mentioned possibly making this configurable. This restores the scaling default; happy to layer a toggle on top in a follow-up if you'd prefer.


Implemented with AI assistance (Claude); I reviewed, tested and own all changes.

Deploying a verticle with setInstances(N) and ThreadingModel.VIRTUAL_THREAD
pinned every instance to the first instance's event loop, so raising the
instance count no longer scaled I/O: the single event loop saturates while
the virtual threads starve. This regressed Vert.x 4.5.x, where each
virtual-thread instance was assigned an event loop via eventLoopGroup.next().

DefaultDeployment now builds a fresh context for each virtual-thread
instance, letting ContextBuilderImpl round-robin over the event loop group
as the event-loop threading model already does.

Fixes eclipse-vertx#5924

Implemented with AI assistance (Claude); all changes were reviewed, tested
and are owned by the author.

Signed-off-by: Develop-KIM <kimdonghwan913@gmail.com>
@Develop-KIM Develop-KIM force-pushed the fix/virtual-thread-event-loop-distribution branch from 8df9168 to 99c36a7 Compare July 10, 2026 17:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Vert.x 5] Regression: setInstances(N) shares a single EventLoop for all instances when using ThreadingModel.VIRTUAL_THREAD

1 participant