Permalink
Browse files

Introduce 'safe mode' for bumping MongoDB database that wraps mongodu…

…mp in fsync&lock. Disabled at default and easy to change in future to native function of mongodb(when it will be implemented)
  • Loading branch information...
1 parent 34992a6 commit 37bb9ff206c2dab73e829ff2efb2e7be75bb6af6 @imanel imanel committed Apr 26, 2011
Showing with 67 additions and 0 deletions.
  1. +2 −0 README.md
  2. +37 −0 lib/backup/database/mongodb.rb
  3. +28 −0 spec/database/mongodb_spec.rb
View
@@ -205,6 +205,8 @@ The __Mail__ notifier. I have not provided the SMTP options to use my Gmail acco
The __Twitter__ notifier. You will require your consumer and oauth credentials, which I have also left out of this example.
+MongoDB backup utility (mongodump) by default does not fsync & lock the database, opening a possibility for inconsistent data dump. This is addressed by setting safe = true which causes mongodump to be wrapped with lock&fsync calls (with a lock takedown after the dump). Please check the Wiki on this subject and remember this is a very fresh feature, needing some more real-world testing. Disabled at default.
+
Check out the Wiki for more information on all the above subjects.
### And that's it!
@@ -29,13 +29,18 @@ class MongoDB < Base
attr_accessor :additional_options
##
+ # 'safe' dump meaning wrapping mongodump with fsync & lock
+ attr_accessor :safe
+
+ ##
# Creates a new instance of the MongoDB database object
def initialize(&block)
load_defaults!
@only_collections ||= Array.new
@additional_options ||= Array.new
@ipv6 ||= false
+ @safe ||= false
instance_eval(&block)
prepare!
@@ -109,11 +114,17 @@ def mongodump
def perform!
log!
+ lock_database if @safe.eql?(true)
if collections_to_dump.is_a?(Array) and not collections_to_dump.empty?
specific_collection_dump!
else
dump!
end
+ unlock_database if @safe.eql?(true)
+ # This should be tested, but I can't find method for catching exception if Mocha...
+ rescue => e
+ unlock_database if @safe.eql?(true)
+ raise e
end
##
@@ -132,6 +143,32 @@ def specific_collection_dump!
end
end
+ ##
+ # Builds the full mongo string based on all attributes
+ def mongo_shell
+ [utility(:mongo), database, credential_options, connectivity_options, ipv6].join(' ')
+ end
+
+ ##
+ # Locks database for dump
+ def lock_database
+ lock_command = <<EOF
+echo 'use admin
+db.runCommand({"fsync" : 1, "lock" : 1})' | #{mongo_shell}
+EOF
+ run(lock_command)
+ end
+
+ ##
+ # Unlocks database for dump
+ def unlock_database
+ unlock_command = <<EOF
+echo 'use admin
+db.$cmd.sys.unlock.findOne()' | #{mongo_shell}
+EOF
+ run(unlock_command)
+ end
+
end
end
end
@@ -129,6 +129,34 @@
db.perform!
end
+ it 'should lock database before dump if safe mode is enabled' do
+ db.safe = true
+ db.expects(:lock_database)
+
+ db.perform!
+ end
+
+ it 'should not lock database before dump if safe mode is disabled' do
+ db.safe = false
+ db.expects(:lock_database).never
+
+ db.perform!
+ end
+
+ it 'should unlock database after dump if safe mode is enabled' do
+ db.safe = true
+ db.expects(:unlock_database)
+
+ db.perform!
+ end
+
+ it 'should not unlock database after dump if safe mode is disabled' do
+ db.safe = false
+ db.expects(:unlock_database).never
+
+ db.perform!
+ end
+
it 'should dump only the provided collections' do
db.only_collections = %w[users admins profiles]
db.expects(:specific_collection_dump!)

0 comments on commit 37bb9ff

Please sign in to comment.