-
Notifications
You must be signed in to change notification settings - Fork 592
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
BasicCancel hangs when using new async consumer #341
Comments
@karoberts how can we reproduce? |
There is nothing that refers to consumer dispatch machinery in this call stack. Please take this to the mailing list and provide server logs and a way to reproduce. |
Actually I can think of a way of |
I'm working on reducing my code into a reproducible sample. Will report back soon. |
Thank you! |
ok, I think I've found it. I built up a sample using just rabbitmq client code, and of course it worked. But then after much experimentation, I discovered the key. The issue occurs when the BasicCancel is issued before the async callback has entered a continuation. For example, this works
but this will cause BasicCancel() to hang and throw TimeoutException after 20 seconds
If you still need the code, I can provide, but this seems pretty clear that BasicCancel() must be waiting for something that isn't unlocked until the async callback gives back the thread. A workaround is an |
Thanks, that saves us maintainers a lot of time!
As a workaround you probably can use |
@karoberts can you give a few indicators where the code is hosted? Do you have an environment which is context aware like WPF, WinForms or anything the like? |
@michaelklishin What we are seeing here is a consequence of the synchronous blocking nature of the model in combination with async. I think I highlighted this previously in conversations. Until the model is significantly refactored to be truly asynchronous clients using this library might always be subjected to deadlocks. By default the thread that enters the async code path executes all the synchronous parts of the callstack until the first await statement is reached. In our case this is the worker dispatcher thread. Since BasicCancel calls into Monitor.Wait this captures the dispatcher thread and we are locked for the wait time of the timeout of the blocking cell implementation since the value cannot be set (holding the lock by the same thread). Since no context capturing is enabled here many async implementations should be fine because they will probably have awaited something first before they call methods on the model. All consumer that do not access the model are also fine. So the solution here is to do a Yield at the beginning of the event handler or for safety precaution reasons it would be possible to modify to Work class to do it uniformly
I guess any other work like moving the model to a true asynchronous one is probably out of question at this stage since the work on the new client should start soonish |
Thanks for the analysis @danielmarbach. So the question here is whether we should always Yield in |
@danielmarbach For my particular use case, there won't be any context. In my code, I'm doing this just in case to avoid any context issues, which I got from here
|
Any movement on this one? I am running into this, as well as some other behavior that I think is caused by the same underlying problem.
calling basic cancel on the consumer hangs for almost exactly 10seconds before returning.
hangs for 10s before exiting the using block. This same behavior happens if I call channel.Close() and channel.Dispose() instead of the using block. If no AsyncEventingBasicConsumer has been registered, or I swap to a regular EventingBasicConsumer, the issue disappears.
I don't fully grasp this comment.. but if there's a workaround I can use on my side please let me know! |
I'm still curious about the status of this issue 😃 |
@seabrookmx I am scheduling this bug to be investigated and hopefully fixed for 6.0. Could you please provide a full code sample I can run to reproduce? |
@lukebakken maybe this helps you? https://github.com/NickLydon/RabbitMQAsyncDeadlock I think this is the same issue... |
Another reason why the client should be async all the way and not a weird mixture of both. Unfortunately the async consumer can have this behavior where yielding is needed depending on what API is called. Especially the one's that use the blocking cell are subjected to deadlocks. |
@michaelklishin can you give a simple example for suggested workaround, please. |
@a-dengin set the |
@michaelklishin thank you for reply. But i am i little bit confused. In fact I can not find any overloaded method |
I have consumers which check if a certain condition has occurred and cancel. They was I do that is using IModel.BasicCancel(consumerTag). I've been using this successfully for quite some time.
Now using the new async consumer, the call to BasicCancel hangs.
It gets stuck here
I know for certain this only affects the async consumer because the exact same code with a sync consumer takes about 3ms inside BasicCancel.
The text was updated successfully, but these errors were encountered: