litestream 0.4.0 → 0.5.1

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
  SHA256:
3
- metadata.gz: 3437fcda9be45b8f50a4fcae89e6cc07d3ea590d7e3f0d2f10e50c9f8c6058b6
4
- data.tar.gz: f53e83796ed362491b50929d2924d070b6efa166830f7de6195e39300121be81
3
+ metadata.gz: c7942f70131f11cfcfad19c07442ab78571ad5bb28f67d8e38f1c35573a04413
4
+ data.tar.gz: 7584c2778dff02a7ded7078656ba57247146a67924c2ef05f4e1fe8407ea04e9
5
5
  SHA512:
6
- metadata.gz: d8731b80f97dee5bc512e4f4acd58aebb63a435379736d281edc8bd2b42e6cc524c2eb7a55fefc2cf93218b91b2fb12c4fe180685031c9924dacd73a918fe344
7
- data.tar.gz: 209fec5c3e355675feeee661ca64194cae370b85cbe90e162aaa2df059b2ccc5806b44e94daf706c29140e106e716eb1d3505a5efeeec65dfecbe4ee763ece09
6
+ metadata.gz: a48912bbcafb448473ebfd6ff378db5faa0a7f1830b786dff1b6574f8bd9ce210fd1f6b2f6652555544a38923a68fd87562abbca8e497db4282facc91a353ce0
7
+ data.tar.gz: a2a798e38ccf63d1fd21008bf1ef626a77652f8a8d1ef192fb1eeb11344f933d209f661dc3d24afd905d534ad5c295f4a0afb07b5cc45781ce152b2431746d72
data/README.md CHANGED
@@ -92,9 +92,135 @@ bin/rails litestream:replicate -- -exec "foreman start"
92
92
 
