solid_backup 0.2.2 → 0.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '09416cd05590f787149f5de85fd0094cb124edeebd4a86a41bc943c1c3ea40bb'
4
- data.tar.gz: 31565779742aef8295c71134d01f0f78eaf578950800ca7d4f62f612512eafc6
3
+ metadata.gz: 195e930fb3a17dcfc67ad285b28ccdcd4dd7bcb3fc659a7bc85c4f7e1ab915e6
4
+ data.tar.gz: 1db537f2585f0548b73444e8f3661b811ea040a4ac306ed0a46eae3e9783c544
5
5
  SHA512:
6
- metadata.gz: 427730d383e49058a091bc569950f8ea857aa3d135d6b5a2815aad687662a09fa7dcf3a4a3031e0ffaa7975f4a3920c09666244fa7a6a55aa8ae52e01d400469
7
- data.tar.gz: 94306f1e4b34f84e2fa516d905187c6b74290dd33f7476704cdd6d8040514b91aaa49859c0778b2fd7fbeacd5c7fe4671ab22a3f0d9163ef96b88df87a580709
6
+ metadata.gz: ed4aba930756d2f3b32ad5571a5d84095497e9f63b3952f212350258608dc5f2ed0af100d19764d25d69146606560976d38630906bae695412a87c7b0b7befdb
7
+ data.tar.gz: 4189596b8c2468d089306a01d11ce08d3f8dd42e036a0eeb96e2e4837205cd4987011ca8d4102b70708848766140021d32cc19619681ce9c800d270958fbfab7
data/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # Solid Backup
2
2
 
3
+ ## 0.3.0
4
+
5
+ - `destination` is the directory where backup files will be placed, not a
6
+ %-encoded template.
7
+ - New `expiration:` option can be set to determine backup expiration policy;
8
+ currently only `SolidBackup::Expiration::Age.new(maximum: ...)` and
9
+ `SolidBackup::Expiration::None.new` are supported.
10
+
3
11
  ## 0.2.2
4
12
 
5
13
  - Don't call `SolidBackup` too early to avoid errors caused by Rails not being
data/README.md CHANGED
@@ -28,11 +28,12 @@ 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` - a `strftime` template used to generate backup file names; the
32
- directory under which the files are placed must already exist
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.
36
37
 
37
38
  Additionally, `SolidBackup::Backup::API` takes the following parameters:
38
39
 
@@ -52,10 +53,19 @@ SolidBackup.configure do |config|
52
53
  # Back up the primary database using the SQLite backup API.
53
54
  production.backup "primary",
54
55
  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.
56
+ destination: "storage/backups",
57
+
58
+ # Start the next backup 15 minutes after the previous one.
59
+ interval_in_minutes: 15,
60
+
61
+ # Back up in 100-page increments.
62
+ step: 100,
63
+
64
+ # Wait 0.1 seconds if a lock interrupts a backup step.
65
+ wait: 0.1,
66
+
67
+ # Remove backups older than 30 days.
68
+ expiration: SolidBackup::Expiration::Age.new(maximum: 30.days)
59
69
 
60
70
  # Don't back up the cache database.
61
71
  production.backup "cache", SolidBackup::Backup::None
@@ -63,8 +73,13 @@ SolidBackup.configure do |config|
63
73
  # Back up the queue database using VACUUM INTO.
64
74
  production.backup "queue",
65
75
  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.
76
+ destination: "storage/backups",
77
+
78
+ # Start the next backup 60 minutes after the previous one.
79
+ interval_in_minutes: 60,
80
+
81
+ # Remove backups older than 12 hours.
82
+ expiration: SolidBackup::Expiration::Age.new(maximum: 12.hours)
68
83
  end
69
84
  end
70
85
  ```
@@ -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
- destination = SQLite3::Database.new(backup_path)
16
+ target = SQLite3::Database.new(backup_path)
17
17
 
18
- backup = SQLite3::Backup.new(destination, "main", source, "main")
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
- destination.close
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)
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
@@ -4,10 +4,11 @@ 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:)
8
8
  self.database = database
9
9
  self.destination = destination
10
10
  self.interval_in_minutes = interval_in_minutes
11
+ self.expiration = expiration
11
12
 
12
13
  reset_countdown
13
14
  end
@@ -19,23 +20,34 @@ module SolidBackup
19
20
 
20
21
  if countdown <= 0
21
22
  perform
23
+ remove_expired_backups
22
24
  reset_countdown
23
25
  end
24
26
  end
25
27
 
28
+ private
29
+
30
+ attr_accessor :countdown, :expiration
31
+ attr_writer :database
32
+ attr_reader :destination, :interval_in_minutes
33
+
26
34
  def perform
27
35
  ActiveSupport::Notifications.instrument("backup.solid_backup", database:) do
28
36
  lock do
29
- do_perform(Time.now.utc.strftime(destination.to_s))
37
+ do_perform(Time.now.utc.strftime((destination / file_name_pattern).to_s))
30
38
  end
31
39
  end
32
40
  end
33
41
 
34
- private
35
-
36
- attr_accessor :countdown
37
- attr_writer :database
38
- attr_reader :destination, :interval_in_minutes
42
+ def remove_expired_backups
43
+ files = destination.children.select(&:file?)
44
+ expiration.select_expired(files) do |pathname|
45
+ Time.strptime(pathname.basename.to_s, file_name_pattern)
46
+ rescue ArgumentError
47
+ end.each do |pathname|
48
+ pathname.unlink
49
+ end
50
+ end
39
51
 
40
52
  def interval_in_minutes=(value)
41
53
  if !value.is_a?(Integer) || value <= 0
@@ -49,17 +61,16 @@ module SolidBackup
49
61
 
50
62
  def destination=(value)
51
63
  value = Pathname(value)
52
- directory = value.dirname
53
64
 
54
- if !directory.directory?
55
- raise "invalid backup configuration for #{database}: destination directory #{directory} must exist"
65
+ if !value.directory?
66
+ raise "invalid backup configuration for #{database}: destination directory #{value} must exist"
56
67
  end
57
68
 
58
- test_path = directory / "solid_backup.txt"
69
+ test_path = value / "solid_backup.txt"
59
70
  begin
60
71
  test_path.write("Solid Backup Test")
61
72
  rescue Errno::EACCES
62
- raise "invalid backup configuration for #{database}: destination directory #{directory} must be writeable"
73
+ raise "invalid backup configuration for #{database}: destination directory #{value} must be writeable"
63
74
  ensure
64
75
  test_path.unlink
65
76
  end
@@ -79,10 +90,14 @@ module SolidBackup
79
90
  ActiveRecord::Base.connection_handler.establish_connection(database.to_sym)
80
91
  end
81
92
 
93
+ def file_name_pattern
94
+ "#{database}_%Y%m%dT%H%M%S.sqlite3"
95
+ end
96
+
82
97
  delegate :with_connection, to: :connection_pool
83
98
 
84
99
  def lock
85
- path = destination.dirname / ".solid_backup.#{database}.lock"
100
+ path = destination / ".solid_backup.#{database}.lock"
86
101
 
87
102
  File.open(path, File::RDWR | File::CREAT) do |f|
88
103
  if f.flock(File::LOCK_EX | File::LOCK_NB)
@@ -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
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,9 @@ module SolidBackup
15
15
  <<~CODE.chomp
16
16
  production.backup #{db_config.name.inspect},
17
17
  SolidBackup::Backup::API,
18
- destination: "storage/backups/#{db_config.name}_%Y%m%dT%H%M%S.sqlite3",
18
+ destination: "storage/backups",
19
19
  interval_in_minutes: 60,
20
+ expiration: SolidBackup::Expiration::Age.new(maximum: 24.hours),
20
21
  step: 100,
21
22
  wait: 0.1
22
23
  CODE
@@ -1,3 +1,3 @@
1
1
  module SolidBackup
2
- VERSION = "0.2.2"
2
+ VERSION = "0.3.0"
3
3
  end
data/lib/solid_backup.rb CHANGED
@@ -6,6 +6,7 @@ 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"
9
10
  require "solid_backup/version"
10
11
  require "solid_backup/railtie"
11
12
 
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.2.2
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Greg Navis
@@ -110,6 +110,7 @@ files:
110
110
  - lib/solid_backup/backup/vacuum_into.rb
111
111
  - lib/solid_backup/configuration.rb
112
112
  - lib/solid_backup/configuration/environment.rb
113
+ - lib/solid_backup/expiration.rb
113
114
  - lib/solid_backup/generators/install_generator.rb
114
115
  - lib/solid_backup/railtie.rb
115
116
  - lib/solid_backup/version.rb