Skip to content

Instantly share code, notes, and snippets.

@bhbryant
Created September 21, 2011 01:19
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save bhbryant/1230938 to your computer and use it in GitHub Desktop.
Save bhbryant/1230938 to your computer and use it in GitHub Desktop.
auto-failover ReplicaSet config for MongoMapper, by way of ruby driver
replica_sets = mm_env['replica_sets'] # node1:port&node2:port
rs_list = replica_sets.split("&").map {|rs| host,port = rs.split(":"); [host,port.to_i] }
MongoMapper.connection = Mongo::RetryReplicaSetConnection.new(*(rs_list << {:read_secondary => true, :auto_refresh => true }))
MongoMapper.database = mm_env['database']
MongoMapper.database.authenticate(mm_env['username'], mm_env['password'])
# encoding: utf-8
## pulled from mongoid
# Provides behaviour for retrying commands on connection failure.
module Mongo::Retry
# Retries command on connection failures.
#
# This is useful when using replica sets. When a primary server wents
# down and a command is issued, the driver will raise a
# Mongo::ConnectionFailure. We wait a little bit, because nodes are
# electing themselves, and then retry the given command.
#
# By setting Mongoid.max_retries_on_connection_failure to a value of 0,
# no attempt will be made, immediately raising connection failure.
# Otherwise it will attempt to make the specified number of retries
# and then raising the exception to clients.
#
# @example Retry the command.
# retry_on_connection_failure do
# collection.send(name, *args)
# end
#
# @since 2.0.0
def retry_on_connection_failure
retries = 0
begin
yield
rescue Mongo::ConnectionFailure => ex
retries += 1
raise ex if retries > 30 # Config.max_retries_on_connection_failure
Kernel.sleep(0.5) ## Todo.. modify for event machine
log_retry retries
retry
end
end
private
def log_retry(retry_number)
puts "A Mongo::ConnectionFailure was raised. Retry attempt ##{retry_number}."
end
end
class Mongo::RetryReplicaSetConnection < Mongo::ReplSetConnection
## wraps basic send message calls in catch blocks to auto retry connection
## per: http://permalink.gmane.org/gmane.comp.db.mongodb.user/25261
# After looking at the Ruby MongoDB driver code it appears that we could
# handle this transparently in the following methods:
# Connection::send_message
# Connection::send_message_with_safe_check
# Connection::receive_message
# I put together a new Connection class that is derived from
# ReplSetConnection to implement this behavior.
## This is ripped from Mongoid block
include Mongo::Retry
def send_message(operation, message, opts={})
retry_on_connection_failure do
super(operation, message, opts)
end
end
def send_message_with_safe_check(operation, message, db_name, log_message=nil, last_error_params=false)
retry_on_connection_failure do
super(operation, message, db_name, log_message, last_error_params)
end
end
def receive_message(operation, message, log_message=nil, socket=nil, command=false)
retry_on_connection_failure do
super(operation, message, log_message, socket, command)
end
end
end
@xtat
Copy link

xtat commented Oct 8, 2011

This works really well! Why is this not in mongomapper?

@bhbryant
Copy link
Author

bhbryant commented Oct 8, 2011 via email

@xtat
Copy link

xtat commented Oct 8, 2011

Makes sense! I think my main point is-- this must exist somewhere in the toolchain. Currently your gist ranks really high in Google because it is the only thing that works right now :)

@thomasdavis
Copy link

@bhbryant aha even I got here.

Trying to make it work with latest mongo gem, rs_list is a different format now

@thomasdavis
Copy link

If you could give me a hand modifying it to work that would be fantastic.

Getting a too many arguments error on the initialize

@jakimowicz
Copy link

Thank you for this brilliant gist !

@thomasdavis, you should take a look at my fork I had to make.

Basically, it does not require you to change your connection adapter/class and fix the too many arguments error.

@aboyon
Copy link

aboyon commented Oct 24, 2014

Good morning guys, I saw this and seems to work really well.
So my question is... is this gist already implemented into a gem or something like that?
If not, I'll apply as is here :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment