mongo-oplog-backup 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3acf779f860fdb9a7fc941b68f7755c36f215ad6
4
- data.tar.gz: 5a1a19a402c4d337b90c81efc693aeb7d583e454
3
+ metadata.gz: 0683856bbe55049a12b2d1116dab286a2777ac0a
4
+ data.tar.gz: ec2de886b2be3a374fa7b19e5882dc0ba81d2235
5
5
  SHA512:
6
- metadata.gz: 5c01fe3144e8a2c891c363bac256082c17f836d71fa2b097d8f3e737afcaba51d41034b6c7dda7025ac5d0ee282a35908796441bbb9bf41cafc0aa89ac9e35af
7
- data.tar.gz: 94b42dac6b402b476c83ff6f1f131884d266d2b8bc545b389440574fc36f084bcbd7df9152d6149ee1d2f9a2c37e47864822d795c0d619109c93a3de2d1a17e1
6
+ metadata.gz: 9afd48be80dc546dfd14c6486eb4a324f5cb4c42deb7eda7d95c5f51e10827f2afb7f2cd19876d7bde06af8feac632c211e2e21da3c8b40305f7291b359f0cab
7
+ data.tar.gz: fe30271a6df011ebe714408f4311020c6c9d339e8bfb94ed7a27ef941c933437c46de2212a8562afd272675040d18708b59869d062bd635cf33bcc21a3350691
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Experimental** incremental backup system for MongoDB based on the oplog.
4
4
 
5
- Not ready for any important data yet.
5
+ **Not ready for any important data yet. Use at your own risk.**
6
6
 
7
7
  ## Installation
8
8
 
@@ -12,9 +12,24 @@ Not ready for any important data yet.
12
12
 
13
13
  ## Usage
14
14
 
15
+ To backup from localhost to the `mybackup` directory.
16
+
15
17
  mongo-oplog-backup backup --dir mybackup
16
18
 
17
- TODO: Write usage instructions here
19
+ The first run will perform a full backup. Subsequent runs will backup any new entries from the oplog.
20
+ A full backup can be forced with the `--full` option.
21
+
22
+ For connection options, see `mongo-oplog-backup backup --help`.
23
+
24
+ ## To restore
25
+
26
+ mongo-oplog-backup merge --dir mybackup/backup-<timestamp>
27
+
28
+ The above command merges the individual oplog backups into `mybackup/backup-<timestamp>/dump/oplog.bson`.
29
+ This allows you to restore the backup with the `mongorestore` command:
30
+
31
+ mongorestore --drop --oplogReplay backup/backup-<timestamp>/dump
32
+
18
33
 
19
34
  ## Backup structure
20
35
 
@@ -25,9 +40,10 @@ TODO: Write usage instructions here
25
40
  * `oplog-<start>-<end>.bson` - The oplog from the start timestamp until the end timestamp (inclusive).
26
41
 
27
42
  Each time a full backup is performed, a new backup folder is created.
43
+
28
44
  ## Contributing
29
45
 
30
- 1. Fork it ( http://github.com/<my-github-username>/mongo-oplog-backup/fork )
46
+ 1. Fork it ( http://github.com/journeyapps/mongo-oplog-backup/fork )
31
47
  2. Create your feature branch (`git checkout -b my-new-feature`)
32
48
  3. Commit your changes (`git commit -am 'Add some feature'`)
33
49
  4. Push to the branch (`git push origin my-new-feature`)
@@ -12,7 +12,9 @@ opts = Slop.parse(help: true, strict: true) do
12
12
  on :d, :dir, "Directory to store backup files. Defaults to 'backup'.", argument: :required
13
13
  on :full, 'Force full backup'
14
14
  on :oplog, 'Force oplog backup'
15
+ on :'if-not-busy', 'Do nothing when another backup is busy running.'
15
16
 
17
+ on :f, :file, 'Configuration file for common defaults', argument: :required
16
18
  on :ssl, "Connect to a mongod instance over an SSL connection"
17
19
  on :host, "Specifies a resolvable hostname for the mongod that you wish to backup.", default: 'localhost', argument: :required
18
20
  on :port, "Specifies the port that mongod is running on", default: '27017', argument: :required
@@ -25,6 +27,7 @@ opts = Slop.parse(help: true, strict: true) do
25
27
  dir: dir,
26
28
  ssl: opts.ssl?
27
29
  }
30
+ config_opts[:file] = opts[:file]
28
31
  config_opts[:host] = opts[:host]
29
32
  config_opts[:port] = opts[:port]
30
33
  config_opts[:username] = opts[:username]
@@ -38,7 +41,7 @@ opts = Slop.parse(help: true, strict: true) do
38
41
  end
39
42
  config = MongoOplogBackup::Config.new(config_opts)
40
43
  backup = MongoOplogBackup::Backup.new(config)
41
- backup.perform(mode)
44
+ backup.perform(mode, if_not_busy: opts.if_not_busy?)
42
45
  end
43
46
  end
44
47
 
@@ -2,6 +2,9 @@ require 'json'
2
2
  require 'fileutils'
3
3
  require 'mongo_oplog_backup/oplog'
4
4
 
5
+ class LockError < StandardError
6
+ end
7
+
5
8
  module MongoOplogBackup
6
9
  class Backup
7
10
  attr_reader :config
@@ -10,6 +13,16 @@ module MongoOplogBackup
10
13
  @config = config
11
14
  end
12
15
 
16
+ def lock(lockname, &block)
17
+ File.open(lockname, File::RDWR|File::CREAT, 0644) do |file|
18
+ got_lock = file.flock(File::LOCK_EX|File::LOCK_NB)
19
+ if got_lock == false
20
+ raise LockError, "Failed to acquire lock - another backup may be busy"
21
+ end
22
+ yield
23
+ end
24
+ end
25
+
13
26
  def backup_oplog(options={})
14
27
  start_at = options[:start]
15
28
  backup = options[:backup]
@@ -91,42 +104,61 @@ module MongoOplogBackup
91
104
  }
92
105
  end
93
106
 
94
- def perform(mode=:auto)
95
- state_file = config.state_file
96
- state = JSON.parse(File.read(state_file)) rescue nil
97
- state ||= {}
98
- have_position = (state['position'] && state['backup'])
99
-
100
- if mode == :auto
101
- if have_position
102
- mode = :oplog
103
- else
104
- mode = :full
107
+ def perform(mode=:auto, options={})
108
+ if_not_busy = options[:if_not_busy] || false
109
+
110
+ perform_oplog_afterwards = false
111
+
112
+ lock(config.lock_file) do
113
+ state_file = config.state_file
114
+ state = JSON.parse(File.read(state_file)) rescue nil
115
+ state ||= {}
116
+ have_position = (state['position'] && state['backup'])
117
+
118
+ if mode == :auto
119
+ if have_position
120
+ mode = :oplog
121
+ else
122
+ mode = :full
123
+ end
105
124
  end
106
- end
107
125
 
108
- if mode == :oplog
109
- raise "Unknown backup position - cannot perform oplog backup." unless have_position
110
- MongoOplogBackup.log.info "Performing incremental oplog backup"
111
- position = BSON::Timestamp.from_json(state['position'])
112
- result = backup_oplog(start: position, backup: state['backup'])
113
- unless result[:empty]
114
- new_entries = result[:entries] - 1
115
- state['position'] = result[:position]
126
+ if mode == :oplog
127
+ raise "Unknown backup position - cannot perform oplog backup." unless have_position
128
+ MongoOplogBackup.log.info "Performing incremental oplog backup"
129
+ position = BSON::Timestamp.from_json(state['position'])
130
+ result = backup_oplog(start: position, backup: state['backup'])
131
+ unless result[:empty]
132
+ new_entries = result[:entries] - 1
133
+ state['position'] = result[:position]
134
+ File.write(state_file, state.to_json)
135
+ MongoOplogBackup.log.info "Backed up #{new_entries} new entries to #{result[:file]}"
136
+ else
137
+ MongoOplogBackup.log.info "Nothing new to backup"
138
+ end
139
+ elsif mode == :full
140
+ MongoOplogBackup.log.info "Performing full backup"
141
+ result = backup_full
142
+ state = result
116
143
  File.write(state_file, state.to_json)
117
- MongoOplogBackup.log.info "Backed up #{new_entries} new entries to #{result[:file]}"
118
- else
119
- MongoOplogBackup.log.info "Nothing new to backup"
144
+ MongoOplogBackup.log.info "Performed full backup"
145
+
146
+ perform_oplog_afterwards = true
120
147
  end
121
- elsif mode == :full
122
- MongoOplogBackup.log.info "Performing full backup"
123
- result = backup_full
124
- state = result
125
- File.write(state_file, state.to_json)
126
- MongoOplogBackup.log.info "Performed full backup"
148
+ end
127
149
 
150
+ # Has to be outside the lock
151
+ if perform_oplog_afterwards
128
152
  # Oplog backup
129
- perform(:oplog)
153
+ perform(:oplog, options)
154
+ end
155
+
156
+ rescue LockError => e
157
+ if if_not_busy
158
+ MongoOplogBackup.log.info e.message
159
+ MongoOplogBackup.log.info 'Not performing backup'
160
+ else
161
+ raise
130
162
  end
131
163
  end
132
164
 
@@ -3,7 +3,23 @@ module MongoOplogBackup
3
3
  attr_reader :options
4
4
 
5
5
  def initialize(options)
6
- @options = options
6
+ config_file = options.delete(:file)
7
+ # Command line options take precedence
8
+ @options = from_file(config_file).merge(options)
9
+ end
10
+
11
+ def from_file file
12
+ options = {}
13
+ unless file.nil?
14
+ conf = YAML.load_file(file)
15
+ options[:ssl] = conf["ssl"] unless conf["ssl"].nil?
16
+ options[:host] = conf["host"] unless conf["host"].nil?
17
+ options[:port] = conf["port"].to_s unless conf["port"].nil?
18
+ options[:username] = conf["username"] unless conf["username"].nil?
19
+ options[:password] = conf["password"] unless conf["password"].nil?
20
+ end
21
+
22
+ options
7
23
  end
8
24
 
9
25
  def backup_dir
@@ -31,6 +47,10 @@ module MongoOplogBackup
31
47
  File.join(backup_dir, 'backup.json')
32
48
  end
33
49
 
50
+ def lock_file
51
+ File.join(backup_dir, 'backup.lock')
52
+ end
53
+
34
54
  def exec(cmd)
35
55
  MongoOplogBackup.log.debug ">>> #{cmd}"
36
56
  `#{cmd}`
@@ -1,3 +1,3 @@
1
1
  module MongoOplogBackup
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/sample-config.yml ADDED
@@ -0,0 +1,6 @@
1
+ # Any settings specified here are overridden by command line options.
2
+ host: localhost
3
+ port: 27017
4
+ username: backup-user
5
+ password: s3kr1t
6
+ ssl: true
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongo-oplog-backup
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ralf Kistner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-15 00:00:00.000000000 Z
11
+ date: 2014-08-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bson
@@ -120,6 +120,7 @@ files:
120
120
  - lib/mongo_oplog_backup/version.rb
121
121
  - mongo-oplog-backup.gemspec
122
122
  - oplog-last-timestamp.js
123
+ - sample-config.yml
123
124
  - spec/backup_spec.rb
124
125
  - spec/enumerable_spec.rb
125
126
  - spec/fixtures/oplog-1408088734:1-1408088740:1.bson