-
Notifications
You must be signed in to change notification settings - Fork 595
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
ObjectDisposedException
when channel is closed by RabbitMQ due to a channel exception
#1749
Comments
I encountered similar problem, but kind of backwards, because this ObjectDisposedException caused closing connection with such close reason.
The error happened when there was significant load and RabbitMQ responses were several times slower than usual. But I haven't got to the bottom of issue and I don't know the root cause. I'm not sure if it's library itself or usage of the library causing the error, but I can't find any documentation about handling this. Without this timeout some of close requests were running for really long (maybe even infinitely? Some deadlock? There was no channel on server side), so the timeout was added to unblock the thread. To be sure I also checked without running CloseAsync on IChannel at all, but it haven't helped. |
@ramonsmits I still have yet to hit an Here is my test application: It starts a consumer, then repeatedly uses the HTTP API to close the connection. I have left it running a long time in my test environment, and have yet to see Maybe there's something up with my code? If you can take a look, I'd appreciate it. |
I managed to reproduce the problem I described. I called I think the |
For I/O exceptions, it is. And it can and does trigger a connection recovery. For when a connection is closed, the methods should simply be made idempotent. And |
Yep, that's how it's supposed to work 😉 Thank you for the details, @ZajacPiotr98. Since you can reproduce this issue, would you be able to test a pull request once I open it? |
I tested these changes and it's not resolving problem for the case I run. It was simple test:
Also, for this test the
I'm not completely sure what is handled in the |
@ZajacPiotr98 as far as I understand, #1752 focuses on connection objects and you are trying with channels. Their (channels') behavior should generally be identical to the idea above but you and @lukebakken may be focussing on the same problem in two different places. |
@ZajacPiotr98 did you follow the same test procedure you detailed in this comment? #1752 adds a |
Yes, maybe I was not precise if it was @lukebakken I've even tried without Clumsy and had same results. Additionally, to mitigate this behavior, as workaround, I added in the app the isDisposing field, that is setting to true at the start of Disposing channel and never lets run another |
Ah, you're not awaiting the |
Fixes #1749 * Add test project * Add `FirstChanceException` logging. * Add code to repeatedly close connection. * Allow passing hostname to GH-1749 program as the first arg. * Toxiproxy tests fixup. * Standardize on `_disposedValue` name. * Ensure `_disposedValue` is set in `finally` block. * Add lock object for disposal of Channels and Connections. Note: a `SemaphoreSlim` can't be used because it must be disposed as well, and that can't happen cleanly in a `Dispose` method.
@ZajacPiotr98 - please give my PR a test. I've added what amounts to a "Disposing" state to channels and connections, that only allows one thread to do the disposal. |
I will be able to test it next week. But the problem is not with the multiple dispose. I'm awaiting the dispose invocation and it only happens when multiple threads do dispose in the same time. But, like I mentioned I corrected this behavior with workaround on my side. The problem, that is really impacting application is: I think invoking dispose on the exception or when channel is no longer needed should not close entire connection. But it does sometimes, when disposal of semaphores finishes before I only used double dispose without awaiting as an example to reproduce that issue. I couldn't find a way to reproduce it same as on the environment under load tests as there is significant load and thus closing channel is taking more time. I asked about it here:
|
Well, concurrent disposal is a scenario I haven't seen mentioned here, I only thought of the case where a connectio nor channel is disposed N times sequentially. Given that shared use of channels was not supported by this library till 7.0, and that concurrent disposal is not necessarily supported by a lot of libraries for other data services, this may be out of scope for this client. Sharing one-off channels for passive declares between threads seems unnecessary to me but maybe that's an inevitable effect of adopting tasks for the parts that interact with RabbitMQ :( |
@ZajacPiotr98 - I'm looking to see if there is a straightforward way to address what you have pointed out with the Your comment here gives me the best description of how to reproduce the error. When you do a passive declaration of a queue that doesn't exist, RabbitMQ closes the channel by sending This gives me a simple test case to try. |
Can you please save all stack traces to text files, and attach the files to this issue?
Which semaphore? If I had a more complete set of information from you, I could probably figure this out 😄 |
The code you provide here should never result in Before I spend any more time investigating, I need code with instructions for reproducing the errors you describe with the |
I managed to reproduce this issue with simulating some delay between Here's the code, that reproduces issue 100% times:
I posted stack trace in my first post here (it's close reason of a connection):
|
Fixes #1749 * Add test project * Add `FirstChanceException` logging. * Add code to repeatedly close connection. * Allow passing hostname to GH-1749 program as the first arg. * Toxiproxy tests fixup. * Standardize on `_disposedValue` name. * Ensure `_disposedValue` is set in `finally` block. * Add lock object for disposal of Channels and Connections. Note: a `SemaphoreSlim` can't be used because it must be disposed as well, and that can't happen cleanly in a `Dispose` method.
Thank you very much. I can reproduce the issue every time and am looking into it. |
Fixes #1749 * Add test project for GH #1749. * Add `FirstChanceException` logging in #1749 test project. * Add code to repeatedly close connection. * Allow passing hostname to GH-1749 program as the first arg. * Toxiproxy tests fixup. * Ensure `_disposed` is set in `finally` block. * Add lock object for disposal of Channels and Connections. Note: a `SemaphoreSlim` can't be used because it must be disposed as well, and that can't happen cleanly in a `Dispose` method. * Add basic test to see what dispose after a channel exception does. * Modify `CreateChannel` app to try and trigger GH1751 * Add TCS for server-originated channel closure. This is the "real" fix for the issue.
Fixes #1749 * Add test project for GH #1749. * Add `FirstChanceException` logging in #1749 test project. * Add code to repeatedly close connection. * Allow passing hostname to GH-1749 program as the first arg. * Toxiproxy tests fixup. * Ensure `_disposed` is set in `finally` block. * Add lock object for disposal of Channels and Connections. Note: a `SemaphoreSlim` can't be used because it must be disposed as well, and that can't happen cleanly in a `Dispose` method. * Add basic test to see what dispose after a channel exception does. * Modify `CreateChannel` app to try and trigger GH1751 * Add TCS for server-originated channel closure. This is the "real" fix for the issue.
@ZajacPiotr98 if you could try the latest code in this PR, I would appreciate it: Basically, this is the problem:
The fix is to ensure that ...and, if this completion source is not-null during disposal, the code waits up to 5 seconds for it to complete here: |
ObjectDisposedException
when channel is closed by the AMQP broker.
Fixes #1749 * Ensure `Dispose` and `DisposeAsync` are idempotent and thread-safe. * Use TaskCompletionSource when `HandleChannelCloseAsync` runs to allow dispose methods to wait.
ObjectDisposedException
when channel is closed by the AMQP broker.ObjectDisposedException
when channel is closed by RabbitMQ
ObjectDisposedException
when channel is closed by RabbitMQObjectDisposedException
when channel is closed by RabbitMQ due to a channel exception
I tested this changes and I haven't seen problem anymore. Seems good to me. When it's possible to get a new version of the library with these changes? |
Hopefully by the end of this month. |
Fixes #1749 * Ensure `Dispose` and `DisposeAsync` are idempotent and thread-safe. * Use TaskCompletionSource when `HandleChannelCloseAsync` runs to allow dispose methods to wait. * Use `Interlocked` for thread safety. * I like `_isDisposing` better. So sue me! * Move the `Interlocked.Exchange` code to a getter, for readability. * Minor nullable change. Co-authored-by: Daniel Marbach <daniel.marbach@openplace.net>
Fixes #1749 * Add test project for GH #1749. * Add `FirstChanceException` logging in #1749 test project. * Add code to repeatedly close connection. * Allow passing hostname to GH-1749 program as the first arg. * Toxiproxy tests fixup. * Ensure `_disposed` is set in `finally` block. * Add lock object for disposal of Channels and Connections. Note: a `SemaphoreSlim` can't be used because it must be disposed as well, and that can't happen cleanly in a `Dispose` method. * Add basic test to see what dispose after a channel exception does. * Modify `CreateChannel` app to try and trigger GH1751 * Add TCS for server-originated channel closure. This is the "real" fix for the issue.
Fixes #1749 * Add test project for GH #1749. * Add `FirstChanceException` logging in #1749 test project. * Add code to repeatedly close connection. * Allow passing hostname to GH-1749 program as the first arg. * Toxiproxy tests fixup. * Ensure `_disposed` is set in `finally` block. * Add lock object for disposal of Channels and Connections. Note: a `SemaphoreSlim` can't be used because it must be disposed as well, and that can't happen cleanly in a `Dispose` method. * Add basic test to see what dispose after a channel exception does. * Modify `CreateChannel` app to try and trigger GH1751 * Add TCS for server-originated channel closure. This is the "real" fix for the issue.
See @ZajacPiotr98's comment for a description of this bug:
#1749 (comment)
The issue reported by @ramonsmits is NOT fixed by #1752, and has been copied to a new issue, #1760
The text was updated successfully, but these errors were encountered: