mongo-oplog-backup 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +19 -3
- data/bin/mongo-oplog-backup +4 -1
- data/lib/mongo_oplog_backup/backup.rb +62 -30
- data/lib/mongo_oplog_backup/config.rb +21 -1
- data/lib/mongo_oplog_backup/version.rb +1 -1
- data/sample-config.yml +6 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0683856bbe55049a12b2d1116dab286a2777ac0a
|
4
|
+
data.tar.gz: ec2de886b2be3a374fa7b19e5882dc0ba81d2235
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
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`)
|
data/bin/mongo-oplog-backup
CHANGED
@@ -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
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
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
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
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 "
|
118
|
-
|
119
|
-
|
144
|
+
MongoOplogBackup.log.info "Performed full backup"
|
145
|
+
|
146
|
+
perform_oplog_afterwards = true
|
120
147
|
end
|
121
|
-
|
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
|
-
|
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}`
|
data/sample-config.yml
ADDED
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.
|
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-
|
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
|