ask-state-providers 0.4.3 → 0.4.5

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: 51d2e01d6072da9c0296061aabcd038dd19e24b72d80de80b68a7ae3ed0ca8c3
4
- data.tar.gz: 2e2f5887f6e4cc7cfdfd56c20318532cc12c1a709533a333aff715a3d2b29056
3
+ metadata.gz: 39454f2f084a68e8741e42372f9fd84bb42764dd943788ad90705753b985bb56
4
+ data.tar.gz: 1fc9f522ab52cfbfa6757f0b519e61c4d5eef53ebdec2e3a99fef266b461a309
5
5
  SHA512:
6
- metadata.gz: 3552f0646a71c13685d16671d2dada5dec480c8dc40096e792c09220e9cd7da0013af0c600004c30935dba15dc53b66c37159c0e9b7011ce060d042d8524780a
7
- data.tar.gz: 5236da7d851548d43c89dec20ab7f130a6a54f5b77b52ced711553607143dc98a4e03c476f23b60480e79ebea3ac1c0548a0d7340add3ffe3526e1595d68d073
6
+ metadata.gz: e60207c2814ac955f0c2137966bb0a01b0bdf3743afc4441ee07a04a5e930584a773085a83c0883f963830e312525dc01ce262510b17e5fee73a7ab04104690a
7
+ data.tar.gz: 26534c8ede214627d96f80e5eb83622c455c1c2ef9ed521dd306f683a791cd19bb92e9577de91f910d1d0361386ea101388135f6ea38db671cc06a93ec98535a
@@ -3,6 +3,8 @@
3
3
  require "json"
4
4
  require "time"
5
5
  require "securerandom"
6
+ require "connection_pool"
7
+ require "pg"
6
8
 
7
9
  module Ask
8
10
  module State
@@ -58,7 +60,12 @@ module Ask
58
60
  # @param pool_size [Integer] connection pool size (default 5)
59
61
  def initialize(url: ENV.fetch("DATABASE_URL", "postgres://localhost:5432/ask_state"),
60
62
  pool_size: 5)
61
- require "pg"
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/
62
69
 
63
70
  @pool = ConnectionPool.new(size: pool_size) do
64
71
  ::PG.connect(url)
@@ -67,6 +74,14 @@ 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)
@@ -81,9 +96,11 @@ module Ask
81
96
 
82
97
  def set(key, value, ttl: nil)
83
98
  @pool.with do |conn|
99
+ # Explicit casts: $3 is nil when no ttl, and PostgreSQL can't
100
+ # infer a type for a bare NULL parameter.
84
101
  conn.exec_params(<<~SQL, [key, JSON.generate(value), ttl ? Time.now.utc + ttl : nil])
85
102
  INSERT INTO state_store (key, value, expires_at)
86
- VALUES ($1, $2, $3)
103
+ VALUES ($1::text, $2::text, $3::timestamptz)
87
104
  ON CONFLICT (key) DO UPDATE SET
88
105
  value = EXCLUDED.value,
89
106
  expires_at = EXCLUDED.expires_at
@@ -105,14 +122,21 @@ module Ask
105
122
  now = Time.now.utc
106
123
  expires = ttl ? now + ttl : nil
107
124
 
125
+ # Insert when the key is missing or its row is expired. An
126
+ # expired row still conflicts on the primary key, so the
127
+ # ON CONFLICT branch overwrites it — but only when it is
128
+ # actually expired, never a live value.
108
129
  result = conn.exec_params(<<~SQL, [key, JSON.generate(value), expires, now])
109
130
  INSERT INTO state_store (key, value, expires_at)
110
- SELECT $1, $2, $3
131
+ SELECT $1::text, $2::text, $3::timestamptz
111
132
  WHERE NOT EXISTS (
112
133
  SELECT 1 FROM state_store
113
134
  WHERE key = $1 AND (expires_at IS NULL OR expires_at > $4)
114
135
  )
115
- ON CONFLICT (key) DO NOTHING
136
+ ON CONFLICT (key) DO UPDATE SET
137
+ value = EXCLUDED.value,
138
+ expires_at = EXCLUDED.expires_at
139
+ WHERE state_store.expires_at IS NOT NULL AND state_store.expires_at <= $4
116
140
  SQL
117
141
  result.cmd_tuples > 0
118
142
  end
@@ -167,9 +191,9 @@ module Ask
167
191
  conn.exec_params("DELETE FROM locks WHERE key = $1 AND expires_at <= $2",
168
192
  [key, now])
169
193
 
170
- result = conn.exec_params(<<~SQL, [key, expires_at_time, token, now])
194
+ result = conn.exec_params(<<~SQL, [key, expires_at_time, token])
171
195
  INSERT INTO locks (key, expires_at, token)
172
- SELECT $1, $2, $3
196
+ SELECT $1::text, $2::timestamptz, $3::text
173
197
  WHERE NOT EXISTS (
174
198
  SELECT 1 FROM locks
175
199
  WHERE key = $1
@@ -248,13 +272,16 @@ module Ask
248
272
 
249
273
  return unless max_length
250
274
 
251
- conn.exec_params(<<~SQL, [key, key, max_length])
252
- DELETE FROM lists WHERE id <= (
275
+ # Keep the newest max_length entries: the cutoff is the
276
+ # oldest id inside the newest max_length — delete strictly
277
+ # older rows (and only this list's).
278
+ conn.exec_params(<<~SQL, [key, max_length, key])
279
+ DELETE FROM lists WHERE list_key = $3 AND id < (
253
280
  SELECT COALESCE(MIN(id), 0) FROM (
254
281
  SELECT id FROM lists
255
282
  WHERE list_key = $1
256
283
  ORDER BY id DESC
257
- LIMIT $3
284
+ LIMIT $2::bigint
258
285
  ) sub
259
286
  )
260
287
  SQL
@@ -3,7 +3,7 @@
3
3
  module Ask
4
4
  module State
5
5
  module Providers
6
- VERSION = "0.4.3"
6
+ VERSION = "0.4.5"
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.3
4
+ version: 0.4.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kaka Ruto
@@ -23,6 +23,34 @@ dependencies:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
25
  version: 0.11.3
26
+ - !ruby/object:Gem::Dependency
27
+ name: connection_pool
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '2.2'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '2.2'
40
+ - !ruby/object:Gem::Dependency
41
+ name: pg
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '1.5'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '1.5'
26
54
  - !ruby/object:Gem::Dependency
27
55
  name: minitest
28
56
  requirement: !ruby/object:Gem::Requirement