liveBook · Manning

@Blocking

As explained in Quarkus in Action

@Blocking is an annotation that marks an operation as potentially blocking so a reactive framework can schedule it on a worker thread.

A reactive return type does not guarantee that every operation inside a method is nonblocking. Quarkus normally dispatches methods returning reactive types to an event-loop thread, where blocking work can prevent other requests from progressing. Put @Blocking on a method that must perform such work to have Quarkus run it on a worker thread instead. section 8.1.1 section 8.4.1

A gRPC inventory service illustrates the distinction. Its add and remove methods use reactive types, but database inserts and deletes can block. Both methods therefore need @Blocking. The annotation changes where the methods execute; it does not turn their database operations into nonblocking I/O. section 7.2.2 section 8.4.2

Listing 8.5 The GrpcInventoryService#add method
@Override
@Blocking
public Multi<CarResponse> add(Multi<InsertCarRequest> requests) {
  return requests
      .map(request -> {
          Car car = new Car();
          car.setLicensePlateNumber(request.getLicensePlateNumber());
          car.setManufacturer(request.getManufacturer());
          car.setModel(request.getModel());
          return car;
      }).onItem().invoke(car -> {
          QuarkusTransaction.requiringNew().run( () -> {
              carRepository.persist(car);
              Log.info("Persisting " + car);
          });
      }).map(car -> CarResponse.newBuilder()
          .setLicensePlateNumber(car.getLicensePlateNumber())
          .setManufacturer(car.getManufacturer())
          .setModel(car.getModel())
          .setId(car.getId())
          .build());
}

The add method is annotated @Blocking even though it returns Multi<CarResponse>. Its pipeline maps each request to a Car, persists it through the repository, then maps it to a response. section 8.4.2

Worker-thread dispatch and transaction boundaries solve different problems. In the streaming add method, QuarkusTransaction.requiringNew().run() gives each persist operation its own transaction. Making the entire stream one transaction would leave it open until the stream closed, risking a bottleneck or timeout. Conversely, a genuinely nonblocking operation can remain on the event loop; @NonBlocking provides the opposite scheduling hint. section 7.2.2 section 8.1.1

How Quarkus in Action covers it

How deep it goes
Explains signature-based scheduling and demonstrates annotated database methods, but does not detail the worker-pool implementation.
Examples
Uses a gRPC car-inventory stream with repository persistence and a JAX-RS blocked-thread warning.
What it assumes
Assumes familiarity with Java service methods and database operations while explaining why reactive return types affect thread selection.
What you can do after
Apply it via Quarkus to dispatch methods with blocking database work to worker threads, even when they return reactive types.
How it connects
Connects event loops, worker threads, reactive types, gRPC, database I/O, and transaction boundaries.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Quarkus in Action. The section links point to the parts of the book it draws on.