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 +4 -4
- data/README.md +128 -2
- data/exe/litestream +0 -1
- data/lib/litestream/commands.rb +96 -58
- data/lib/litestream/version.rb +1 -1
- data/lib/litestream.rb +0 -1
- data/lib/tasks/litestream_tasks.rake +49 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c7942f70131f11cfcfad19c07442ab78571ad5bb28f67d8e38f1c35573a04413
|
4
|
+
data.tar.gz: 7584c2778dff02a7ded7078656ba57247146a67924c2ef05f4e1fe8407ea04e9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
-
|
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)
|
data/lib/litestream/commands.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
14
|
-
end
|
12
|
+
ExecutableNotFoundException = Class.new(StandardError)
|
15
13
|
|
16
14
|
# raised when LITESTREAM_INSTALL_DIR does not exist
|
17
|
-
|
18
|
-
end
|
15
|
+
DirectoryNotFoundException = Class.new(StandardError)
|
19
16
|
|
20
|
-
|
21
|
-
|
22
|
-
|
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
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
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
|
-
|
33
|
-
|
34
|
-
|
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
|
-
|
37
|
-
if
|
38
|
-
raise
|
39
|
-
|
40
|
-
|
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
|
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
|
-
|
50
|
-
|
51
|
-
|
76
|
+
def replicate(argv = {})
|
77
|
+
execute("replicate", argv)
|
78
|
+
end
|
52
79
|
|
53
|
-
|
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
|
-
|
56
|
-
|
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
|
-
|
88
|
+
args = {
|
89
|
+
"-o" => File.join(dir, "#{base}-#{now}#{ext}")
|
90
|
+
}.merge(argv)
|
59
91
|
|
60
|
-
|
61
|
-
|
92
|
+
execute("restore", args, database)
|
93
|
+
end
|
62
94
|
|
63
|
-
|
95
|
+
def databases(argv = {})
|
96
|
+
execute("databases", argv)
|
97
|
+
end
|
64
98
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
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
|
-
|
73
|
-
|
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
|
-
|
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
|
-
|
83
|
-
"--config" => Rails.root.join("config", "litestream.yml").to_s
|
84
|
-
}.merge(argv).to_a.flatten.compact
|
111
|
+
private
|
85
112
|
|
86
|
-
command =
|
87
|
-
|
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
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
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
|
data/lib/litestream/version.rb
CHANGED
data/lib/litestream.rb
CHANGED
@@ -13,7 +13,7 @@ namespace :litestream do
|
|
13
13
|
true
|
14
14
|
end
|
15
15
|
|
16
|
-
desc
|
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
|
+
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-
|
11
|
+
date: 2024-04-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubyzip
|