litestream 0.5.3 → 0.5.4

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: 213a742471df552871cb90f5f092a110491e618bea9036a3e206d46c68cd8593
4
- data.tar.gz: cdf0264bcb4c20658e3a27636ea1e58ba42c50a0d184730acae7bee0cec65727
3
+ metadata.gz: 54afac4af36cb4d209c3794111131de4947b5744cb703eca6f13b1401721b516
4
+ data.tar.gz: 6b740f328b644690153d61ae83c895c773b2af47bbbc772cc69307624c2ab975
5
5
  SHA512:
6
- metadata.gz: 4a09739e36c3811d424e25e3a3d5f1abe1c21771409dd8b8162893b4e92df8a3699fc8272ec01b85005553a9147c43e3745d2ad3f3a4a77346051be826ab6242
7
- data.tar.gz: e1ae61215ca82f440f13653ff5c24de815cff766e70714aa53b477b2e2e64c52391ef47520668db216af8160f6192e9b7e7092984705f24f70df6395f8bf56b6
6
+ metadata.gz: 9213864ec337125ee7419b143e38a68c2e0e92610d6f68ce61c2ebb0c78180b422c95b5effbb7e79ec25338d750ef06a860d636e45e780cfb148fc22ba31d472
7
+ data.tar.gz: 26d80893a73ef2ad642c8ec693313383248ce46db89326c20bb3c051f7d5213afb74d6e64f3ea056ad6908e0e41bf8beccdfc5d3892024d6982caa9f7ac62dd1
data/README.md CHANGED
@@ -53,9 +53,11 @@ The gem streamlines the configuration process by providing a default configurati
53
53
 
54
54
  ```yaml
55
55
  dbs:
56
- - path: $LITESTREAM_DATABASE_PATH
56
+ - path: storage/production.sqlite3
57
57
  replicas:
58
- - url: $LITESTREAM_REPLICA_BUCKET
58
+ - type: s3
59
+ bucket: $LITESTREAM_REPLICA_BUCKET
60
+ path: storage/production.sqlite3
59
61
  access-key-id: $LITESTREAM_ACCESS_KEY_ID
60
62
  secret-access-key: $LITESTREAM_SECRET_ACCESS_KEY
61
63
  ```
@@ -65,7 +67,6 @@ The gem also provides a default initializer file at `config/initializers/litestr
65
67
  ```ruby
66
68
  litestream_credentials = Rails.application.credentials.litestream
67
69
  Litestream.configure do |config|
68
- config.database_path = ActiveRecord::Base.connection_db_config.database
69
70
  config.replica_bucket = litestream_credentials.replica_bucket
70
71
  config.replica_key_id = litestream_credentials.replica_key_id
71
72
  config.replica_access_key = litestream_credentials.replica_access_key
@@ -240,7 +241,48 @@ replica generation index size created
240
241
  s3 a295b16a796689f3 1 4645465 2024-04-17T00:01:19Z
241
242
  ```
242
243
 
243
- ### Additional commands
244
+ ### Running commands from Ruby
245
+
246
+ In addition to the provided rake tasks, you can also run Litestream commands directly from Ruby. The gem provides a `Litestream::Commands` module that wraps the Litestream CLI commands. This is particularly useful for the introspection commands, as you can use the output in your Ruby code.
247
+
248
+ The `Litestream::Commands.databases` method returns an array of hashes with the "path" and "replicas" keys for each database:
249
+
250
+ ```ruby
251
+ Litestream::Commands.databases
252
+ # => [{"path"=>"/Users/you/Code/your-app/storage/production.sqlite3", "replicas"=>"s3"}]
253
+ ```
254
+
255
+ The `Litestream::Commands.generations` method returns an array of hashes with the "name", "generation", "lag", "start", and "end" keys for each generation:
256
+
257
+ ```ruby
258
+ Litestream::Commands.generations('storage/production.sqlite3')
259
+ # => [{"name"=>"s3", "generation"=>"5f4341bc3d22d615", "lag"=>"3s", "start"=>"2024-04-17T19:48:09Z", "end"=>"2024-04-17T19:48:09Z"}]
260
+ ```
261
+
262
+ The `Litestream::Commands.snapshots` method returns an array of hashes with the "replica", "generation", "index", "size", and "created" keys for each snapshot:
263
+
264
+ ```ruby
265
+ Litestream::Commands.snapshots('storage/production.sqlite3')
266
+ # => [{"replica"=>"s3", "generation"=>"5f4341bc3d22d615", "index"=>"0", "size"=>"4645465", "created"=>"2024-04-17T19:48:09Z"}]
267
+ ```
268
+
269
+ You can also restore a database programatically using the `Litestream::Commands.restore` method, which returns the path to the restored database:
270
+
271
+ ```ruby
272
+ Litestream::Commands.restore('storage/production.sqlite3')
273
+ # => "storage/production-20240418090048.sqlite3"
274
+ ```
275
+
276
+ Finally, you can verify the integrity of a restored database using the `Litestream::Commands.verify` method, which returns a hash with the "size" and "tables" keys for the original and restored databases:
277
+
278
+ ```ruby
279
+ Litestream::Commands.verify('storage/production.sqlite3')
280
+ # => {"size"=>{"original"=>21688320, "restored"=>21688320}, "tables"=>{"original"=>9, "restored"=>9}}
281
+ ```
282
+
283
+ You _can_ start the replication process using the `Litestream::Commands.replicate` method, but this is not recommended. The replication process should be managed by Litestream itself, and you should not need to manually start it.
284
+
285
+ ### Running commands from CLI
244
286
 
245
287
  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`.
246
288
 
@@ -272,7 +314,6 @@ Once you have a MinIO server running, you can create a bucket for Litestream to
272
314
 
273
315
  ```ruby
274
316
  Litestream.configure do |config|
275
- config.database_path = ActiveRecord::Base.connection_db_config.database
276
317
  config.replica_bucket = "s3://mybkt.localhost:9000/"
277
318
  config.replica_key_id = "minioadmin"
278
319
  config.replica_access_key = "minioadmin"
@@ -76,11 +76,11 @@ module Litestream
76
76
  exe_file
77
77
  end
78
78
 
79
- def replicate(async: true, **argv)
80
- execute("replicate", argv, async: async)
79
+ def replicate(async: false, **argv)
80
+ execute("replicate", argv, async: async, hashify: false)
81
81
  end
82
82
 
83
- def restore(database, async: true, **argv)
83
+ def restore(database, async: false, **argv)
84
84
  raise DatabaseRequiredException, "database argument is required for restore command, e.g. litestream:restore -- --database=path/to/database.sqlite" if database.nil?
85
85
 
86
86
  dir, file = File.split(database)
@@ -93,28 +93,12 @@ module Litestream
93
93
  "-o" => backup
94
94
  }.merge(argv)
95
95
 
96
- execute("restore", args, database, async: async)
96
+ execute("restore", args, database, async: async, hashify: false)
97
97
 
98
98
  backup
99
99
  end
100
100
 
101
- def databases(async: true, **argv)
102
- execute("databases", argv, async: async)
103
- end
104
-
105
- def generations(database, async: true, **argv)
106
- raise DatabaseRequiredException, "database argument is required for generations command, e.g. litestream:generations -- --database=path/to/database.sqlite" if database.nil?
107
-
108
- execute("generations", argv, database, async: async)
109
- end
110
-
111
- def snapshots(database, async: true, **argv)
112
- raise DatabaseRequiredException, "database argument is required for snapshots command, e.g. litestream:snapshots -- --database=path/to/database.sqlite" if database.nil?
113
-
114
- execute("snapshots", argv, database, async: async)
115
- end
116
-
117
- def verify(database, async: true, **argv)
101
+ def verify(database, async: false, **argv)
118
102
  raise DatabaseRequiredException, "database argument is required for verify command, e.g. litestream:verify -- --database=path/to/database.sqlite" if database.nil? || !File.exist?(database)
119
103
 
120
104
  backup = restore(database, async: false, **argv)
@@ -129,14 +113,35 @@ module Litestream
129
113
  Dir.glob(backup + "*").each { |file| File.delete(file) }
130
114
 
131
115
  {
132
- size: {original: original_size, restored: restored_size},
133
- tables: {original: original_tables_count, restored: restored_tables_count}
116
+ "size" => {"original" => original_size, "restored" => restored_size},
117
+ "tables" => {"original" => original_tables_count, "restored" => restored_tables_count}
134
118
  }
135
119
  end
136
120
 
121
+ def databases(async: false, **argv)
122
+ execute("databases", argv, async: async, hashify: true)
123
+ end
124
+
125
+ def generations(database, async: false, **argv)
126
+ raise DatabaseRequiredException, "database argument is required for generations command, e.g. litestream:generations -- --database=path/to/database.sqlite" if database.nil?
127
+
128
+ execute("generations", argv, database, async: async, hashify: true)
129
+ end
130
+
131
+ def snapshots(database, async: false, **argv)
132
+ raise DatabaseRequiredException, "database argument is required for snapshots command, e.g. litestream:snapshots -- --database=path/to/database.sqlite" if database.nil?
133
+
134
+ execute("snapshots", argv, database, async: async, hashify: true)
135
+ end
136
+
137
137
  private
138
138
 
139
- def execute(command, argv = {}, database = nil, async: true)
139
+ def execute(command, argv = {}, database = nil, async: false, hashify: false)
140
+ cmd = prepare(command, argv, database)
141
+ run(cmd, async: async, hashify: hashify)
142
+ end
143
+
144
+ def prepare(command, argv = {}, database = nil)
140
145
  if Litestream.configuration
141
146
  ENV["LITESTREAM_REPLICA_BUCKET"] ||= Litestream.configuration.replica_bucket
142
147
  ENV["LITESTREAM_ACCESS_KEY_ID"] ||= Litestream.configuration.replica_key_id
@@ -149,14 +154,24 @@ module Litestream
149
154
  cmd = [executable, command, *args, database].compact
150
155
  puts cmd.inspect if ENV["DEBUG"]
151
156
 
157
+ cmd
158
+ end
159
+
160
+ def run(cmd, async: false, hashify: false)
152
161
  if async
153
162
  # To release the resources of the Ruby process, just fork and exit.
154
163
  # The forked process executes litestream and replaces itself.
155
164
  exec(*cmd) if fork.nil?
156
165
  else
157
- system(*cmd)
166
+ out = `#{cmd.join(" ")}`
167
+ text_table_to_hashes(out) if hashify
158
168
  end
