ask-state-providers 0.4.1 → 0.4.2
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/lib/ask/state/providers/sqlite.rb +63 -41
- data/lib/ask/state/providers/version.rb +1 -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: 15f0b0f326dfb8c8edb6de15f540d9586336431aa187277e7a708600de665daa
|
|
4
|
+
data.tar.gz: 109159d685d50ee6070575abb4e8b58e8422b24c20fab8a65c1a4143af53a258
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4fc50fe29074ef8f25079cc572c037e4b8f665bc746c579117f3726934c076027541663747eca6c3984d411e3ae712ef0d1e0ded76869f9d2cc2cddcc4b71379
|
|
7
|
+
data.tar.gz: cc5cbd6989e1a04ca8e6d69fc09e37673dd4bd83479bfd97e3ad2360622c1eb887ca386806d8b4b461b78fcf57e08e29a73ab7db3f1d7fec2cc99591ec381a19
|
|
@@ -25,22 +25,17 @@ module Ask
|
|
|
25
25
|
def initialize(path: "sessions.db", **pragmas)
|
|
26
26
|
require "sqlite3"
|
|
27
27
|
|
|
28
|
+
@path = path
|
|
29
|
+
@pid = Process.pid
|
|
28
30
|
@mutex = Mutex.new
|
|
29
|
-
@
|
|
30
|
-
@db.results_as_hash = true
|
|
31
|
-
@db.busy_timeout = 5000
|
|
32
|
-
|
|
33
|
-
defaults = {
|
|
31
|
+
@pragmas = {
|
|
34
32
|
journal_mode: "WAL",
|
|
35
33
|
synchronous: "NORMAL",
|
|
36
34
|
foreign_keys: "ON",
|
|
37
35
|
cache_size: -64_000
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
defaults.merge(pragmas).each do |key, value|
|
|
41
|
-
@db.execute("PRAGMA #{key} = #{value}")
|
|
42
|
-
end
|
|
36
|
+
}.merge(pragmas)
|
|
43
37
|
|
|
38
|
+
connect
|
|
44
39
|
migrate
|
|
45
40
|
end
|
|
46
41
|
|
|
@@ -48,7 +43,7 @@ module Ask
|
|
|
48
43
|
|
|
49
44
|
def get(key)
|
|
50
45
|
@mutex.synchronize do
|
|
51
|
-
row =
|
|
46
|
+
row = db.get_first_row(<<~SQL, [key, Time.now.to_f])
|
|
52
47
|
SELECT value FROM state_store
|
|
53
48
|
WHERE key = ? AND (expires_at IS NULL OR expires_at > ?)
|
|
54
49
|
SQL
|
|
@@ -58,7 +53,7 @@ module Ask
|
|
|
58
53
|
|
|
59
54
|
def set(key, value, ttl: nil)
|
|
60
55
|
@mutex.synchronize do
|
|
61
|
-
|
|
56
|
+
db.execute(<<~SQL, [key, JSON.generate(value), ttl ? Time.now.to_f + ttl : nil])
|
|
62
57
|
INSERT OR REPLACE INTO state_store (key, value, expires_at)
|
|
63
58
|
VALUES (?, ?, ?)
|
|
64
59
|
SQL
|
|
@@ -67,10 +62,10 @@ module Ask
|
|
|
67
62
|
|
|
68
63
|
def delete(key)
|
|
69
64
|
@mutex.synchronize do
|
|
70
|
-
|
|
65
|
+
db.execute("DELETE FROM state_store WHERE key = ?", [key])
|
|
71
66
|
# delete removes everything under the key, including ordered
|
|
72
67
|
# lists (consumers store event feeds as lists).
|
|
73
|
-
|
|
68
|
+
db.execute("DELETE FROM lists WHERE list_key = ?", [key])
|
|
74
69
|
end
|
|
75
70
|
end
|
|
76
71
|
|
|
@@ -79,15 +74,15 @@ module Ask
|
|
|
79
74
|
now = Time.now.to_f
|
|
80
75
|
expires = ttl ? now + ttl : nil
|
|
81
76
|
|
|
82
|
-
row =
|
|
77
|
+
row = db.get_first_row(
|
|
83
78
|
"SELECT 1 FROM state_store WHERE key = ? AND (expires_at IS NULL OR expires_at > ?)",
|
|
84
79
|
[key, now]
|
|
85
80
|
)
|
|
86
81
|
return false if row
|
|
87
82
|
|
|
88
83
|
# Key doesn't exist or is expired — delete any leftovers, then insert
|
|
89
|
-
|
|
90
|
-
|
|
84
|
+
db.execute("DELETE FROM state_store WHERE key = ?", [key])
|
|
85
|
+
db.execute(<<~SQL, [key, JSON.generate(value), expires])
|
|
91
86
|
INSERT INTO state_store (key, value, expires_at)
|
|
92
87
|
VALUES (?, ?, ?)
|
|
93
88
|
SQL
|
|
@@ -97,16 +92,16 @@ module Ask
|
|
|
97
92
|
|
|
98
93
|
def clear
|
|
99
94
|
@mutex.synchronize do
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
95
|
+
db.execute("DELETE FROM state_store")
|
|
96
|
+
db.execute("DELETE FROM locks")
|
|
97
|
+
db.execute("DELETE FROM queues")
|
|
98
|
+
db.execute("DELETE FROM lists")
|
|
104
99
|
end
|
|
105
100
|
end
|
|
106
101
|
|
|
107
102
|
def exists?(key)
|
|
108
103
|
@mutex.synchronize do
|
|
109
|
-
row =
|
|
104
|
+
row = db.get_first_row(<<~SQL, [key, Time.now.to_f])
|
|
110
105
|
SELECT 1 FROM state_store
|
|
111
106
|
WHERE key = ? AND (expires_at IS NULL OR expires_at > ?)
|
|
112
107
|
SQL
|
|
@@ -129,7 +124,7 @@ module Ask
|
|
|
129
124
|
WHERE (expires_at IS NULL OR expires_at > ?)
|
|
130
125
|
SQL
|
|
131
126
|
end
|
|
132
|
-
|
|
127
|
+
db.execute(sql, params).map { |r| r["key"] }
|
|
133
128
|
end
|
|
134
129
|
end
|
|
135
130
|
|
|
@@ -141,14 +136,14 @@ module Ask
|
|
|
141
136
|
expires_at_time = Time.now + ttl
|
|
142
137
|
token = SecureRandom.hex(16)
|
|
143
138
|
|
|
144
|
-
row =
|
|
139
|
+
row = db.get_first_row(
|
|
145
140
|
"SELECT 1 FROM locks WHERE key = ? AND expires_at > ?",
|
|
146
141
|
[key, now]
|
|
147
142
|
)
|
|
148
143
|
return nil if row
|
|
149
144
|
|
|
150
|
-
|
|
151
|
-
|
|
145
|
+
db.execute("DELETE FROM locks WHERE key = ?", [key])
|
|
146
|
+
db.execute(<<~SQL, [key, expires_at_time.to_f, token])
|
|
152
147
|
INSERT INTO locks (key, expires_at, token)
|
|
153
148
|
VALUES (?, ?, ?)
|
|
154
149
|
SQL
|
|
@@ -159,11 +154,11 @@ module Ask
|
|
|
159
154
|
|
|
160
155
|
def release_lock(key, lock)
|
|
161
156
|
@mutex.synchronize do
|
|
162
|
-
|
|
157
|
+
db.execute(
|
|
163
158
|
"DELETE FROM locks WHERE key = ? AND token = ?",
|
|
164
159
|
[key, lock.token]
|
|
165
160
|
)
|
|
166
|
-
|
|
161
|
+
db.changes > 0
|
|
167
162
|
end
|
|
168
163
|
end
|
|
169
164
|
|
|
@@ -171,18 +166,18 @@ module Ask
|
|
|
171
166
|
|
|
172
167
|
def enqueue(queue, value)
|
|
173
168
|
@mutex.synchronize do
|
|
174
|
-
|
|
169
|
+
db.execute(<<~SQL, [queue, JSON.generate(value), Time.now.iso8601])
|
|
175
170
|
INSERT INTO queues (queue_name, value, enqueued_at)
|
|
176
171
|
VALUES (?, ?, ?)
|
|
177
172
|
SQL
|
|
178
|
-
id =
|
|
173
|
+
id = db.last_insert_row_id
|
|
179
174
|
QueueEntry.new(id: id.to_s, value: value, enqueued_at: Time.now)
|
|
180
175
|
end
|
|
181
176
|
end
|
|
182
177
|
|
|
183
178
|
def dequeue(queue)
|
|
184
179
|
@mutex.synchronize do
|
|
185
|
-
row =
|
|
180
|
+
row = db.get_first_row(<<~SQL, [queue])
|
|
186
181
|
DELETE FROM queues
|
|
187
182
|
WHERE id = (
|
|
188
183
|
SELECT id FROM queues
|
|
@@ -204,7 +199,7 @@ module Ask
|
|
|
204
199
|
|
|
205
200
|
def queue_depth(queue)
|
|
206
201
|
@mutex.synchronize do
|
|
207
|
-
row =
|
|
202
|
+
row = db.get_first_row(
|
|
208
203
|
"SELECT COUNT(*) AS cnt FROM queues WHERE queue_name = ?", [queue]
|
|
209
204
|
)
|
|
210
205
|
row["cnt"]
|
|
@@ -217,14 +212,14 @@ module Ask
|
|
|
217
212
|
@mutex.synchronize do
|
|
218
213
|
serialized = JSON.generate(value)
|
|
219
214
|
|
|
220
|
-
|
|
215
|
+
db.execute(<<~SQL, [key, serialized])
|
|
221
216
|
INSERT INTO lists (list_key, value)
|
|
222
217
|
VALUES (?, ?)
|
|
223
218
|
SQL
|
|
224
219
|
|
|
225
220
|
return unless max_length
|
|
226
221
|
|
|
227
|
-
row =
|
|
222
|
+
row = db.get_first_row(<<~SQL, [key, max_length])
|
|
228
223
|
SELECT MIN(id) AS cutoff FROM (
|
|
229
224
|
SELECT id FROM lists
|
|
230
225
|
WHERE list_key = ?
|
|
@@ -234,7 +229,7 @@ module Ask
|
|
|
234
229
|
SQL
|
|
235
230
|
return unless row && row["cutoff"]
|
|
236
231
|
|
|
237
|
-
|
|
232
|
+
db.execute(<<~SQL, [key, row["cutoff"]])
|
|
238
233
|
DELETE FROM lists WHERE list_key = ? AND id < ?
|
|
239
234
|
SQL
|
|
240
235
|
end
|
|
@@ -243,7 +238,7 @@ module Ask
|
|
|
243
238
|
def list_range(key, start = 0, stop = -1)
|
|
244
239
|
@mutex.synchronize do
|
|
245
240
|
rows = if stop == -1
|
|
246
|
-
|
|
241
|
+
db.execute(<<~SQL, [key, start])
|
|
247
242
|
SELECT value FROM lists
|
|
248
243
|
WHERE list_key = ?
|
|
249
244
|
ORDER BY id ASC
|
|
@@ -251,7 +246,7 @@ module Ask
|
|
|
251
246
|
SQL
|
|
252
247
|
else
|
|
253
248
|
limit = stop - start + 1
|
|
254
|
-
|
|
249
|
+
db.execute(<<~SQL, [key, limit, start])
|
|
255
250
|
SELECT value FROM lists
|
|
256
251
|
WHERE list_key = ?
|
|
257
252
|
ORDER BY id ASC
|
|
@@ -265,11 +260,11 @@ module Ask
|
|
|
265
260
|
def list_remove(key, value)
|
|
266
261
|
@mutex.synchronize do
|
|
267
262
|
serialized = JSON.generate(value)
|
|
268
|
-
|
|
263
|
+
db.execute(
|
|
269
264
|
"DELETE FROM lists WHERE list_key = ? AND value = ?",
|
|
270
265
|
[key, serialized]
|
|
271
266
|
)
|
|
272
|
-
|
|
267
|
+
db.changes
|
|
273
268
|
end
|
|
274
269
|
end
|
|
275
270
|
|
|
@@ -285,14 +280,41 @@ module Ask
|
|
|
285
280
|
|
|
286
281
|
def close
|
|
287
282
|
@mutex.synchronize do
|
|
288
|
-
@db
|
|
283
|
+
@db&.close unless @db&.closed?
|
|
289
284
|
end
|
|
290
285
|
end
|
|
291
286
|
|
|
292
287
|
private
|
|
293
288
|
|
|
289
|
+
# Open (or reopen) the database connection. Reconnect is the
|
|
290
|
+
# fork story: when a process forks (SolidQueue workers, Puma
|
|
291
|
+
# cluster), the child inherits the parent's connection, which
|
|
292
|
+
# sqlite3's fork safety closes — so every operation goes through
|
|
293
|
+
# #db, which reopens the connection and gives the child a fresh
|
|
294
|
+
# mutex when the process id changed.
|
|
295
|
+
def connect
|
|
296
|
+
@db = SQLite3::Database.new(@path)
|
|
297
|
+
@db.results_as_hash = true
|
|
298
|
+
@db.busy_timeout = 5000
|
|
299
|
+
@pragmas.each do |key, value|
|
|
300
|
+
@db.execute("PRAGMA #{key} = #{value}")
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
# The live connection, reopened if a fork closed it. Call this
|
|
305
|
+
# inside the mutex synchronize (connect swaps the mutex when the
|
|
306
|
+
# process changed, so the old mutex is the one we're holding).
|
|
307
|
+
def db
|
|
308
|
+
if @pid != Process.pid || @db.nil? || @db.closed?
|
|
309
|
+
@pid = Process.pid
|
|
310
|
+
@mutex = Mutex.new
|
|
311
|
+
connect
|
|
312
|
+
end
|
|
313
|
+
@db
|
|
314
|
+
end
|
|
315
|
+
|
|
294
316
|
def migrate
|
|
295
|
-
|
|
317
|
+
db.execute_batch(<<~SQL)
|
|
296
318
|
CREATE TABLE IF NOT EXISTS state_store (
|
|
297
319
|
key TEXT PRIMARY KEY NOT NULL,
|
|
298
320
|
value TEXT NOT NULL,
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: ask-state-providers
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.4.
|
|
4
|
+
version: 0.4.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kaka Ruto
|
|
@@ -106,7 +106,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
106
106
|
- !ruby/object:Gem::Version
|
|
107
107
|
version: '0'
|
|
108
108
|
requirements: []
|
|
109
|
-
rubygems_version: 4.0.
|
|
109
|
+
rubygems_version: 4.0.18
|
|
110
110
|
specification_version: 4
|
|
111
111
|
summary: Pluggable state backends for the ask-rb ecosystem
|
|
112
112
|
test_files: []
|