jakubkulhan / bunny

Performant pure-PHP AMQP (RabbitMQ) sync/async (ReactPHP) library
MIT License
706 stars 102 forks source link

Fix swallowing exceptions #112

Open mantiz opened 3 years ago

mantiz commented 3 years ago

First of all, I'm doing my first steps using ReactPHP, trying to use the async client. Therefore my understanding and assumptions may be wrong.

Of course, I did something wrong during my tests (tried to consume a non-existing queue) but didn't get an exception. During my research I found #39 and #36 and I thought I would try to find a solution.

From my understanding (which might be wrong), the problematic line (at least for my problem) is here. The callback passed to done gets executed in the happy path and immediately rejects the deferred value. Now, the rejection causes several callbacks of the promise chain to be executed which will finally throw the exception if it is unhandled. The thrown exception gets caught here which rejects the deferred value with the caught exception but this somehow swallows the exception. I think this happens because there are several calls of ClientMethods::flushWriteBuffer in different places where the possibly returned Promise is just ignored, e.g. here.

According to the docs, the callback passed to LoopInterface::addWriteStream is not allowed to throw an exception, so (re)throwing the exception here is no option.

Instead, I tried to defer resolving/rejecting the deferred value from within the callback to a future tick of the event loop. This way, the callback still does not throw, fulfilling the requirements, and (re)throwing the exception eventually happens within the event loop where it seems to be handled correctly.

I tried to write tests for this behaviour using the example code from #36.

I think it shouldn't cause any issues since resolving/rejecting the deferred value is just delayed a little bit until the next tick of the event loop.