Description
Currently, VertxRoutingContextHandler invokes dispatcher.invoke() directly on the Vert.x event loop thread. If a Jakarta REST resource performs blocking work (JDBC calls, Thread.sleep, file I/O, etc.), it blocks the event loop and triggers Vert.x BlockedThreadChecker warnings.
Resources that use RESTEasy's async APIs (@Suspended AsyncResponse, CompletionStage return types) work correctly — VertxExecutionContext.executeBlockingIo() already moves blocking I/O to worker threads when invoked. The gap is synchronous, blocking resource methods that don't opt into async.
Current Workaround
Write non-blocking resources, or use @Suspended AsyncResponse / CompletionStage return types to move blocking work off the event loop.
Possible Approaches
- Always
executeBlocking(): Wrap all dispatcher.invoke() calls in vertx.executeBlocking(). Safe but penalizes purely non-blocking endpoints with unnecessary thread context switching.
- Smart dispatch: Inspect resource method signatures at deploy time (similar to Quarkus's
@Blocking/@NonBlocking annotations) and route to the event loop or worker thread accordingly. More complex but preserves event loop performance for reactive resources.
- Annotation-based opt-in: Provide a
@Blocking annotation that users apply to resource methods or classes to indicate they should run on worker threads.
Description
Currently,
VertxRoutingContextHandlerinvokesdispatcher.invoke()directly on the Vert.x event loop thread. If a Jakarta REST resource performs blocking work (JDBC calls,Thread.sleep, file I/O, etc.), it blocks the event loop and triggers Vert.xBlockedThreadCheckerwarnings.Resources that use RESTEasy's async APIs (
@Suspended AsyncResponse,CompletionStagereturn types) work correctly —VertxExecutionContext.executeBlockingIo()already moves blocking I/O to worker threads when invoked. The gap is synchronous, blocking resource methods that don't opt into async.Current Workaround
Write non-blocking resources, or use
@Suspended AsyncResponse/CompletionStagereturn types to move blocking work off the event loop.Possible Approaches
executeBlocking(): Wrap alldispatcher.invoke()calls invertx.executeBlocking(). Safe but penalizes purely non-blocking endpoints with unnecessary thread context switching.@Blocking/@NonBlockingannotations) and route to the event loop or worker thread accordingly. More complex but preserves event loop performance for reactive resources.@Blockingannotation that users apply to resource methods or classes to indicate they should run on worker threads.