-
Notifications
You must be signed in to change notification settings - Fork 65
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
Connection handling improvements. #66
Connection handling improvements. #66
Conversation
…re retry logic. Automatic fork and InheritedError handling: Lazy reconnects reduce zookeeper and redis server load. Reduces/prevents kernel mutex deadlocks in forking env. Trace connection debug logging.
We make heavy use of process-forking environments (unicorn and resque), along with multiple parallel redis server connections (sharded clusters). In production usage we were running into kernel mutex deadlocks (we're on ruby 1.9). Research (1) indicates that there's some subtle pthread mutex handling bugs in ruby, especially during process termination. This primarily was caused by reconnect() calls, which trigger zookeeper thread mutex deadlocking. ZK and redis libs can detect InheritedErrors during forking, so there's really no need to preemptively do this via reconnect() anymore. As a nice side effect of this new 'lazy' connection handling, we reduce zookeeper and redis server load, since we're cycling client connections and lookups far far less. We've also done rather extensive zookeeper cluster failure load testing, leading to robustified zookeeper connectivity failure handling. ZK failures are still pretty much stop-the-world events for redis command processing, but using cached redis cluster state is on the todo list. (1) |
Will look into test failures.... |
#Resque automatically calls this method on job fork. | ||
#We now auto-detect underlying zk & redis client InheritedError's and reconnect automatically as needed. | ||
|
||
#purge_clients |
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.
Can we remove all code that's commented out like this? We can always look at the previous code via git history.
Thanks for putting this together! These fixes look good. Just a minor code about leaving in commented-out code. Also, any thoughts on the build failures? |
@arohter - I just made you a committer for the project! Thanks for helping maintain redis_failover. Feel free to merge this once you think it's ready. |
Fixed and added some tests. Have some in-progress tests for zk error handling, but will do that in separate PR. The lingering jruby test failure should disappear in the read-only slave healthchecking fix PR coming next. |
Connection handling improvements.
#NOTE: We've found that using the 'recommended' zk fork-hook would trigger | ||
#kernel mutex deadlocks in forking env (unicorn & resque) [ruby 1.9] | ||
#https://github.com/zk-ruby/zk/wiki/Forking & https://github.com/zk-ruby/zk/blob/master/RELEASES.markdown#v150 | ||
#ZK.install_fork_hook |
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.
What version of zk and zookeeper were you using?
In the past couple months I made a lot of fixes that improved some of these situations.
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.
...and not calling the fork hooks will likely lead to segfaults (which is why this stuff is here in the first place).
The issue is that the apache zookeeper C library does not provide a way to say, "cleanup this zookeeper connection without issuing a close", so the child can't clean up the parents connection without closing the parents connection.
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.
this is insanely dangerous and will break worse with this disabled. Safe operation requires the use of the fork hooks to ensure that the mutexes are owned by the thread calling fork() and the child is able to clean up the connection properly.
This is likely just masking the error or causing a different kind of broken behavior. The latest release of zk-1.9.3 & zookeeper-1.4.8 have fixes for the event delivery system that we believe caused the deadlocks (@eric has been running them with success in production for several weeks)
You must install the fork hooks for correct operation in forking environments.
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.
I've addressed this in my longer comment below, but to be clear, redis_failover has never installed ZK.install_fork_hook. My PR simply adds a comment about our experiences with it.
P.S. I'm happy to remove the comment, especially now that we have new zk/zookeeper releases.
We're running the latest released versions of zk (1.9.2) and zookeeper (1.4.7). You made a lot of fixes to redis_failover, or to zk/zookeeper code in the past few months? I'd be very interested in seeing these :) Interestingly enough, we found that using the "recommended" ZK.install_fork_hook actually made things far worse - we were getting a ton of deadlocks - which is why we stopped using it (but I added the NOTE text for future ref). After some research, as you mentioned, the issues stem from the underlying zookeeper threading model, combined with ruby's (at least 1.9.3, have yet to try 2.0 or 2.1) buggy kernel mutex handling. In our case, due to our heavy use of resque's process-forking jobs, the preemptive reconnect() calls (which trigger zk.reopen) combined with process termination triggers the bug. In the end, removing the preemptive reopen, combined with lazy retry handling significantly reduced mutex contention, virtually eliminating deadlocks in our production env. |
The changes were made to zk-ruby/zk and zk-ruby/zookeeper. Do you have a ruby stack trace of where the processes were hung? I just noticed it looks like there are a couple fixes that haven't made it into official releases yet, so I'll work with @slyphon to make that happen. |
Some traces [pthread_mutex_destroy: Device or resource busy EBUSY]: Here's a bit with fork hook enabled: strace of the hung processes show them spinning on sched_yield (sometimes using 100% cpu). Like zk-ruby/zk#50 |
Are you saying those first ones with the Without the stack trace of all threads in the ones with fork hooks enabled, it's hard to know exactly what else was going on to cause the deadlocks. I am working with @slyphon to create releases for the latest changes to zk and zookeeper. I'd be interested to see if they fix the issues you see with using the fork hook. |
Okay. zookeeper 1.4.8 and zk 1.9.3 have been released. Let me know if those fix your problem when you have the fork hooks enabled. |
The pthread_mutex_destroy are without fork hook. I dont think I have any gist logs of the traces with the fork hook enabled. |
I'm definitely concerned with master not having the fork hooks enabled. They're there to prevent those pthread errors you saw. |
#NOTE: Explicit/manual reconnects are no longer needed or desired, and | ||
#triggered kernel mutex deadlocks in forking env (unicorn & resque) [ruby 1.9] | ||
#Resque automatically calls this method on job fork. | ||
#We now auto-detect underlying zk & redis client InheritedError's and reconnect automatically as needed. |
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.
no no no, do not do this please, for the love of god. we've fixed the deadlocks. don't do a buggy workaround.
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.
This PR does not change behavior wrt use (or not) of ZK.install_fork_hook
I would suggest this pull request be reverted in its entirety and revisited. There's a bunch of stuff here that is troubling. |
Revert "Merge pull request #66 from fanhattan/connection-handling-improv...
Wow. Go offline for a few hours and things turn crazy... ;-) Let's be perfectly clear here folks: ZK.install_fork_hook was and has never been enabled in redis_failover Walking through the logic and production experience backing this PR: Ruby apparently has a bug related to kernel mutex handling, particularly during process shutdown. redis_failover already detected zookeeper InheritedConnectionErrors, automatically (and correctly) calling zk.reopen as needed. Reverting this PR is unnecessary, and without these code changes redis_failover was unusable for us in the real world. In our heavy production workload, moving to this "only as needed" reconnect/reopen handling virtually eliminated the large amount of pthread_mutex_destroy errors we were seeing. It also noticeably reduced the connection load on our zookeeper and redis clusters. Now, on to the root topic of the objections: ZK.install_fork_hook We were initially very excited about discovering the existence of ZK.install_fork_hook, as it seemed to offer a solution to the very problem we were hitting. Unfortunately, after enabling it, we started running into deadlocks. [The good news is it appeared to reduce/eliminate the pthread_mutex_destroy errors!] Deadlocks and 100% cpu spins are far worse than ungraceful process exits. ZK.install_fork_hook + zk 1.9.2 + zookeeper 1.4.7 was a completely unusable combo for us. Good news is apparently we're not alone in running into ZK.install_fork_hook problems. And @eric has kindly worked on fixing the underlying issue! I'm looking forward to trying it again with zookeeper 1.4.8 and zk 1.9.3. |
@arohter, could you please do the following:
This will give us more context and help us re-review the changes that were made. Sorry for the confusion. |
All the changes in this PR are still valid, as they have nothing to do with the use of ZK.install_fork_hook. I will test and issue a new PR for the use of |
For the record, the use of the zk fork hook ( https://github.com/fanhattan/redis_failover/compare/conn_handling_v2_nofork...conn_handling_v2_forkhook ) still causes almost immediate deadlocks in our env, despite running latest versions of zk/zookeeper. |
That is definitely a strange one. I wonder what is causing this to happen Would you be able to set the environment variable It appears that something about the shutdown process isn't working as it The reason why it's so important to be running the fork hooks is because We've put a lot of effort into the zookeeper forking behavior to accomodate On Mon, Mar 17, 2014 at 1:00 AM, arohter [email protected] wrote:
|
+1 for trying to debug the ZK shutdown issue!
|
Yes, I will try and grab some debug logs of this in action. |
More robust zk client error handling: support progressively more severe retry logic.
Automatic fork and InheritedError handling:
Lazy reconnects reduce zookeeper and redis server load.
Reduces/prevents kernel mutex deadlocks in forking env.
Trace connection debug logging.