93
93
  This example utilizes the `-exec` option available on [the `replicate` command](https://litestream.io/reference/replicate/) which provides basic process management, since Litestream will exit when the child process exits. In this example, we only launch our collection of Rails application processes (like Rails and SolidQueue, for example) after the Litestream replication process is ready.
94
94
 
95
- The rake task is the recommended way to interact with the Litestream replication process in your Rails application or Ruby project. But, you _can_ take full manual control over the replication process and simply run the `litestream replicate --config config/litestream.yml` command to start the Litestream process. Since the gem installs the native executable via Bundler, the `litestream` command will be available in your `PATH`.
95
+ The Litestream `replicate` command supports the following options, which can be passed through the rake task:
96
+ ```shell
97
+ -config PATH
98
+ Specifies the configuration file.
99
+ Defaults to /etc/litestream.yml
100
+
101
+ -exec CMD
102
+ Executes a subcommand. Litestream will exit when the child
103
+ process exits. Useful for simple process management.
104
+
105
+ -no-expand-env
106
+ Disables environment variable expansion in configuration file.
107
+ ```
108
+
109
+ ### Restoration
110
+
111
+ You can restore any replicated database at any point using the gem's provided `litestream:restore` rake task. This rake task requires that you specify which specific database you want to restore. As with the `litestream:replicate` task, you pass arguments to the rake task via argument forwarding. For example, to restore the production database, you would run:
112
+
113
+ ```shell
114
+ bin/rails litestream:restore -- --database=storage/production.sqlite3
115
+ # or
116
+ bundle exec rake litestream:restore -- --database=storage/production.sqlite3
117
+ ```
118
+
119
+ You can restore any of the databases specified in your `config/litestream.yml` file. The `--database` argument should be the path to the database file you want to restore and must match the value for the `path` key of one of your configured databases. The `litestream:restore` rake task will automatically load the configuration file and set the environment variables before calling the Litestream executable.
120
+
121
+ If you need to pass arguments through the rake task to the underlying `litestream` command, that can be done with additional forwarded arguments:
122
+
123
+ ```shell
124
+ bin/rails litestream:replicate -- --database=storage/production.sqlite3 --if-db-not-exists
125
+ ```
126
+
127
+ You can forward arguments in whatever order you like, you simply need to ensure that the `--database` argument is present. You can also use either a single-dash `-database` or double-dash `--database` argument format. The Litestream `restore` command supports the following options, which can be passed through the rake task:
128
+
129
+ ```shell
130
+ -o PATH
131
+ Output path of the restored database.
132
+ Defaults to original DB path.
133
+
134
+ -if-db-not-exists
135
+ Returns exit code of 0 if the database already exists.
136
+
137
+ -if-replica-exists
138
+ Returns exit code of 0 if no backups found.
139
+
140
+ -parallelism NUM
141
+ Determines the number of WAL files downloaded in parallel.
142
+ Defaults to 8
143
+
144
+ -replica NAME
145
+ Restore from a specific replica.
146
+ Defaults to replica with latest data.
147
+
148
+ -generation NAME
149
+ Restore from a specific generation.
150
+ Defaults to generation with latest data.
151
+
152
+ -index NUM
153
+ Restore up to a specific WAL index (inclusive).
154
+ Defaults to use the highest available index.
155
+
156
+ -timestamp TIMESTAMP
157
+ Restore to a specific point-in-time.
158
+ Defaults to use the latest available backup.
159
+
160
+ -config PATH
161
+ Specifies the configuration file.
162
+ Defaults to /etc/litestream.yml
163
+
164
+ -no-expand-env
165
+ Disables environment variable expansion in configuration file.
166
+ ```
167
+
168
+ ### Introspection
96
169
 
97
- The full set of commands available to the `litestream` executable are covered in Litestream's [command reference](https://litestream.io/reference/). Currently, only the `replicate` command is provided as a rake task by the gem.
170
+ Litestream offers a handful of commands that allow you to introspect the state of your replication. The gem provides a few rake tasks that wrap these commands for you. For example, you can list the databases that Litestream is configured to replicate:
171
+
172
+ ```shell
173
+ bin/rails litestream:databases
174
+ ```
175
+
176
+ This will return a list of databases and their configured replicas:
177
+
178
+ ```
179
+ path replicas
180
+ /Users/you/Code/your-app/storage/production.sqlite3 s3
181
+ ```
182
+
183
+ You can also list the generations of a specific database:
184
+
185
+ ```shell
186
+ bin/rails litestream:generations -- --database=storage/production.sqlite3
187
+ ```
188
+
189
+ This will list all generations for the specified database, including stats about their lag behind the primary database and the time range they cover.
190
+
191
+ ```
192
+ name generation lag start end
193
+ s3 a295b16a796689f3 -156ms 2024-04-17T00:01:19Z 2024-04-17T00:01:19Z
194
+ ```
195
+
196
+ Finally, you can list the snapshots available for a database:
197
+
198
+ ```shell
199
+ bin/rails litestream:snapshots -- --database=storage/production.sqlite3
200
+ ```
201
+
202
+ This command lists snapshots available for that specified database:
203
+
204
+ ```
205
+ replica generation index size created
206
+ s3 a295b16a796689f3 1 4645465 2024-04-17T00:01:19Z
207
+ ```
208
+
209
+ ### Additional commands
210
+
211
+ The rake tasks are the recommended way to interact with the Litestream utility in your Rails application or Ruby project. But, you _can_ work directly with the Litestream CLI. Since the gem installs the native executable via Bundler, the `litestream` command will be available in your `PATH`.
212
+
213
+ The full set of commands available to the `litestream` executable are covered in Litestream's [command reference](https://litestream.io/reference/), but can be summarized as:
214
+
215
+ ```shell
216
+ litestream databases [arguments]
217
+ litestream generations [arguments] DB_PATH|REPLICA_URL
218
+ litestream replicate [arguments]
219
+ litestream restore [arguments] DB_PATH|REPLICA_URL
220
+ litestream snapshots [arguments] DB_PATH|REPLICA_URL
221
+ litestream version
222
+ litestream wal [arguments] DB_PATH|REPLICA_URL
223
+ ```
98
224
 
99
225
  ### Using in development
100
226
 
data/exe/litestream CHANGED
@@ -5,7 +5,6 @@ require "litestream/commands"
5
5
 
6
6
  begin
7
7
  command = [Litestream::Commands.executable, *ARGV]
8
- puts command.inspect
9
8
  exec(*command)
10
9
  rescue Litestream::Commands::UnsupportedPlatformException, Litestream::Commands::ExecutableNotFoundException => e
11
10
  warn("ERROR: " + e.message)
@@ -6,90 +6,128 @@ module Litestream
6
6
  GEM_NAME = "litestream"
7
7
 
8
8
  # raised when the host platform is not supported by upstream litestream's binary releases
9
- class UnsupportedPlatformException < StandardError
10
- end
9
+ UnsupportedPlatformException = Class.new(StandardError)
11
10
 
12
11
  # raised when the litestream executable could not be found where we expected it to be
13
- class ExecutableNotFoundException < StandardError
14
- end
12
+ ExecutableNotFoundException = Class.new(StandardError)
15
13
 
16
14
  # raised when LITESTREAM_INSTALL_DIR does not exist
17
- class DirectoryNotFoundException < StandardError
18
- end
15
+ DirectoryNotFoundException = Class.new(StandardError)
19
16
 
20
- def self.platform
21
- [:cpu, :os].map { |m| Gem::Platform.local.send(m) }.join("-")
22
- end
17
+ # raised when a litestream command requires a database argument but it isn't provided
18
+ DatabaseRequiredException = Class.new(StandardError)
19
+
20
+ class << self
21
+ def platform
22
+ [:cpu, :os].map { |m| Gem::Platform.local.send(m) }.join("-")
23
+ end
23
24
 
24
- def self.executable(exe_path: DEFAULT_DIR)
25
- litestream_install_dir = ENV["LITESTREAM_INSTALL_DIR"]
26
- if litestream_install_dir
27
- if File.directory?(litestream_install_dir)
28
- warn "NOTE: using LITESTREAM_INSTALL_DIR to find litestream executable: #{litestream_install_dir}"
29
- exe_path = litestream_install_dir
30
- exe_file = File.expand_path(File.join(litestream_install_dir, "litestream"))
25
+ def executable(exe_path: DEFAULT_DIR)
26
+ litestream_install_dir = ENV["LITESTREAM_INSTALL_DIR"]
27
+ if litestream_install_dir
28
+ if File.directory?(litestream_install_dir)
29
+ warn "NOTE: using LITESTREAM_INSTALL_DIR to find litestream executable: #{litestream_install_dir}"
30
+ exe_path = litestream_install_dir
31
+ exe_file = File.expand_path(File.join(litestream_install_dir, "litestream"))
32
+ else
33
+ raise DirectoryNotFoundException, <<~MESSAGE
34
+ LITESTREAM_INSTALL_DIR is set to #{litestream_install_dir}, but that directory does not exist.
35
+ MESSAGE
36
+ end
31
37
  else
32
- raise DirectoryNotFoundException, <<~MESSAGE
33
- LITESTREAM_INSTALL_DIR is set to #{litestream_install_dir}, but that directory does not exist.
34
- MESSAGE
38
+ if Litestream::Upstream::NATIVE_PLATFORMS.keys.none? { |p| Gem::Platform.match_gem?(Gem::Platform.new(p), GEM_NAME) }
39
+ raise UnsupportedPlatformException, <<~MESSAGE
40
+ litestream-ruby does not support the #{platform} platform
41
+ Please install litestream following instructions at https://litestream.io/install
42
+ MESSAGE
43
+ end
44
+
45
+ exe_file = Dir.glob(File.expand_path(File.join(exe_path, "*", "litestream"))).find do |f|
46
+ Gem::Platform.match_gem?(Gem::Platform.new(File.basename(File.dirname(f))), GEM_NAME)
47
+ end
35
48
  end
36
- else
37
- if Litestream::Upstream::NATIVE_PLATFORMS.keys.none? { |p| Gem::Platform.match_gem?(Gem::Platform.new(p), GEM_NAME) }
38
- raise UnsupportedPlatformException, <<~MESSAGE
39
- litestream-ruby does not support the #{platform} platform
40
- Please install litestream following instructions at https://litestream.io/install
49
+
50
+ if exe_file.nil? || !File.exist?(exe_file)
51
+ raise ExecutableNotFoundException, <<~MESSAGE
52
+ Cannot find the litestream executable for #{platform} in #{exe_path}
53
+
54
+ If you're using bundler, please make sure you're on the latest bundler version:
55
+
56
+ gem install bundler
57
+ bundle update --bundler
58
+
59
+ Then make sure your lock file includes this platform by running:
60
+
61
+ bundle lock --add-platform #{platform}
62
+ bundle install
63
+
64
+ See `bundle lock --help` output for details.
65
+
66
+ If you're still seeing this message after taking those steps, try running
67
+ `bundle config` and ensure `force_ruby_platform` isn't set to `true`. See
68
+ https://github.com/fractaledmind/litestream-ruby#check-bundle_force_ruby_platform
69
+ for more details.
41
70
  MESSAGE
42
71
  end
43
72
 
44
- exe_file = Dir.glob(File.expand_path(File.join(exe_path, "*", "litestream"))).find do |f|
45
- Gem::Platform.match_gem?(Gem::Platform.new(File.basename(File.dirname(f))), GEM_NAME)
46
- end
73
+ exe_file
47
74
  end
48
75
 
49
- if exe_file.nil? || !File.exist?(exe_file)
50
- raise ExecutableNotFoundException, <<~MESSAGE
51
- Cannot find the litestream executable for #{platform} in #{exe_path}
76
+ def replicate(argv = {})
77
+ execute("replicate", argv)
78
+ end
52
79
 
53
- If you're using bundler, please make sure you're on the latest bundler version:
80
+ def restore(database, argv = {})
81
+ raise DatabaseRequiredException, "database argument is required for restore command, e.g. litestream:restore -- --database=path/to/database.sqlite" if database.nil?
54
82
 
55
- gem install bundler
56
- bundle update --bundler
83
+ dir, file = File.split(database)
84
+ ext = File.extname(file)
85
+ base = File.basename(file, ext)
86
+ now = Time.now.utc.strftime("%Y%m%d%H%M%S")
57
87
 
58
- Then make sure your lock file includes this platform by running:
88
+ args = {
89
+ "-o" => File.join(dir, "#{base}-#{now}#{ext}")
90
+ }.merge(argv)
59
91
 
60
- bundle lock --add-platform #{platform}
61
- bundle install
92
+ execute("restore", args, database)
93
+ end
62
94
 
63
- See `bundle lock --help` output for details.
95
+ def databases(argv = {})
96
+ execute("databases", argv)
97
+ end
64
98
 
65
- If you're still seeing this message after taking those steps, try running
66
- `bundle config` and ensure `force_ruby_platform` isn't set to `true`. See
67
- https://github.com/fractaledmind/litestream-ruby#check-bundle_force_ruby_platform
68
- for more details.
69
- MESSAGE
99
+ def generations(database, argv = {})
100
+ raise DatabaseRequiredException, "database argument is required for generations command, e.g. litestream:generations -- --database=path/to/database.sqlite" if database.nil?
101
+
102
+ execute("generations", argv, database)
70
103
  end
71
104
 
72
- exe_file
73
- end
105
+ def snapshots(database, argv = {})
106
+ raise DatabaseRequiredException, "database argument is required for snapshots command, e.g. litestream:snapshots -- --database=path/to/database.sqlite" if database.nil?
74
107
 
75
- def self.replicate(argv = {})
76
- if Litestream.configuration
77
- ENV["LITESTREAM_REPLICA_BUCKET"] = Litestream.configuration.replica_bucket
78
- ENV["LITESTREAM_ACCESS_KEY_ID"] = Litestream.configuration.replica_key_id
79
- ENV["LITESTREAM_SECRET_ACCESS_KEY"] = Litestream.configuration.replica_access_key
108
+ execute("snapshots", argv, database)
80
109
  end
81
110
 
82
- args = {
83
- "--config" => Rails.root.join("config", "litestream.yml").to_s
84
- }.merge(argv).to_a.flatten.compact
111
+ private
85
112
 
86
- command = [executable, "replicate", *args]
87
- puts command.inspect
113
+ def execute(command, argv = {}, database = nil)
114
+ if Litestream.configuration
115
+ ENV["LITESTREAM_REPLICA_BUCKET"] ||= Litestream.configuration.replica_bucket
116
+ ENV["LITESTREAM_ACCESS_KEY_ID"] ||= Litestream.configuration.replica_key_id
117
+ ENV["LITESTREAM_SECRET_ACCESS_KEY"] ||= Litestream.configuration.replica_access_key
118
+ end
88
119
 
89
- # To release the resources of the Ruby process, just fork and exit.
90
- # The forked process executes litestream and replaces itself.
91
- if fork.nil?
92
- exec(*command)
120
+ args = {
121
+ "--config" => Rails.root.join("config", "litestream.yml").to_s
122
+ }.merge(argv).to_a.flatten.compact
123
+ cmd = [executable, command, *args, database].compact
124
+
125
+ # To release the resources of the Ruby process, just fork and exit.
126
+ # The forked process executes litestream and replaces itself.
127
+ if fork.nil?
128
+ puts cmd.inspect if ENV["DEBUG"]
129
+ exec(*cmd)
130
+ end
93
131
  end
94
132
  end
95
133
  end
@@ -1,3 +1,3 @@
1
1
  module Litestream
2
- VERSION = "0.4.0"
2
+ VERSION = "0.5.1"
3
3
  end
data/lib/litestream.rb CHANGED
@@ -14,7 +14,6 @@ module Litestream
14
14
  attr_accessor :database_path, :replica_bucket, :replica_key_id, :replica_access_key
15
15
 
16
16
  def initialize
17
- @mailer_sender = "donotreply@example.com"
18
17
  end
19
18
  end
20
19
  end
@@ -13,7 +13,7 @@ namespace :litestream do
13
13
  true
14
14
  end
15
15
 
16
- desc "Start a process to monitor and continuously replicate the SQLite databases defined in your configuration file"
16
+ desc 'Monitor and continuously replicate SQLite databases defined in your config file, e.g. rake litestream:replicate -- -exec "foreman start"'
17
17
  task replicate: :environment do
18
18
  options = {}
19
19
  if (separator_index = ARGV.index("--"))
@@ -24,4 +24,52 @@ namespace :litestream do
24
24
 
25
25
  Litestream::Commands.replicate(options)
26
26
  end
27
+
28
+ desc "Restore a SQLite database from a Litestream replica, e.g. rake litestream:restore -- -database=storage/production.sqlite3"
29
+ task restore: :environment do
30
+ options = {}
31
+ if (separator_index = ARGV.index("--"))
32
+ ARGV.slice(separator_index + 1, ARGV.length)
33
+ .map { |pair| pair.split("=") }
34
+ .each { |opt| options[opt[0]] = opt[1] || nil }
35
+ end
36
+
37
+ Litestream::Commands.restore(options.delete("--database") || options.delete("-database"), options)
38
+ end
39
+
40
+ desc "List all databases and associated replicas in the config file, e.g. rake litestream:databases -- -no-expand-env"
41
+ task databases: :environment do
42
+ options = {}
43
+ if (separator_index = ARGV.index("--"))
44
+ ARGV.slice(separator_index + 1, ARGV.length)
45
+ .map { |pair| pair.split("=") }
46
+ .each { |opt| options[opt[0]] = opt[1] || nil }
47
+ end
48
+
49
+ Litestream::Commands.databases(options)
50
+ end
51
+
52
+ desc "List all generations for a database or replica, e.g. rake litestream:generations -- -database=storage/production.sqlite3"
53
+ task generations: :environment do
54
+ options = {}
55
+ if (separator_index = ARGV.index("--"))
56
+ ARGV.slice(separator_index + 1, ARGV.length)
57
+ .map { |pair| pair.split("=") }
58
+ .each { |opt| options[opt[0]] = opt[1] || nil }
59
+ end
60
+
61
+ Litestream::Commands.generations(options.delete("--database") || options.delete("-database"), options)
62
+ end
63
+
64
+ desc "List all snapshots for a database or replica, e.g. rake litestream:snapshots -- -database=storage/production.sqlite3"
65
+ task snapshots: :environment do
66
+ options = {}
67
+ if (separator_index = ARGV.index("--"))
68
+ ARGV.slice(separator_index + 1, ARGV.length)
69
+ .map { |pair| pair.split("=") }
70
+ .each { |opt| options[opt[0]] = opt[1] || nil }
71
+ end
72
+
73
+ Litestream::Commands.snapshots(options.delete("--database") || options.delete("-database"), options)
74
+ end
27
75
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: litestream
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen Margheim
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-04-12 00:00:00.000000000 Z
11
+ date: 2024-04-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubyzip