solid_backup 0.1.0 → 0.2.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 +9 -0
- data/README.md +22 -16
- data/lib/solid_backup/backup/none.rb +2 -2
- data/lib/solid_backup/backup.rb +13 -13
- data/lib/solid_backup/configuration/environment.rb +41 -0
- data/lib/solid_backup/configuration.rb +13 -18
- data/lib/solid_backup/generators/install_generator.rb +10 -8
- data/lib/solid_backup/version.rb +1 -1
- data/lib/solid_backup.rb +2 -1
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ee0e8ba44bfedbddb05671e68d06bdfc8e9e88f1d79ab38678cd15ce8023aba8
|
|
4
|
+
data.tar.gz: 3f1b4ec2ea4fa5b28528e398db334f9f987c34458f572ea3c2a36f5c3ebd45bd
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3b0025209e6f305cff67b6d3fb19e86a371541c299cd38ea092a8252ed4635c62b072b64f5e6221affa1028cf222b038c634c3f496b9a18384db34eb52cf37f6
|
|
7
|
+
data.tar.gz: 020ef132f3024278de2e68a440cfd5c613f1f55397525164da15a4fdb2d27307606010f7f06ea830751d1229016432b98fce7d9d0b31119bdd46e6f4cfdf8295
|
data/CHANGELOG.md
ADDED
data/README.md
CHANGED
|
@@ -44,22 +44,28 @@ mode in practice:
|
|
|
44
44
|
|
|
45
45
|
```ruby
|
|
46
46
|
SolidBackup.configure do |config|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
47
|
+
config.enabled = true
|
|
48
|
+
|
|
49
|
+
# Configure backups for all production databases. Other environments can have
|
|
50
|
+
# backups configured for them via additional config.environment blocks.
|
|
51
|
+
config.environment("production") do |production|
|
|
52
|
+
# Back up the primary database using the SQLite backup API.
|
|
53
|
+
production.backup "primary",
|
|
54
|
+
SolidBackup::Backup::API,
|
|
55
|
+
destination: "storage/backups/primary_%Y%m%dT%H%M%S.sqlite3",
|
|
56
|
+
interval_in_minutes: 15, # Start the next backup 15 minutes after the previous one.
|
|
57
|
+
step: 100, # Back up in 100-page increments.
|
|
58
|
+
wait: 0.1 # Wait 0.1 seconds if a lock interrupts a backup step.
|
|
59
|
+
|
|
60
|
+
# Don't back up the cache database.
|
|
61
|
+
production.backup "cache", SolidBackup::Backup::None
|
|
62
|
+
|
|
63
|
+
# Back up the queue database using VACUUM INTO.
|
|
64
|
+
production.backup "queue",
|
|
65
|
+
SolidBackup::Backup::VacuumInto,
|
|
66
|
+
destination: "storage/backups/queue_%Y%m%dT%H%M%S.sqlite3",
|
|
67
|
+
interval_in_minutes: 60 # Start the next backup 60 minutes after the previous one.
|
|
68
|
+
end
|
|
63
69
|
end
|
|
64
70
|
```
|
|
65
71
|
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
# - perform - to avoid creating lock files and emitting instrumentation events
|
|
7
7
|
# - destination and interval_in_minutes - to allow nil values
|
|
8
8
|
class SolidBackup::Backup::None < SolidBackup::Backup
|
|
9
|
-
def initialize(
|
|
10
|
-
super(
|
|
9
|
+
def initialize(database:)
|
|
10
|
+
super(database:, destination: nil, interval_in_minutes: 0)
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def tick
|
data/lib/solid_backup/backup.rb
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
module SolidBackup
|
|
2
2
|
class Backup
|
|
3
|
-
attr_reader :
|
|
3
|
+
attr_reader :database
|
|
4
4
|
|
|
5
|
-
def initialize(
|
|
6
|
-
self.
|
|
5
|
+
def initialize(database:, destination:, interval_in_minutes:)
|
|
6
|
+
self.database = database
|
|
7
7
|
self.destination = destination
|
|
8
8
|
self.interval_in_minutes = interval_in_minutes
|
|
9
9
|
|
|
@@ -22,7 +22,7 @@ module SolidBackup
|
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def perform
|
|
25
|
-
ActiveSupport::Notifications.instrument("backup.solid_backup",
|
|
25
|
+
ActiveSupport::Notifications.instrument("backup.solid_backup", database:) do
|
|
26
26
|
lock do
|
|
27
27
|
do_perform(Time.now.utc.strftime(destination.to_s))
|
|
28
28
|
end
|
|
@@ -32,13 +32,13 @@ module SolidBackup
|
|
|
32
32
|
private
|
|
33
33
|
|
|
34
34
|
attr_accessor :countdown
|
|
35
|
-
attr_writer :
|
|
35
|
+
attr_writer :database
|
|
36
36
|
attr_reader :destination, :interval_in_minutes
|
|
37
37
|
|
|
38
38
|
def interval_in_minutes=(value)
|
|
39
|
-
if !value.is_a?(Integer)
|
|
39
|
+
if !value.is_a?(Integer) || value <= 0
|
|
40
40
|
raise TypeError.new(<<~ERROR)
|
|
41
|
-
invalid backup configuration for #{
|
|
41
|
+
invalid backup configuration for #{database}: interval_in_minutes is set to #{value.inspect}, but should be set to a positive integer
|
|
42
42
|
ERROR
|
|
43
43
|
end
|
|
44
44
|
|
|
@@ -50,14 +50,14 @@ module SolidBackup
|
|
|
50
50
|
directory = value.dirname
|
|
51
51
|
|
|
52
52
|
if !directory.directory?
|
|
53
|
-
raise "invalid backup configuration for #{
|
|
53
|
+
raise "invalid backup configuration for #{database}: destination directory #{directory} must exist"
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
test_path = directory / "solid_backup.txt"
|
|
57
57
|
begin
|
|
58
58
|
test_path.write("Solid Backup Test")
|
|
59
|
-
rescue Errno::
|
|
60
|
-
raise "invalid backup configuration for #{
|
|
59
|
+
rescue Errno::EACCES
|
|
60
|
+
raise "invalid backup configuration for #{database}: destination directory #{directory} must be writeable"
|
|
61
61
|
ensure
|
|
62
62
|
test_path.unlink
|
|
63
63
|
end
|
|
@@ -74,13 +74,13 @@ module SolidBackup
|
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
def connection_pool
|
|
77
|
-
ActiveRecord::Base.connection_handler.establish_connection(
|
|
77
|
+
ActiveRecord::Base.connection_handler.establish_connection(database.to_sym)
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
delegate :with_connection, to: :connection_pool
|
|
81
81
|
|
|
82
82
|
def lock
|
|
83
|
-
path = destination.dirname / ".solid_backup.#{
|
|
83
|
+
path = destination.dirname / ".solid_backup.#{database}.lock"
|
|
84
84
|
|
|
85
85
|
File.open(path, File::RDWR | File::CREAT) do |f|
|
|
86
86
|
if f.flock(File::LOCK_EX | File::LOCK_NB)
|
|
@@ -94,7 +94,7 @@ module SolidBackup
|
|
|
94
94
|
|
|
95
95
|
begin
|
|
96
96
|
File.unlink(path)
|
|
97
|
-
rescue
|
|
97
|
+
rescue Errno::ENOENT
|
|
98
98
|
end
|
|
99
99
|
end
|
|
100
100
|
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
class SolidBackup::Configuration::Environment
|
|
2
|
+
attr_reader :name, :backups
|
|
3
|
+
|
|
4
|
+
def initialize(name)
|
|
5
|
+
self.name = name
|
|
6
|
+
self.backups = []
|
|
7
|
+
|
|
8
|
+
yield(self)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def tick
|
|
12
|
+
backups.each(&:tick)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def backup(database, algorithm_class, **algorithm_arguments)
|
|
16
|
+
database = database.to_s
|
|
17
|
+
if backups.any? { it.database == database }
|
|
18
|
+
raise ArgumentError.new("backups for #{database} in #{name} have already been configured")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
backups << algorithm_class.new(database:, **algorithm_arguments)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def validate!
|
|
25
|
+
configured_databases = backups.map(&:database)
|
|
26
|
+
all_databases = ActiveRecord::Base.configurations.configs_for(env_name: name).select do |db_config|
|
|
27
|
+
db_config.adapter == "sqlite3"
|
|
28
|
+
end.map(&:name)
|
|
29
|
+
|
|
30
|
+
if (missing_databases = all_databases - configured_databases).present?
|
|
31
|
+
raise "SQLite databases missing from backup configuration for #{name}: #{missing_databases.join(", ")}"
|
|
32
|
+
end
|
|
33
|
+
if (unknown_databases = configured_databases - all_databases).present?
|
|
34
|
+
raise "unknown SQLite databases present in backup configuration for #{name}: #{unknown_databases.join(", ")}"
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
attr_writer :name, :backups
|
|
41
|
+
end
|
|
@@ -1,20 +1,25 @@
|
|
|
1
1
|
module SolidBackup
|
|
2
2
|
class Configuration
|
|
3
3
|
attr_accessor :enabled
|
|
4
|
-
attr_reader :
|
|
4
|
+
attr_reader :environments
|
|
5
5
|
|
|
6
6
|
def initialize
|
|
7
7
|
self.enabled = nil
|
|
8
|
-
self.
|
|
8
|
+
self.environments = []
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
def
|
|
11
|
+
def environment(name, &)
|
|
12
12
|
name = name.to_s
|
|
13
|
-
if
|
|
14
|
-
raise ArgumentError.new("#{name} has
|
|
13
|
+
if environments.any? { it.name == name }
|
|
14
|
+
raise ArgumentError.new("environment #{name} has already been configured")
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
environments << SolidBackup::Configuration::Environment.new(name, &)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def tick(env_name)
|
|
21
|
+
env_name = env_name.to_s
|
|
22
|
+
environments.find { it.name == env_name }&.tick
|
|
18
23
|
end
|
|
19
24
|
|
|
20
25
|
def validate!
|
|
@@ -22,17 +27,7 @@ module SolidBackup
|
|
|
22
27
|
raise TypeError.new("enabled is set to #{enabled.inspect}, but allowed values are true or false")
|
|
23
28
|
end
|
|
24
29
|
|
|
25
|
-
|
|
26
|
-
all_database_names = ActiveRecord::Base.configurations.configs_for(env_name: Rails.env).select do |db_config|
|
|
27
|
-
db_config.adapter == "sqlite3"
|
|
28
|
-
end.map(&:name)
|
|
29
|
-
|
|
30
|
-
if (missing_databases = all_database_names - configured_database_names).present?
|
|
31
|
-
raise "SQLite databases missing from backup configuration: #{missing_databases.join(", ")}"
|
|
32
|
-
end
|
|
33
|
-
if (unknown_databases = configured_database_names - all_database_names).present?
|
|
34
|
-
raise "unknown SQLite databases present in backup configuration: #{unknown_databases.join(", ")}"
|
|
35
|
-
end
|
|
30
|
+
environments.each(&:validate!)
|
|
36
31
|
end
|
|
37
32
|
|
|
38
33
|
def enabled?
|
|
@@ -45,6 +40,6 @@ module SolidBackup
|
|
|
45
40
|
|
|
46
41
|
private
|
|
47
42
|
|
|
48
|
-
attr_writer :
|
|
43
|
+
attr_writer :environments
|
|
49
44
|
end
|
|
50
45
|
end
|
|
@@ -13,15 +13,15 @@ module SolidBackup
|
|
|
13
13
|
ActiveRecord::Base.configurations.configs_for(env_name: "production").each do |db_config|
|
|
14
14
|
backups << if yes?("Back up #{db_config.name} in production?")
|
|
15
15
|
<<~CODE.chomp
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
production.backup #{db_config.name.inspect},
|
|
17
|
+
SolidBackup::Backup::API,
|
|
18
|
+
destination: "storage/backups/#{db_config.name}_%Y%m%dT%H%M%S.sqlite3",
|
|
19
|
+
interval_in_minutes: 60,
|
|
20
|
+
step: 100,
|
|
21
|
+
wait: 0.1
|
|
22
22
|
CODE
|
|
23
23
|
else
|
|
24
|
-
"
|
|
24
|
+
"production.backup #{db_config.name.inspect}, SolidBackup::Backup::None"
|
|
25
25
|
end
|
|
26
26
|
end
|
|
27
27
|
|
|
@@ -34,7 +34,9 @@ module SolidBackup
|
|
|
34
34
|
#
|
|
35
35
|
# IMPORTANT: all databases must be explicitly configured, even if they're not
|
|
36
36
|
# backed up, to avoid accidentally excluding new databases from backups.
|
|
37
|
-
|
|
37
|
+
config.environment("production") do |production|
|
|
38
|
+
#{backups.join("\n").indent(4)}
|
|
39
|
+
end
|
|
38
40
|
end
|
|
39
41
|
CODE
|
|
40
42
|
|
data/lib/solid_backup/version.rb
CHANGED
data/lib/solid_backup.rb
CHANGED
|
@@ -3,6 +3,7 @@ require "solid_backup/backup/none"
|
|
|
3
3
|
require "solid_backup/backup/api"
|
|
4
4
|
require "solid_backup/backup/vacuum_into"
|
|
5
5
|
require "solid_backup/configuration"
|
|
6
|
+
require "solid_backup/configuration/environment"
|
|
6
7
|
require "solid_backup/version"
|
|
7
8
|
require "solid_backup/railtie"
|
|
8
9
|
|
|
@@ -18,7 +19,7 @@ module SolidBackup
|
|
|
18
19
|
end
|
|
19
20
|
|
|
20
21
|
def tick
|
|
21
|
-
configuration.
|
|
22
|
+
configuration.tick(Rails.env)
|
|
22
23
|
end
|
|
23
24
|
|
|
24
25
|
private
|
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.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Greg Navis
|
|
@@ -99,6 +99,7 @@ executables: []
|
|
|
99
99
|
extensions: []
|
|
100
100
|
extra_rdoc_files: []
|
|
101
101
|
files:
|
|
102
|
+
- CHANGELOG.md
|
|
102
103
|
- MIT-LICENSE.txt
|
|
103
104
|
- README.md
|
|
104
105
|
- lib/puma/plugin/solid_backup.rb
|
|
@@ -108,6 +109,7 @@ files:
|
|
|
108
109
|
- lib/solid_backup/backup/none.rb
|
|
109
110
|
- lib/solid_backup/backup/vacuum_into.rb
|
|
110
111
|
- lib/solid_backup/configuration.rb
|
|
112
|
+
- lib/solid_backup/configuration/environment.rb
|
|
111
113
|
- lib/solid_backup/generators/install_generator.rb
|
|
112
114
|
- lib/solid_backup/railtie.rb
|
|
113
115
|
- lib/solid_backup/version.rb
|