class MongoOplogBackup::Restore
Attributes
config[R]
Public Class Methods
new(config)
click to toggle source
# File lib/mongo_oplog_backup/restore.rb, line 17 def initialize(config) @config = config end
Public Instance Methods
backup_folder()
click to toggle source
# File lib/mongo_oplog_backup/restore.rb, line 9 def backup_folder File.join(config.backup_dir) end
oplog_restore_folder()
click to toggle source
# File lib/mongo_oplog_backup/restore.rb, line 13 def oplog_restore_folder File.join(backup_folder, 'tmp-restore') end
perform(mode, options={})
click to toggle source
# File lib/mongo_oplog_backup/restore.rb, line 96 def perform(mode, options={}) if options[:oplogLimit] raise ArgumentError, "oplogLimit is not a timestamp: eg. <seconds>[:ordinal]" unless options[:oplogLimit] =~ /\A\d+(?::\d+)?\z/ end if mode == :oplog restore_oplogs(backup_folder, options) elsif mode == :full restore_dump(backup_folder, options) restore_oplogs(backup_folder, options) end end
restore_dump(dir, options={})
click to toggle source
# File lib/mongo_oplog_backup/restore.rb, line 81 def restore_dump(dir, options={}) restore_args = ['--stopOnError'] restore_args << '--noIndexRestore' if !!config.options[:noIndexRestore] restore_args << '--gzip' if config.use_compression? restore_args << File.join(dir, 'dump') MongoOplogBackup.log.debug "Starting full restore..." status = config.mongorestore(restore_args).status if status != 0 MongoOplogBackup.log.error("Mongorestore failed.") raise 'Full restore failed.' end MongoOplogBackup.log.debug "Full restore complete." end
restore_oplogs(dir, options={})
click to toggle source
Given a directory of oplog dumps generated by the backup feature, iteratively mongorestore them. Mongorestore <3.4 expects a file named oplog.bson the directory specified Mongorestore 3.4 adds support for –oplogFile parameter, which simplifies things.
# File lib/mongo_oplog_backup/restore.rb, line 25 def restore_oplogs(dir, options={}) default_restore_args = ['--stopOnError', '--oplogReplay'] default_restore_args << '--noIndexRestore' if !!config.options[:noIndexRestore] if options[:oplogLimit] default_restore_args += ['--oplogLimit', options[:oplogLimit]] oplog_limit = BSON::Timestamp.from_string(options[:oplogLimit]) end oplog_start_at = BSON::Timestamp.from_string(config.options[:oplogStartAt]) if config.options[:oplogStartAt] source_files = Oplog.find_oplogs(dir) validate_continuity!(source_files) MongoOplogBackup.log.debug "Starting oplog restore..." source_files.each do |filename| # TODO: mongorestore 3.4 supports --oplogFile unless oplog_start_at.nil? timestamps = Oplog.timestamps_from_filename(filename) if timestamps[:last] <= oplog_start_at MongoOplogBackup.log.debug "Skipping batch: #{filename}. Last op in batch (ts: #{timestamps[:last]}) is before oplogStartAt: #{oplog_start_at}" next end end unless oplog_limit.nil? timestamps = Oplog.timestamps_from_filename(filename) if timestamps[:first] > oplog_limit MongoOplogBackup.log.debug "Skipping batch: #{filename}. First op in batch (ts: #{timestamps[:first]}) is after oplogLimit: #{oplog_limit}" next end end temp_file_path = create_temp_oplog_dir(dir, filename) oplog_dir_path = File.dirname(temp_file_path) restore_args = default_restore_args.dup restore_args << '--gzip' if Oplog.gzip_fingerprint(filename) restore_args << oplog_dir_path status = config.mongorestore(restore_args).status if status != 0 MongoOplogBackup.log.error("Mongorestore failed during oplog restore. Aborting. Exit code: #{status}") raise 'Oplog restore failed.' end begin File.unlink(temp_file_path) Dir.rmdir(oplog_dir_path) rescue SystemCallError => e MongoOplogBackup.log.error("Clean-up error for '#{temp_file_path}. #{e.message}") raise end end MongoOplogBackup.log.debug "Oplog restore complete." end
Private Instance Methods
create_temp_oplog_dir(dir, filename)
click to toggle source
# File lib/mongo_oplog_backup/restore.rb, line 126 def create_temp_oplog_dir dir, filename temp_dir = File.join(dir, 'tmp-restore', File.basename(filename)) temp_file_path = File.join(temp_dir, 'oplog.bson') FileUtils.mkdir_p(temp_dir) File.link(filename, temp_file_path) return temp_file_path rescue Errno::EEXIST => e # Probably, 'new_name' already exists when creating link. MongoOplogBackup.log.warn("Temporary oplog.bson link already exists: #{e.message}. Assuming valid.") return temp_file_path rescue SystemCallError => e MongoOplogBackup.log.error("Setup error for oplog '#{filename}' in '#{temp_file_path}") raise end
validate_continuity!(source_files)
click to toggle source
Check for gaps in timestamps based only on the filename, trusting that the oplog set is complete (ie. the filename timestamps match the actual data)
# File lib/mongo_oplog_backup/restore.rb, line 112 def validate_continuity!(source_files) expected_first = nil MongoOplogBackup.log.debug "Checking timestamps..." source_files.each do |filename| timestamps = Oplog.timestamps_from_filename(filename) raise("Filename without timestamps found in restore set: #{filename}") if timestamps.nil? if expected_first && expected_first != timestamps[:first] raise "Missing oplog dump? Expected a filename with first timestamp '#{expected_first}', but got '#{timestamps[:first]}' from filename '#{filename}'." end expected_first = timestamps[:last] end true end