Aggregated Error Handling in a vertx application

2,108 views
Skip to first unread message

Paul Klein

unread,
Dec 9, 2015, 5:18:39 AM12/9/15
to vert.x
Hi,

I'm trying to build a vert.x-3 application in java that contains different types of verticles and connected to external services.
Having built the structure and the main features, I'm trying to figure out what is the best way of aggregate exceptions
in order to send them to a error handling service (Sentry); or at least the most acceptable solution.

Could I only rely on a logging system that is not too verbose for example ?

I could also use an "ErrorHandler object" to whom I will give all the raised exceptions and will be in charge of further processing.
It can also be implemented as a verticle that listens on the eventBus so errors are not handled locally but across the whole cluster.

Does someone has something specific to recommend that will have a good synergy with vertx ?

Arnaud Estève

unread,
Dec 9, 2015, 5:31:06 AM12/9/15
to vert.x
That's a very good design question I'm really interested about (and curious to read what ppl have in mind)

What I would do :

Create a ErrorHandlerService interface as generic as possible.

Then this service could be implemented by, say, SentryErrorService.

You can instanciate the service and use it within your verticles directly. 

But you could also use vertx-service-proxy to proxify it over the event-bus (careful though, if the service method takes an exception as parameter, I don't think it's allowed to be proxified for now).

The main goal is that you can use it in a standard, local way but also in a remote (RPC) way, transparently. Vert.x allows that.

Once you get your service-proxy interface, you can implement it with a LoggerErrorHandlerService, ELKErrorHandlerService, SentryErrorHandlerService, etc.

I might give a try to implement such a thing :)

Paul Klein

unread,
Dec 9, 2015, 10:19:58 AM12/9/15
to vert.x
It seems like a appropriate design that provides a nice generic interface. I'm going to try this option :)

It could be interesting if we could achieve the same "Bubble-up" effect similar to what we can do in a synchronous app,
i.e. each raised exception is thrown to it's caller method until it reaches a caller that will catch those exceptions and pass
them to the error handler without polluting the code visibility in each sub-method with "try/catch" statements.
It seems quite difficult to achieve that within callbacks but perhaps someone has an intuition on how this can done ?

poiuytrez

unread,
Dec 10, 2015, 7:32:53 AM12/10/15
to vert.x
Hi guys, 

I am in the same team as Paul.

I tried the exception bubble up however I have issues because of the functional interfaces defined in vert.x:

I do this when I define a route:
this.post("/mobile_backend/element/").handler(restController::postElement);

The postElement need to throw an exception. Unfortunately, I get a compile error:
Error:(37, 51) java: incompatible thrown types org.apache.commons.configuration.ConfigurationException in method reference

