smplkit 3.0.135 → 3.0.136
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/smplkit/event_stream.rb +39 -9
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 82238262a8b9ee5b4d6a01d678238c0b47f5b16b029d2af72f7452d39b522040
|
|
4
|
+
data.tar.gz: e7fa9dc7b95404fd98661942c5cce79d538d2b6812cd0245cd3bf8617316a821
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 38eb4b4763f7a80130e7160d4736176782f55161624e4b2fddfc514bd8155df49820ba83710e2a3cf2eb02ab2856d7f453e05c5c96302657d3393700eb9aa650
|
|
7
|
+
data.tar.gz: 149450fce5a3dc5da39c26a3bed0c191bae710a6541acf5ce967062008d72c627c066354019eece249d8da34da730cef9c258d14bafb528f0a5e29393356d453
|
data/lib/smplkit/event_stream.rb
CHANGED
|
@@ -35,14 +35,23 @@ module Smplkit
|
|
|
35
35
|
#
|
|
36
36
|
# On disconnect the reactor reconnects with exponential backoff (base
|
|
37
37
|
# delay doubling up to +MAX_BACKOFF+ seconds), resetting to the base on
|
|
38
|
-
# every successful connect. +stop+
|
|
39
|
-
#
|
|
38
|
+
# every successful connect. +stop+ flips +@closed+ and waits; the reader
|
|
39
|
+
# re-checks the flag at least every +POLL_INTERVAL+, tears its own
|
|
40
|
+
# connection down in-reactor, and the daemon thread terminates.
|
|
40
41
|
class EventStream
|
|
41
42
|
# Seconds without any bytes from the server (events or keepalive
|
|
42
43
|
# comments) before the connection is considered dead — two missed
|
|
43
44
|
# 30-second server keepalives.
|
|
44
45
|
READ_TIMEOUT = 45
|
|
45
46
|
|
|
47
|
+
# How long a single blocking read may park the reactor before it wakes
|
|
48
|
+
# to re-check +@closed+. Liveness is tracked as a deadline across
|
|
49
|
+
# polls (see +read_loop+), so this changes nothing on the wire — it
|
|
50
|
+
# exists so +stop+ never has to interrupt the reactor from a foreign
|
|
51
|
+
# thread: a cross-thread close cannot wake a fiber blocked in
|
|
52
|
+
# +body.read+, which left teardown hanging until the next keepalive.
|
|
53
|
+
POLL_INTERVAL = 1.0
|
|
54
|
+
|
|
46
55
|
# Ceiling for the exponential reconnect backoff, in seconds.
|
|
47
56
|
MAX_BACKOFF = 60
|
|
48
57
|
|
|
@@ -254,12 +263,19 @@ module Smplkit
|
|
|
254
263
|
def stop
|
|
255
264
|
Smplkit.debug("events", "stopping shared event stream")
|
|
256
265
|
@closed = true
|
|
257
|
-
close_active_connection
|
|
258
266
|
thread = @stream_thread
|
|
259
267
|
@stream_thread = nil
|
|
260
268
|
if thread
|
|
261
|
-
|
|
262
|
-
|
|
269
|
+
# The reactor re-checks +@closed+ at least every POLL_INTERVAL and
|
|
270
|
+
# closes its own connection in-reactor on the way out — closing it
|
|
271
|
+
# from this thread instead would race the reactor and cannot wake
|
|
272
|
+
# a blocked read.
|
|
273
|
+
thread.join(POLL_INTERVAL + 1.0)
|
|
274
|
+
if thread.alive?
|
|
275
|
+
# Last resort: a connect attempt wedged before the read loop.
|
|
276
|
+
thread.kill
|
|
277
|
+
close_active_connection
|
|
278
|
+
end
|
|
263
279
|
end
|
|
264
280
|
# Set authoritatively after the thread is dead so a racing connect
|
|
265
281
|
# call (which also sets "connecting") cannot clobber this value.
|
|
@@ -405,19 +421,33 @@ module Smplkit
|
|
|
405
421
|
end
|
|
406
422
|
|
|
407
423
|
# Read chunks until EOF, feeding the SSE parser and dispatching the
|
|
408
|
-
# events it completes.
|
|
409
|
-
#
|
|
410
|
-
#
|
|
424
|
+
# events it completes. Reads park for at most POLL_INTERVAL at a time
|
|
425
|
+
# so +stop+ is honored promptly; liveness is a rolling deadline — any
|
|
426
|
+
# received bytes, including keepalive comment frames, push it out by
|
|
427
|
+
# READ_TIMEOUT. A deadline breach raises into the reconnect path.
|
|
411
428
|
def read_loop(task, body)
|
|
412
429
|
parser = Parser.new
|
|
430
|
+
deadline = monotonic_now + READ_TIMEOUT
|
|
413
431
|
until @closed
|
|
414
|
-
|
|
432
|
+
begin
|
|
433
|
+
chunk = task.with_timeout(POLL_INTERVAL) { body.read }
|
|
434
|
+
rescue Async::TimeoutError
|
|
435
|
+
raise Async::TimeoutError, "no data for #{READ_TIMEOUT}s" if monotonic_now >= deadline
|
|
436
|
+
|
|
437
|
+
next
|
|
438
|
+
end
|
|
415
439
|
break if chunk.nil?
|
|
416
440
|
|
|
441
|
+
deadline = monotonic_now + READ_TIMEOUT
|
|
417
442
|
process_chunk(parser, chunk)
|
|
418
443
|
end
|
|
419
444
|
end
|
|
420
445
|
|
|
446
|
+
# Seam for specs; the liveness deadline math needs a controllable clock.
|
|
447
|
+
def monotonic_now
|
|
448
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
449
|
+
end
|
|
450
|
+
|
|
421
451
|
def process_chunk(parser, chunk)
|
|
422
452
|
parser.feed(chunk).each { |event| handle_event(event.name, event.data) }
|
|
423
453
|
@retry_base = parser.retry_ms / 1000.0 unless parser.retry_ms.nil?
|