Contact Us 1-800-596-4880

Non-Blocking Operations

The Mule 4 execution engine is based on reactive streams. That means there’s top level support for non-blocking operations. By default, all operations are blocking. As you probably noticed, the semantics of all the operation examples you have seen before are inherently blocking: the runtime invokes a method and gets a value in response. That is fine for operations that are more CPU intensive than they are IO intensive, or for operations that consume APIs that cannot be consumed in a non-blocking way, such as JDBC (the API that powers the Database connector).

HTTP is one of the protocols that does supports this. Being able to perform non-blocking HTTP request is key for scalability of gateway proxies. Any connector consuming a REST API can also benefit from this.

This simplified HTTP request example shows how the SDK allows you to develop non-blocking operations:

public void request(String url, @Connection HttpClient client, @Content String body, (1)
    CompletionCallback<InputStream, HttpAttributes> callback ) { (2)
 client.send(url, body, new HttpResponseCallback() {
   void onResponse(HttpResponse response) {
     callback.success(Result.<InputStream, HttpAttributes>builder() (3)
                          .output(response.getBody())
                          .attributes(toAttributes(response))
                          .build());
   }

   void onError(Exception e) {
     callback.error(e); (4)
   }
 });
}
1 Unlike blocking operations, the return type is not specified through the method’s return type. Non-blocking operations always need to be specified through void methods.
2 An operation becomes non-blocking when it has an argument of type CompletionCallback. This interface uses generics to specify the operation’s return type. The first generic is the type of the output payload, and the second one the type of the output attributes. These generics are mandatory.
3 The return value is passed through the success(Result) method of the CompletionCallback. See Result for more information on the Result object
4 Non-blocking operations should not throw exceptions. Any errors should be channeled through the error() method in the callback.
Notice that in this example, the CompletionCallback is being consumed from between an HttpResponseCallback. This second callback is provided by an HttpClient that supports asynchronous responses. As stated previously, in order to do non-blocking, you need to be consuming an API which allows that.

The result of this code is a non-blocking operation that returns an InputStream as payload and an HttpAttributes object as message attributes.

Void Operations

It is possible to have a void non-blocking operation. The use case for that is an IO operation that does not return anything, such as writing to a file:

public void write(String path, @Content byte[] bytes, CompletionCallback<Void, Void> callback) {
 writeAsync(result -> callback.success());
}

As you can see here, you can use Java’s Void type to indicate that the operation is void.

Gotcha

You can also have an operation that sets the payload (but NOT the attributes) like this:

public void foo(CompletionCallback<String, Void> callback) {
 ...
}

Execution Types

As stated above, Mule 4 now has a reactive execution engine. Unlike Mule 3.x, where each flow had its own thread pool, SEDA queues, and so on, the Mule 4 runtime has a few global executors through which all tasks are run.

To schedule tasks correctly, the runtime needs to know which kind of processing each operation is going to perform. The different processing types are:

  • CPU_INTENSIVE: Intensive processing, such as a complex, time-consuming calculations or transformations. Note that the SDK will never infer a CPU_INTENSIVE type. See Specifying Execution Types.

  • CPU_LITE: Processing that neither blocks nor is CPU intensive, such as message passing, filtering, routing, or non-blocking IO.

  • BLOCKING: Blocking processing that performs blocking IO operations, Thread.sleep(long), Lock.lock(), or any other technique that blocks the current thread during processing. Although this type includes Thread.sleep, using it is a bad practice that you should avoid.

Inferring Execution Types Automatically

The SDK follows these rules to make a best guess about the execution type so that you don’t have to specify it explicitly:

  • Operation requires connection and is blocking: BLOCKING

  • Operation requires connection and is non-blocking: CPU_LITE

  • None of the above: CPU_LITE

If the execution type to which your operation corresponds does not match the best guess, you should specify the correct one. Failing to do so will negatively impact the performance of any app that uses your module.

Specifying Execution Types

You can manually specify an operation’s execution type through the @Execution annotation, for example:

@Execution(CPU_INTENSIVE)
public void computeFlightPlan() {
  ...
}