tina4ruby 3.10.11 → 3.10.12
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:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 58885d2a44c72f086b6359780fe7f01f88870bf3491973d9f1068934bb8780e1
|
|
4
|
+
data.tar.gz: c567b4d7a4a027d460da4bd528204ffd7ba3ce4aa6e0b3e5d303c748d00f12fe
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 409514dda53214f3f02d36dacd58b845cd9c282e63e814590be34c13e4cac839e8c8f6c6dc571575c434d0736a055109f12e3b56f76087ceee2d4b046930b1e8
|
|
7
|
+
data.tar.gz: caaa5dc05a96d5061bc3e70af18371d5e3fb7e8c10bd8e03cc84ca47be1863527814d478ec5788654b417d13ee754cc84f575ccb3ec43a5c16826fff798a5a52
|
data/lib/tina4/rack_app.rb
CHANGED
|
@@ -107,6 +107,16 @@ module Tina4
|
|
|
107
107
|
if request_obj&.instance_variable_get(:@session)
|
|
108
108
|
sess = request_obj.session
|
|
109
109
|
sess.save
|
|
110
|
+
|
|
111
|
+
# Probabilistic garbage collection (~1% of requests)
|
|
112
|
+
if rand(1..100) == 1
|
|
113
|
+
begin
|
|
114
|
+
sess.gc
|
|
115
|
+
rescue StandardError
|
|
116
|
+
# GC failure is non-critical — silently ignore
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
110
120
|
sid = sess.id
|
|
111
121
|
cookie_val = (env["HTTP_COOKIE"] || "")[/tina4_session=([^;]+)/, 1]
|
|
112
122
|
if sid && sid != cookie_val
|
|
@@ -56,6 +56,12 @@ module Tina4
|
|
|
56
56
|
@db.execute("DELETE FROM #{TABLE_NAME} WHERE expires_at > 0 AND expires_at < ?", [Time.now.to_f])
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
+
# Garbage-collect expired sessions. Matches the Python interface.
|
|
60
|
+
# @param max_age [Integer] maximum session age in seconds (unused — expiry is absolute)
|
|
61
|
+
def gc(max_age)
|
|
62
|
+
@db.execute("DELETE FROM #{TABLE_NAME} WHERE expires_at > 0 AND expires_at < ?", [Time.now.to_f])
|
|
63
|
+
end
|
|
64
|
+
|
|
59
65
|
private
|
|
60
66
|
|
|
61
67
|
def ensure_table
|
|
@@ -44,6 +44,18 @@ module Tina4
|
|
|
44
44
|
end
|
|
45
45
|
end
|
|
46
46
|
|
|
47
|
+
# Garbage-collect expired sessions. Matches the Python interface.
|
|
48
|
+
# @param max_age [Integer] maximum session age in seconds
|
|
49
|
+
def gc(max_age)
|
|
50
|
+
return unless Dir.exist?(@dir)
|
|
51
|
+
now = Time.now
|
|
52
|
+
Dir.glob(File.join(@dir, "sess_*")).each do |file|
|
|
53
|
+
File.delete(file) if File.mtime(file) + max_age < now
|
|
54
|
+
rescue StandardError
|
|
55
|
+
# Corrupt or locked file — skip
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
47
59
|
private
|
|
48
60
|
|
|
49
61
|
def session_path(session_id)
|
data/lib/tina4/version.rb
CHANGED
|
@@ -56,7 +56,7 @@ module Tina4
|
|
|
56
56
|
when "redis"
|
|
57
57
|
RedisBackplane.new(url: url)
|
|
58
58
|
when "nats"
|
|
59
|
-
|
|
59
|
+
NATSBackplane.new(url: url)
|
|
60
60
|
when ""
|
|
61
61
|
nil
|
|
62
62
|
else
|
|
@@ -115,4 +115,76 @@ module Tina4
|
|
|
115
115
|
@redis.close
|
|
116
116
|
end
|
|
117
117
|
end
|
|
118
|
+
|
|
119
|
+
# NATS pub/sub backplane.
|
|
120
|
+
#
|
|
121
|
+
# Requires the +nats-pure+ gem (+gem install nats-pure+). The require is
|
|
122
|
+
# deferred so the rest of Tina4 works fine without it installed — an error
|
|
123
|
+
# is raised only when this class is actually instantiated.
|
|
124
|
+
#
|
|
125
|
+
# NATS is async-native, so we run a background thread with an event
|
|
126
|
+
# machine for the subscription listener.
|
|
127
|
+
class NATSBackplane < WebSocketBackplane
|
|
128
|
+
def initialize(url: nil)
|
|
129
|
+
begin
|
|
130
|
+
require "nats/client"
|
|
131
|
+
rescue LoadError
|
|
132
|
+
raise LoadError,
|
|
133
|
+
"The 'nats-pure' gem is required for NATSBackplane. " \
|
|
134
|
+
"Install it with: gem install nats-pure"
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
@url = url || ENV.fetch("TINA4_WS_BACKPLANE_URL", "nats://localhost:4222")
|
|
138
|
+
@subs = {}
|
|
139
|
+
@threads = {}
|
|
140
|
+
@running = true
|
|
141
|
+
@mutex = Mutex.new
|
|
142
|
+
|
|
143
|
+
# Connect to NATS in a background thread with its own event loop
|
|
144
|
+
@nats = NATS::IO::Client.new
|
|
145
|
+
@nats.connect(@url)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def publish(channel, message)
|
|
149
|
+
@nats.publish(channel, message)
|
|
150
|
+
@nats.flush
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def subscribe(channel, &block)
|
|
154
|
+
@mutex.synchronize do
|
|
155
|
+
sid = @nats.subscribe(channel) do |msg|
|
|
156
|
+
block.call(msg.data) if @running
|
|
157
|
+
end
|
|
158
|
+
@subs[channel] = sid
|
|
159
|
+
|
|
160
|
+
# Run NATS event processing in a background thread
|
|
161
|
+
@threads[channel] ||= Thread.new do
|
|
162
|
+
loop do
|
|
163
|
+
break unless @running
|
|
164
|
+
sleep 0.01
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def unsubscribe(channel)
|
|
171
|
+
@mutex.synchronize do
|
|
172
|
+
sid = @subs.delete(channel)
|
|
173
|
+
@nats.unsubscribe(sid) if sid
|
|
174
|
+
thread = @threads.delete(channel)
|
|
175
|
+
thread&.kill
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def close
|
|
180
|
+
@running = false
|
|
181
|
+
@mutex.synchronize do
|
|
182
|
+
@subs.each_value { |sid| @nats.unsubscribe(sid) rescue nil }
|
|
183
|
+
@subs.clear
|
|
184
|
+
@threads.each_value { |t| t.kill }
|
|
185
|
+
@threads.clear
|
|
186
|
+
end
|
|
187
|
+
@nats.close
|
|
188
|
+
end
|
|
189
|
+
end
|
|
118
190
|
end
|