Based on stackoverflow (http://stackoverflow.com/questions/18198176/java-8-lambda-function-that-throws-exception) it seems that I should modify the io.vertx.core.Hander<E> functional interface 
from:
void handle(E event);
to:
void handle(E event) throws Exception;

I am not sure how I can achieve the bubble up of exceptions...

Any ideas?

poiuytrez

poiuytrez

unread,
Dec 10, 2015, 9:16:46 AM12/10/15
to vert.x
I have another example which is a bit more clear: 

/**
 * Store the key in redis
 */
private void storeRedis() {
    // retrieve the redis client
    RedisClient redisClient = RedisClientSingleton.getRedisClient();

    // store in redis
    redisClient.setnx(key, name, this::handleAsync); // incompatible thrown types java.lang.Throwable in method reference

}

/**
 * Handle the async result
 */
private void handleAsync(AsyncResult<Long> asyncResult) throws Throwable {
    throw asyncResult.cause();
}

I would like the Exception to bubble up but I am getting a compilation error: 
 incompatible thrown types java.lang.Throwable in method reference

I have the same issue everywhere in my code where I have a Handler. 

poiuytrez

Arnaud Estève

unread,
Dec 10, 2015, 9:33:01 AM12/10/15
to vert.x
If it's a checked exception, no matter how hard we try (as end-users), lambdas will always stand in the way. I don't see any solution to this issue except to declare that Handler throws an exception, as you said; but I'm afraid that's a huge change for Vert.x's API ?

Even if it's a runtime exception, such a "global" exception handler would be needed at top-level of Vert.x's API.

Vertx.vertx().registerErrorHandler(...)

Tbch, I'm not even sure how hard / how much work it requires to create such a thing.

Julien Viet

unread,
Dec 10, 2015, 11:22:11 AM12/10/15
to ve...@googlegroups.com, poiuytrez
your example is not clear to me.

you are saying “I would like the exception to bubble up”, but the question is where should it bubble up ?

perhaps you can give a blocking example that would show it so we understand better ?
--
You received this message because you are subscribed to the Google Groups "vert.x" group.
To unsubscribe from this group and stop receiving emails from it, send an email to vertx+un...@googlegroups.com.
Visit this group at http://groups.google.com/group/vertx.
To view this discussion on the web, visit https://groups.google.com/d/msgid/vertx/f42f9081-0fcd-4f75-8ef3-21e6f3125e34%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Clement Escoffier

unread,
Dec 10, 2015, 11:59:43 AM12/10/15
to ve...@googlegroups.com
Hi,

I guess what you would like is a kind of “general” handler (probably on the registered in the ‘vertx’ instance. If the event loop catches an exception, this handler would be invoked. (I may be completely wrong, so let me know).

(If I’m right) I can see several issues. This handler might be called by several event loops, so does not respect the “a handler is always called by the same event loop” rule. It also won’t work for “stuff” happening outside of the event loop. 

Clement


Ronald van Raaphorst

unread,
Dec 11, 2015, 4:02:36 AM12/11/15
to vert.x
Not sure why you want exceptions to bubble up. What do you do at the top with the exception?
At the place where you catch them, handle the exception (by logging the message and the stacktrace, or sending a serialized exception to a failure verticle of some sort) 
and reply the caller that something has gone wrong.

Paul Klein

unread,
Dec 11, 2015, 8:26:53 AM12/11/15
to vert.x
Hi again,

To be a bit more clear, here is what I wanted to say and I will detail it in a more specific use case :

When using cascading async calls, one of the handlers can receive or raise an exception that you want to handle in the current scope,
however sometimes there is also the need to tell the scopes above; when either you want to try something else on failure or that it does not feel natural that the scopes underneath handle the error.
For example, you don't want to pass objects that hold the full context to handlers so they get in charge of telling a potential client that something went wrong, it simply does feel right for me to fit it there.

We found something interesting that might be what we need, i.e. CompletableFuture, here is a practical example of what we are doing :

void postModel(RoutingContext routingContext) {

HttpServerResponse response = routingContext.response();
// create a model object
Model model = new Model(routingContext.request().headers());

if (model.isValid()) {
// set modelid in redis and add a new task to rabbitmq
redisClient.storeModel(model)
.thenCompose(fn -> rabbitMQClient.publishModel(model))
.handle((ok, exception) -> {
if (exception == null) {
response.setStatusCode(200).end();
} else {
logger.error(exception.getMessage(), exception);
response.setStatusCode(500).end(exception.getMessage());
}
return 1;
});
} else {
response.setStatusCode(400).end("Bad request. Invalid Model");
}
}

public CompletableFuture<Void> storeModel(Model model) {
// creates the future
CompletableFuture<Void> future = new CompletableFuture<>();

// store in redis
redisClient.setnx(model.getName(), model.getKey(), asyncResult -> {
if (asyncResult.succeeded()){
// successfully stored in redis, we complete the future
future.complete(null);
} else if (asyncResult.failed()) {
// failure. We complete the future and report the exception
future.completeExceptionally(asyncResult.cause());
}
});

// return the futures
return future;
}



Here we wrap the async calls within a method to benefit from the completableFuture features to handle an exception on the outer scope. It seems more natural since the a vertx handler is not made for chaining calls since we can't throw exceptions in them.

Julien Viet

unread,
Dec 11, 2015, 8:33:07 AM12/11/15
to ve...@googlegroups.com, Paul Klein
now it is more clear.

you can use RxJava that provides Observable for that, Handler<AsyncResult<T>> method have Observable<T> equivalent that notifies the handler result on onNext and the handler failure on onError.

the advantage of RxJava over CompletableFuture is to work with ReadStream too (and we should be able also to transform WriteStream into Observer now that we added WriteStream#end() method)





-- 
Julien Viet
www.julienviet.com

--
You received this message because you are subscribed to the Google Groups "vert.x" group.
To unsubscribe from this group and stop receiving emails from it, send an email to vertx+un...@googlegroups.com.
Visit this group at http://groups.google.com/group/vertx.

poiuytrez

unread,
Dec 16, 2015, 4:00:38 AM12/16/15
to vert.x, pa...@databerries.com
Hi Julien, 

Thank you for your suggestion about RxJava. We finally tested both and we kept CompletableFutures. RxJava seems great but unfortunately it works only with Rxified modules. We can use CompletableFutures everywhere (we are using the Rabbitmq vert.x client which is not rxified).

Have a great day,
poiuytrez 

Julien Viet

unread,
Dec 16, 2015, 4:53:18 AM12/16/15
to ve...@googlegroups.com, poiuytrez, pa...@databerries.com
ok,

we could rxify the rabitmq client though ?


-- 
Julien Viet
www.julienviet.com

poiuytrez

unread,
Dec 16, 2015, 7:20:36 AM12/16/15
to vert.x, guil...@databerries.com, pa...@databerries.com
Sure !

Julien Viet

unread,
Dec 16, 2015, 7:28:32 AM12/16/15
to ve...@googlegroups.com, poiuytrez, pa...@databerries.com, guil...@databerries.com
do you build your own jars of rabbitmq ?



-- 
Julien Viet
www.julienviet.com

poiuytrez

unread,
Dec 16, 2015, 7:53:51 AM12/16/15
to vert.x, guil...@databerries.com, pa...@databerries.com
We are using our own modified version of vertx-rabbitmq-client (https://github.com/kleinplw/vertx-rabbitmq-client) which is already a fork of someone else work. Unfortunately, pulls requests seems to be stuck on the official vert.x rabbitmq client.
We do not build our own jar of rabbitmq. We are using 'com.rabbitmq:amqp-client:3.4.3' from maven central.

Unfortunately, pull requests seems to be stuck

Julien Viet

unread,
Dec 16, 2015, 8:02:06 AM12/16/15
to ve...@googlegroups.com, poiuytrez, pa...@databerries.com, guil...@databerries.com
we are looking for maintainers or rabbitmq client on the stack, perhaps you want to become that person ?

-- 
Julien Viet
www.julienviet.com

poiuytrez

unread,
Dec 16, 2015, 8:12:19 AM12/16/15
to vert.x, guil...@databerries.com, pa...@databerries.com
Yes, I can be the maintainer (or someone in my team). 
Reply all
Reply to author
Forward
0 new messages