159
169
  end
170
+
171
+ def text_table_to_hashes(string)
172
+ keys, *rows = string.split("\n").map { _1.split(/\s+/) }
173
+ rows.map { keys.zip(_1).to_h }
174
+ end
160
175
  end
161
176
  end
162
177
  end
@@ -1,3 +1,3 @@
1
1
  module Litestream
2
- VERSION = "0.5.3"
2
+ VERSION = "0.5.4"
3
3
  end
data/lib/litestream.rb CHANGED
@@ -11,7 +11,7 @@ module Litestream
11
11
  end
12
12
 
13
13
  class Configuration
14
- attr_accessor :database_path, :replica_bucket, :replica_key_id, :replica_access_key
14
+ attr_accessor :replica_bucket, :replica_key_id, :replica_access_key
15
15
 
16
16
  def initialize
17
17
  end
@@ -21,8 +21,9 @@ namespace :litestream do
21
21
  .map { |pair| pair.split("=") }
22
22
  .each { |opt| options[opt[0]] = opt[1] || nil }
23
23
  end
24
+ options.symbolize_keys!
24
25
 
25
- Litestream::Commands.replicate(**options)
26
+ Litestream::Commands.replicate(async: true, **options)
26
27
  end
27
28
 
28
29
  desc "Restore a SQLite database from a Litestream replica, e.g. rake litestream:restore -- -database=storage/production.sqlite3"
@@ -33,8 +34,10 @@ namespace :litestream do
33
34
  .map { |pair| pair.split("=") }
34
35
  .each { |opt| options[opt[0]] = opt[1] || nil }
35
36
  end
37
+ database = options.delete("--database") || options.delete("-database")
38
+ options.symbolize_keys!
36
39
 
37
- Litestream::Commands.restore(options.delete("--database") || options.delete("-database"), **options)
40
+ Litestream::Commands.restore(database, async: true, **options)
38
41
  end
39
42
 
40
43
  desc "List all databases and associated replicas in the config file, e.g. rake litestream:databases -- -no-expand-env"
@@ -45,8 +48,9 @@ namespace :litestream do
45
48
  .map { |pair| pair.split("=") }
46
49
  .each { |opt| options[opt[0]] = opt[1] || nil }
47
50
  end
51
+ options.symbolize_keys!
48
52
 
49
- Litestream::Commands.databases(**options)
53
+ Litestream::Commands.databases(async: true, **options)
50
54
  end
51
55
 
52
56
  desc "List all generations for a database or replica, e.g. rake litestream:generations -- -database=storage/production.sqlite3"
@@ -57,8 +61,10 @@ namespace :litestream do
57
61
  .map { |pair| pair.split("=") }
58
62
  .each { |opt| options[opt[0]] = opt[1] || nil }
59
63
  end
64
+ database = options.delete("--database") || options.delete("-database")
65
+ options.symbolize_keys!
60
66
 
61
- Litestream::Commands.generations(options.delete("--database") || options.delete("-database"), **options)
67
+ Litestream::Commands.generations(database, async: true, **options)
62
68
  end
63
69
 
64
70
  desc "List all snapshots for a database or replica, e.g. rake litestream:snapshots -- -database=storage/production.sqlite3"
@@ -69,8 +75,10 @@ namespace :litestream do
69
75
  .map { |pair| pair.split("=") }
70
76
  .each { |opt| options[opt[0]] = opt[1] || nil }
71
77
  end
78
+ database = options.delete("--database") || options.delete("-database")
79
+ options.symbolize_keys!
72
80
 
73
- Litestream::Commands.snapshots(options.delete("--database") || options.delete("-database"), **options)
81
+ Litestream::Commands.snapshots(database, async: true, **options)
74
82
  end
75
83
 
76
84
  desc "verify backup of SQLite database from a Litestream replica, e.g. rake litestream:verify -- -database=storage/production.sqlite3"
@@ -81,8 +89,10 @@ namespace :litestream do
81
89
  .map { |pair| pair.split("=") }
82
90
  .each { |opt| options[opt[0]] = opt[1] || nil }
83
91
  end
92
+ database = options.delete("--database") || options.delete("-database")
93
+ options.symbolize_keys!
84
94
 
85
- result = Litestream::Commands.verify(options.delete("--database") || options.delete("-database"), **options)
95
+ result = Litestream::Commands.verify(database, async: true, **options)
86
96
 
87
97
  puts <<~TXT if result
88
98
 
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.5.3
4
+ version: 0.5.4
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-17 00:00:00.000000000 Z
11
+ date: 2024-04-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubyzip