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 +4 -4
- data/lib/ask/state/providers/postgres.rb +36 -9
- data/lib/ask/state/providers/version.rb +1 -1
- metadata +29 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 39454f2f084a68e8741e42372f9fd84bb42764dd943788ad90705753b985bb56
|
|
4
|
+
data.tar.gz: 1fc9f522ab52cfbfa6757f0b519e61c4d5eef53ebdec2e3a99fef266b461a309
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
-
|
|
252
|
-
|
|
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 $
|
|
284
|
+
LIMIT $2::bigint
|
|
258
285
|
) sub
|
|
259
286
|
)
|
|
260
287
|
SQL
|
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.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
|