ask-state-providers 0.4.4 → 0.4.6

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: 269e8a62defb31a02eb53fe42e5e13fb375b1a2037d3feb1a88048481f670ff5
4
- data.tar.gz: 86ca88be92adcd9ee95651904cf3fd72079a1adc899a1870c87065de1657d1e8
3
+ metadata.gz: cb68f83181b30aa39191b2d19252a62231921b17aae87e1f3673bc0bb97cfacb
4
+ data.tar.gz: 8ffdbac8c8577c8934482c8ed7b2b8e96563e9994ba48770ecbe7c51b0eaca46
5
5
  SHA512:
6
- metadata.gz: 0f8cd296decf02725687d44a96d3991b074fe975425885bf49ca064f68472370ed17d4596a336c8abaf6f8ff20366d0d7ff571a26dd7f46df1a0017c1d5053ac
7
- data.tar.gz: 7c7425f0732e1f92b66b9458274ded4009e42ef673ac354e266290055af0792a0451d27e2c1b6b1ff9329c26ed2e0400283471dd4890962a7b7b8c1dfae75cd1
6
+ metadata.gz: '0907e2cbe480f8760546a6f5790ea19ab8c81694db591a864ebc4de4f538d1f1baecac436b91257f544ed93e8b9de7ffeaa9d396c118b7a63a87750167a69e62'
7
+ data.tar.gz: 42984849cf013942caf8fbc27a859abc40e37720bf5c26a60e92c4e435f0df0cb133f1c591ef0e88284ac5b342e451c084c34fd0eb55391200d9161163de6ec3
@@ -60,6 +60,13 @@ module Ask
60
60
  # @param pool_size [Integer] connection pool size (default 5)
61
61
  def initialize(url: ENV.fetch("DATABASE_URL", "postgres://localhost:5432/ask_state"),
62
62
  pool_size: 5)
63
+ # On macOS, libpq's default GSSAPI negotiation can segfault
64
+ # when a forked process (SolidQueue worker) opens a fresh
65
+ # connection — the same crash the sibling apps guard with
66
+ # `gssencmode: disable` in database.yml. The store connects
67
+ # directly (not through ActiveRecord), so the guard lives here.
68
+ url = self.class.darwin_safe_url(url) if RUBY_PLATFORM =~ /darwin/
69
+
63
70
  @pool = ConnectionPool.new(size: pool_size) do
64
71
  ::PG.connect(url)
65
72
  end
@@ -67,10 +74,18 @@ module Ask
67
74
  migrate
68
75
  end
69
76
 
77
+ # Append gssencmode=disable to a URL on macOS (idempotent).
78
+ def self.darwin_safe_url(url)
79
+ return url if url.include?("gssencmode")
80
+
81
+ separator = url.include?("?") ? "&" : "?"
82
+ "#{url}#{separator}gssencmode=disable"
83
+ end
84
+
70
85
  # -- key-value --
71
86
 
72
87
  def get(key)
73
- @pool.with do |conn|
88
+ with_connection do |conn|
74
89
  row = conn.exec_params(<<~SQL, [key, Time.now.utc])
75
90
  SELECT value FROM state_store
76
91
  WHERE key = $1 AND (expires_at IS NULL OR expires_at > $2)
@@ -80,7 +95,7 @@ module Ask
80
95
  end
81
96
 
82
97
  def set(key, value, ttl: nil)
83
- @pool.with do |conn|
98
+ with_connection do |conn|
84
99
  # Explicit casts: $3 is nil when no ttl, and PostgreSQL can't
85
100
  # infer a type for a bare NULL parameter.
86
101
  conn.exec_params(<<~SQL, [key, JSON.generate(value), ttl ? Time.now.utc + ttl : nil])
@@ -94,7 +109,7 @@ module Ask
94
109
  end
95
110
 
96
111
  def delete(key)
97
- @pool.with do |conn|
112
+ with_connection do |conn|
98
113
  conn.exec_params("DELETE FROM state_store WHERE key = $1", [key])
99
114
  # delete removes everything under the key, including ordered
100
115
  # lists (consumers store event feeds as lists).
@@ -103,7 +118,7 @@ module Ask
103
118
  end
104
119
 
105
120
  def set_if_not_exists(key, value, ttl: nil)
106
- @pool.with do |conn|
121
+ with_connection do |conn|
107
122
  now = Time.now.utc
108
123
  expires = ttl ? now + ttl : nil
109
124
 
@@ -128,7 +143,7 @@ module Ask
128
143
  end
129
144
 
130
145
  def clear
131
- @pool.with do |conn|
146
+ with_connection do |conn|
132
147
  conn.exec("DELETE FROM state_store")
133
148
  conn.exec("DELETE FROM locks")
134
149
  conn.exec("DELETE FROM queues")
@@ -137,7 +152,7 @@ module Ask
137
152
  end
138
153
 
139
154
  def exists?(key)
140
- @pool.with do |conn|
155
+ with_connection do |conn|
141
156
  result = conn.exec_params(<<~SQL, [key, Time.now.utc])
142
157
  SELECT 1 FROM state_store
143
158
  WHERE key = $1 AND (expires_at IS NULL OR expires_at > $2)
@@ -147,7 +162,7 @@ module Ask
147
162
  end
148
163
 
149
164
  def keys(pattern: nil)
150
- @pool.with do |conn|
165
+ with_connection do |conn|
151
166
  sql, params = if pattern
152
167
  like = self.class.glob_to_like(pattern)
153
168
  [<<~SQL, [like, Time.now.utc]]
@@ -171,7 +186,7 @@ module Ask
171
186
  expires_at_time = now + ttl
172
187
  token = SecureRandom.hex(16)
173
188
 
174
- acquired = @pool.with do |conn|
189
+ acquired = with_connection do |conn|
175
190
  # First clean up expired locks
176
191
  conn.exec_params("DELETE FROM locks WHERE key = $1 AND expires_at <= $2",
177
192
  [key, now])
@@ -191,7 +206,7 @@ module Ask
191
206
  end
192
207
 
193
208
  def release_lock(key, lock)
194
- @pool.with do |conn|
209
+ with_connection do |conn|
195
210
  result = conn.exec_params(
196
211
  "DELETE FROM locks WHERE key = $1 AND token = $2",
197
212
  [key, lock.token]
@@ -205,7 +220,7 @@ module Ask
205
220
  def enqueue(queue, value)
206
221
  id = SecureRandom.uuid
207
222
 
208
- @pool.with do |conn|
223
+ with_connection do |conn|
209
224
  conn.exec_params(<<~SQL, [queue, JSON.generate(value), Time.now.utc])
210
225
  INSERT INTO queues (queue_name, value, enqueued_at)
211
226
  VALUES ($1, $2, $3)
@@ -216,7 +231,7 @@ module Ask
216
231
  end
217
232
 
218
233
  def dequeue(queue)
219
- @pool.with do |conn|
234
+ with_connection do |conn|
220
235
  result = conn.exec_params(<<~SQL, [queue])
221
236
  DELETE FROM queues
222
237
  WHERE id = (
@@ -238,7 +253,7 @@ module Ask
238
253
  end
239
254
 
240
255
  def queue_depth(queue)
241
- @pool.with do |conn|
256
+ with_connection do |conn|
242
257
  result = conn.exec_params(
243
258
  "SELECT COUNT(*) AS cnt FROM queues WHERE queue_name = $1", [queue]
244
259
  )
@@ -249,7 +264,7 @@ module Ask
249
264
  # -- ordered lists --
250
265
 
251
266
  def list_append(key, value, max_length: nil)
252
- @pool.with do |conn|
267
+ with_connection do |conn|
253
268
  conn.exec_params(<<~SQL, [key, JSON.generate(value)])
254
269
  INSERT INTO lists (list_key, value)
255
270
  VALUES ($1, $2)
@@ -274,7 +289,7 @@ module Ask
274
289
  end
275
290
 
276
291
  def list_range(key, start = 0, stop = -1)
277
- @pool.with do |conn|
292
+ with_connection do |conn|
278
293
  rows = if stop == -1
279
294
  conn.exec_params(<<~SQL, [key, start])
280
295
  SELECT value FROM lists
@@ -297,7 +312,7 @@ module Ask
297
312
 
298
313
  def list_remove(key, value)
299
314
  serialized = JSON.generate(value)
300
- @pool.with do |conn|
315
+ with_connection do |conn|
301
316
  result = conn.exec_params(
302
317
  "DELETE FROM lists WHERE list_key = $1 AND value = $2",
303
318
  [key, serialized]
@@ -311,17 +326,29 @@ module Ask
311
326
  # Idempotent setup — creates tables if they don't exist.
312
327
  # Called automatically on initialize. Safe to call multiple times.
313
328
  def setup!
314
- @pool.with { |conn| conn.exec(MIGRATIONS) }
329
+ with_connection { |conn| conn.exec(MIGRATIONS) }
315
330
  end
316
331
 
317
332
  def close
318
- @pool&.shutdown { |c| c.close }
333
+ @pool&.shutdown { |c| c.close unless c.finished? }
319
334
  end
320
335
 
321
336
  private
322
337
 
338
+ # Check out a connection, running the block with it. A dead
339
+ # pooled connection (the server closed it — a fork inherited it,
340
+ # an idle timeout, a server restart) raises PG::ConnectionBad;
341
+ # reload the pool and retry once so a stale checkout never
342
+ # surfaces to callers.
343
+ def with_connection(&block)
344
+ @pool.with(&block)
345
+ rescue PG::ConnectionBad
346
+ @pool.reload { |conn| conn.close unless conn.finished? }
347
+ @pool.with(&block)
348
+ end
349
+
323
350
  def migrate
324
- @pool.with { |conn| conn.exec(MIGRATIONS) }
351
+ with_connection { |conn| conn.exec(MIGRATIONS) }
325
352
  end
326
353
  end
327
354
  end
@@ -3,7 +3,7 @@
3
3
  module Ask
4
4
  module State
5
5
  module Providers
6
- VERSION = "0.4.4"
6
+ VERSION = "0.4.6"
7
7
  end
8
8
  end
9
9
  end
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
4
+ version: 0.4.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kaka Ruto