solid_backup 0.2.2 → 0.4.0
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/CHANGELOG.md +13 -0
- data/README.md +32 -9
- data/lib/solid_backup/backup/api.rb +3 -3
- data/lib/solid_backup/backup/none.rb +4 -4
- data/lib/solid_backup/backup.rb +35 -13
- data/lib/solid_backup/compressor.rb +14 -0
- data/lib/solid_backup/expiration.rb +25 -0
- data/lib/solid_backup/generators/install_generator.rb +3 -1
- data/lib/solid_backup/version.rb +1 -1
- data/lib/solid_backup.rb +2 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: dd198b0749b8c0ec8bbbf9371c95c95e3bf7967b3c76353b6bc8af310acbd160
|
|
4
|
+
data.tar.gz: 6c22706156961bcf44051d1023f6e5b2c50e4e02607a8b87f0dc6b774053963c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2fdd803df8a709d66657b09bec4b49f27a94cc593a95ad27bb82daa1dd812391fb0e71a755124cdb3833c24fd10086b135b66f26665440c4a3d258c39110973d
|
|
7
|
+
data.tar.gz: 40649b13dfeacc3fc4db7c159ae017b0d2034e7075883ce4ad4bb0f1dc48b51ca4db9b62459898134477dede39c653590f095565aa587f4f3807cd52926aad03
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Solid Backup
|
|
2
2
|
|
|
3
|
+
## 0.4.0
|
|
4
|
+
|
|
5
|
+
- Add backup compression support via the `compressor:` option; currently
|
|
6
|
+
`SolidBackup::Compressor::Gzip` is the only supported value.
|
|
7
|
+
|
|
8
|
+
## 0.3.0
|
|
9
|
+
|
|
10
|
+
- `destination` is the directory where backup files will be placed, not a
|
|
11
|
+
%-encoded template.
|
|
12
|
+
- New `expiration:` option can be set to determine backup expiration policy;
|
|
13
|
+
currently only `SolidBackup::Expiration::Age.new(maximum: ...)` and
|
|
14
|
+
`SolidBackup::Expiration::None.new` are supported.
|
|
15
|
+
|
|
3
16
|
## 0.2.2
|
|
4
17
|
|
|
5
18
|
- Don't call `SolidBackup` too early to avoid errors caused by Rails not being
|
data/README.md
CHANGED
|
@@ -28,11 +28,14 @@ will lack backups due to omission. There are three database backup modes:
|
|
|
28
28
|
|
|
29
29
|
Modes other than `SolidBackup::Backup::None` take the following parameters:
|
|
30
30
|
|
|
31
|
-
- `destination` -
|
|
32
|
-
|
|
33
|
-
and be writeable.
|
|
31
|
+
- `destination` - the directory where backup files will be held; it must already
|
|
32
|
+
exist and be writeable.
|
|
34
33
|
- `interval_in_minutes` - the number of minutes to the beginning of the next
|
|
35
34
|
backup after the previous one finished.
|
|
35
|
+
- `expiration` - backup expiration policy; expired backups are automatically
|
|
36
|
+
removed.
|
|
37
|
+
- `compressor` - backup compression policy; `nil` be default which turns
|
|
38
|
+
compression off, but can be set to SolidBackup::Compressor::Gzip.
|
|
36
39
|
|
|
37
40
|
Additionally, `SolidBackup::Backup::API` takes the following parameters:
|
|
38
41
|
|
|
@@ -52,10 +55,22 @@ SolidBackup.configure do |config|
|
|
|
52
55
|
# Back up the primary database using the SQLite backup API.
|
|
53
56
|
production.backup "primary",
|
|
54
57
|
SolidBackup::Backup::API,
|
|
55
|
-
destination: "storage/backups
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
58
|
+
destination: "storage/backups",
|
|
59
|
+
|
|
60
|
+
# Start the next backup 15 minutes after the previous one.
|
|
61
|
+
interval_in_minutes: 15,
|
|
62
|
+
|
|
63
|
+
# Back up in 100-page increments.
|
|
64
|
+
step: 100,
|
|
65
|
+
|
|
66
|
+
# Wait 0.1 seconds if a lock interrupts a backup step.
|
|
67
|
+
wait: 0.1,
|
|
68
|
+
|
|
69
|
+
# Remove backups older than 30 days.
|
|
70
|
+
expiration: SolidBackup::Expiration::Age.new(maximum: 30.days),
|
|
71
|
+
|
|
72
|
+
# Compress backups using gzip.
|
|
73
|
+
compressor: SolidBackup::Compressor::Gzip
|
|
59
74
|
|
|
60
75
|
# Don't back up the cache database.
|
|
61
76
|
production.backup "cache", SolidBackup::Backup::None
|
|
@@ -63,8 +78,16 @@ SolidBackup.configure do |config|
|
|
|
63
78
|
# Back up the queue database using VACUUM INTO.
|
|
64
79
|
production.backup "queue",
|
|
65
80
|
SolidBackup::Backup::VacuumInto,
|
|
66
|
-
destination: "storage/backups
|
|
67
|
-
|
|
81
|
+
destination: "storage/backups",
|
|
82
|
+
|
|
83
|
+
# Start the next backup 60 minutes after the previous one.
|
|
84
|
+
interval_in_minutes: 60,
|
|
85
|
+
|
|
86
|
+
# Remove backups older than 12 hours.
|
|
87
|
+
expiration: SolidBackup::Expiration::Age.new(maximum: 12.hours),
|
|
88
|
+
|
|
89
|
+
# Don't compress backups. This is the default setting.
|
|
90
|
+
compressor: nil
|
|
68
91
|
end
|
|
69
92
|
end
|
|
70
93
|
```
|
|
@@ -13,9 +13,9 @@ class SolidBackup::Backup::API < SolidBackup::Backup
|
|
|
13
13
|
def do_perform(backup_path)
|
|
14
14
|
with_connection do |connection|
|
|
15
15
|
source = connection.raw_connection
|
|
16
|
-
|
|
16
|
+
target = SQLite3::Database.new(backup_path)
|
|
17
17
|
|
|
18
|
-
backup = SQLite3::Backup.new(
|
|
18
|
+
backup = SQLite3::Backup.new(target, "main", source, "main")
|
|
19
19
|
|
|
20
20
|
loop do
|
|
21
21
|
result = backup.step(step)
|
|
@@ -32,7 +32,7 @@ class SolidBackup::Backup::API < SolidBackup::Backup
|
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
backup.finish
|
|
35
|
-
|
|
35
|
+
target.close
|
|
36
36
|
end
|
|
37
37
|
end
|
|
38
38
|
end
|
|
@@ -7,19 +7,19 @@
|
|
|
7
7
|
# - destination and interval_in_minutes - to allow nil values
|
|
8
8
|
class SolidBackup::Backup::None < SolidBackup::Backup
|
|
9
9
|
def initialize(database:)
|
|
10
|
-
super(database:, destination: nil, interval_in_minutes: 0)
|
|
10
|
+
super(database:, destination: nil, interval_in_minutes: 0, expiration: nil, compressor: nil)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def tick
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
-
def perform
|
|
17
|
-
end
|
|
18
|
-
|
|
19
16
|
private
|
|
20
17
|
|
|
21
18
|
attr_writer :destination, :interval_in_minutes
|
|
22
19
|
|
|
20
|
+
def perform
|
|
21
|
+
end
|
|
22
|
+
|
|
23
23
|
def do_perform(backup_path)
|
|
24
24
|
end
|
|
25
25
|
end
|
data/lib/solid_backup/backup.rb
CHANGED
|
@@ -4,10 +4,12 @@ module SolidBackup
|
|
|
4
4
|
class Backup
|
|
5
5
|
attr_reader :database
|
|
6
6
|
|
|
7
|
-
def initialize(database:, destination:, interval_in_minutes:)
|
|
7
|
+
def initialize(database:, destination:, interval_in_minutes:, expiration:, compressor:)
|
|
8
8
|
self.database = database
|
|
9
9
|
self.destination = destination
|
|
10
10
|
self.interval_in_minutes = interval_in_minutes
|
|
11
|
+
self.expiration = expiration
|
|
12
|
+
self.compressor = compressor
|
|
11
13
|
|
|
12
14
|
reset_countdown
|
|
13
15
|
end
|
|
@@ -19,23 +21,40 @@ module SolidBackup
|
|
|
19
21
|
|
|
20
22
|
if countdown <= 0
|
|
21
23
|
perform
|
|
24
|
+
remove_expired_backups
|
|
22
25
|
reset_countdown
|
|
23
26
|
end
|
|
24
27
|
end
|
|
25
28
|
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
attr_accessor :countdown, :expiration, :compressor
|
|
32
|
+
attr_writer :database
|
|
33
|
+
attr_reader :destination, :interval_in_minutes
|
|
34
|
+
|
|
26
35
|
def perform
|
|
27
36
|
ActiveSupport::Notifications.instrument("backup.solid_backup", database:) do
|
|
28
37
|
lock do
|
|
29
|
-
|
|
38
|
+
backup_path = Pathname(Time.now.utc.strftime((destination / file_name_pattern).to_s))
|
|
39
|
+
do_perform(backup_path)
|
|
40
|
+
|
|
41
|
+
next if compressor.nil?
|
|
42
|
+
|
|
43
|
+
compressor.compress(backup_path)
|
|
44
|
+
backup_path.unlink
|
|
30
45
|
end
|
|
31
46
|
end
|
|
32
47
|
end
|
|
33
48
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
49
|
+
def remove_expired_backups
|
|
50
|
+
files = destination.children.select(&:file?)
|
|
51
|
+
expiration.select_expired(files) do |pathname|
|
|
52
|
+
Time.strptime(pathname.basename.to_s, file_name_pattern)
|
|
53
|
+
rescue ArgumentError
|
|
54
|
+
end.each do |pathname|
|
|
55
|
+
pathname.unlink
|
|
56
|
+
end
|
|
57
|
+
end
|
|
39
58
|
|
|
40
59
|
def interval_in_minutes=(value)
|
|
41
60
|
if !value.is_a?(Integer) || value <= 0
|
|
@@ -49,17 +68,16 @@ module SolidBackup
|
|
|
49
68
|
|
|
50
69
|
def destination=(value)
|
|
51
70
|
value = Pathname(value)
|
|
52
|
-
directory = value.dirname
|
|
53
71
|
|
|
54
|
-
if !
|
|
55
|
-
raise "invalid backup configuration for #{database}: destination directory #{
|
|
72
|
+
if !value.directory?
|
|
73
|
+
raise "invalid backup configuration for #{database}: destination directory #{value} must exist"
|
|
56
74
|
end
|
|
57
75
|
|
|
58
|
-
test_path =
|
|
76
|
+
test_path = value / "solid_backup.txt"
|
|
59
77
|
begin
|
|
60
78
|
test_path.write("Solid Backup Test")
|
|
61
79
|
rescue Errno::EACCES
|
|
62
|
-
raise "invalid backup configuration for #{database}: destination directory #{
|
|
80
|
+
raise "invalid backup configuration for #{database}: destination directory #{value} must be writeable"
|
|
63
81
|
ensure
|
|
64
82
|
test_path.unlink
|
|
65
83
|
end
|
|
@@ -79,10 +97,14 @@ module SolidBackup
|
|
|
79
97
|
ActiveRecord::Base.connection_handler.establish_connection(database.to_sym)
|
|
80
98
|
end
|
|
81
99
|
|
|
100
|
+
def file_name_pattern
|
|
101
|
+
"#{database}_%Y%m%dT%H%M%S%Z.sqlite3"
|
|
102
|
+
end
|
|
103
|
+
|
|
82
104
|
delegate :with_connection, to: :connection_pool
|
|
83
105
|
|
|
84
106
|
def lock
|
|
85
|
-
path = destination
|
|
107
|
+
path = destination / ".solid_backup.#{database}.lock"
|
|
86
108
|
|
|
87
109
|
File.open(path, File::RDWR | File::CREAT) do |f|
|
|
88
110
|
if f.flock(File::LOCK_EX | File::LOCK_NB)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module SolidBackup::Compressor
|
|
2
|
+
module Gzip
|
|
3
|
+
def self.compress(source_path)
|
|
4
|
+
target_path = "#{source_path}.gz"
|
|
5
|
+
source_path = Pathname(source_path)
|
|
6
|
+
|
|
7
|
+
Zlib::GzipWriter.open(target_path) do |gzip|
|
|
8
|
+
gzip.mtime = source_path.mtime
|
|
9
|
+
gzip.orig_name = source_path.basename.to_s
|
|
10
|
+
gzip.write(source_path.binread)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
module SolidBackup::Expiration
|
|
2
|
+
class None
|
|
3
|
+
def select_expired(pathnames)
|
|
4
|
+
[]
|
|
5
|
+
end
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
class Age
|
|
9
|
+
def initialize(maximum:)
|
|
10
|
+
self.maximum = maximum
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def select_expired(pathnames)
|
|
14
|
+
threshold = maximum.ago.utc
|
|
15
|
+
pathnames.select do |pathname|
|
|
16
|
+
timestamp = yield(pathname)
|
|
17
|
+
timestamp && timestamp < threshold
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
attr_accessor :maximum
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -15,8 +15,10 @@ module SolidBackup
|
|
|
15
15
|
<<~CODE.chomp
|
|
16
16
|
production.backup #{db_config.name.inspect},
|
|
17
17
|
SolidBackup::Backup::API,
|
|
18
|
-
destination: "storage/backups
|
|
18
|
+
destination: "storage/backups",
|
|
19
19
|
interval_in_minutes: 60,
|
|
20
|
+
expiration: SolidBackup::Expiration::Age.new(maximum: 24.hours),
|
|
21
|
+
compressor: SolidBackup::Compressor::Gzip,
|
|
20
22
|
step: 100,
|
|
21
23
|
wait: 0.1
|
|
22
24
|
CODE
|
data/lib/solid_backup/version.rb
CHANGED
data/lib/solid_backup.rb
CHANGED
|
@@ -6,6 +6,8 @@ require "solid_backup/backup/api"
|
|
|
6
6
|
require "solid_backup/backup/vacuum_into"
|
|
7
7
|
require "solid_backup/configuration"
|
|
8
8
|
require "solid_backup/configuration/environment"
|
|
9
|
+
require "solid_backup/expiration"
|
|
10
|
+
require "solid_backup/compressor"
|
|
9
11
|
require "solid_backup/version"
|
|
10
12
|
require "solid_backup/railtie"
|
|
11
13
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: solid_backup
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Greg Navis
|
|
@@ -108,8 +108,10 @@ files:
|
|
|
108
108
|
- lib/solid_backup/backup/api.rb
|
|
109
109
|
- lib/solid_backup/backup/none.rb
|
|
110
110
|
- lib/solid_backup/backup/vacuum_into.rb
|
|
111
|
+
- lib/solid_backup/compressor.rb
|
|
111
112
|
- lib/solid_backup/configuration.rb
|
|
112
113
|
- lib/solid_backup/configuration/environment.rb
|
|
114
|
+
- lib/solid_backup/expiration.rb
|
|
113
115
|
- lib/solid_backup/generators/install_generator.rb
|
|
114
116
|
- lib/solid_backup/railtie.rb
|
|
115
117
|
- lib/solid_backup/version.rb
|
|
@@ -134,7 +136,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
134
136
|
- !ruby/object:Gem::Version
|
|
135
137
|
version: '0'
|
|
136
138
|
requirements: []
|
|
137
|
-
rubygems_version: 4.0.
|
|
139
|
+
rubygems_version: 4.0.20
|
|
138
140
|
specification_version: 4
|
|
139
141
|
summary: Solid Backup provides simple backups for SQLite databases in Ruby on Rails
|
|
140
142
|
apps.
|