-
Notifications
You must be signed in to change notification settings - Fork 304
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
send_frameset can now raise ConnectionClosedErrors #562
send_frameset can now raise ConnectionClosedErrors #562
Conversation
@michaelklishin Is this the approach you had in mind? If so I'll add some test cases. |
e045b7f
to
cf63dfe
Compare
Thank you. That's one half of what I had in mind. We also need to make missed heartbeats and I/O errors to first mark connection as closed (or maybe introduce a new state, Test cases would be great, we can adopt Toxiproxy for these new tests. |
I think it a new state for def handle_network_failure(exception)
raise NetworkErrorWrapper.new(exception) unless @threaded
@status_mutex.synchronize { @status = :disconnected }
if !recovering_from_network_failure?
begin
@recovering_from_network_failure = true Is it intended to have any thread be able to start the recovery? It would likely be the the reader_loop thread, but it seems like it could be heartbeat or the session thread. This is potentially bad if it happens when I make any call to rabbitmq. Eg |
Recovery happens in the I/O loop thread and there's only one per connection. |
Extra synchronisation can be added but in 6 or so years of the current architecture of Bunny concurrent recovery attempts hasn't been an issue. |
Couldn't this be a possible call stack?
Maybe I'm missing something. I can try to get a repro of that with toxiproxy another time. |
Connections are generally not shared in Ruby apps (that hardly have any concurrency that developers intentionally introduce). I don't mind related changes in a single PR. |
6821517
to
d2691eb
Compare
it "raises a ConnectionClosedError" do | ||
ch = @connection.create_channel | ||
# Assume the IO thread closed the connection due to exception | ||
@connection.instance_variable_set("@status", :closed) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the test case fine like this? or would you prefer a higher level toxiproxy test?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can simply close the connection to the same effect. A Toxiproxy test would be more useful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you close the connection with session#close you would get the other exception Bunny::ChannelAlreadyClosed: cannot use a closed channel! Channel id: 1
because channel knows it's closed. I'll look into a toxiproxy test case. Remove this one?
You mentioned "We also need to make missed heartbeats and I/O errors to first mark connection as closed". Both heartbeat thread and reader_loop will call handle_network_failure from the Transport class. With automatic_recovery the transport will be marked as closed and the session status will be |
@sbonebrake your understanding of the issue is mostly correct. I do not want to focus on the status of the transport since the transport is both an implementation detail and a disposable (as in, you have to instantiate a new one) dependency. There's a difference between As far as Of course you can argue a case for a different set of states or just |
So the behavior we are looking for (which partially might already be the case):
|
I found something in commit logs (0e77347) that makes me think that Bunny has been pretty consistent about |
Good to know, makes sense to me. Is |
@sbonebrake no, just an unintentional inconsistency. Connection closure is a request-response kind of operation. Other clients use |
757492b
to
5df9d43
Compare
Ok, you mentioned three scenarios here:
This is covered by channel_close_spec.rb
Covered by connection recovery tests
Is there anything else I should cover in this PR? Happy to fix any style/naming preferences you have as well. |
5df9d43
to
ba40511
Compare
ba40511
to
492c335
Compare
@sbonebrake please move the Toxiproxy tests under I'd like to have a way to have them skipped when Toxiproxy isn't available (e.g. when you are not running the dependencies in Docker and don't have it running locally). We also make some tests that require setup steps conditional (e.g. TLS). Toxiproxy is a good candidate for that. As far as I can tell in the Ruby client |
I tried running Toxiproxy locally via Homebrew with all defaults and it logs the following:
even though a RabbitMQ node is running doesn't log anything. This likely means that RabbitMQ closes TCP connection that sends no data after a handshake period but also doesn't log it since, well, it sent no data (this is to avoid log spam from TCP load balancer health checks). I haven't done a traffic capture to further confirm this. Are there any manual setup steps that I'm missing? |
@sbonebrake never mind, I'm happy to make those largely cosmetic changes. All tests but TLS pass with Docker Compose but that is due to either stale certificates or recent tls-gen changes that break on MacOS with LibreSSL. |
@sbonebrake thank you! Let me know how soon you need a preview release up on rubygems.org. |
Thanks @michaelklishin Let me if you're still having trouble with toxiproxy. I didn't really try to run it without docker-compose. |
Fixes #561