cable_room 0.6.1 → 0.6.2.beta1
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/cable_room/ports.rb +40 -2
- data/lib/cable_room/room/input_handling.rb +1 -1
- data/lib/cable_room/room_member.rb +61 -9
- data/lib/cable_room/version.rb +1 -1
- 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: 34ff8028043d9407e2f5f9487c472873b8ad56c1cda136ad50038ca28be578ff
|
|
4
|
+
data.tar.gz: 401c6500ac0d1c065967a4901a4a2e3d0ed75b1a8ffc3101260e820b5fff5bc4
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 83df8225dca28fd3c4fa70434698fea93bfd05ab495c5cac8d8718cb5acfe7a8c5800548a2332d2200247efc1dd29cd900d161e1914a75b3f34c908c972c4d24
|
|
7
|
+
data.tar.gz: 25e0451d688da7390793a0cca348e488b618c89dc8e98b510b775e7babdaf4df912c583b1bb1b0d04b8567c899646075c5abb27062e9e71de13f2fd458eb7169
|
data/lib/cable_room/ports.rb
CHANGED
|
@@ -6,8 +6,18 @@ module CableRoom
|
|
|
6
6
|
@ports_proxy ||= PortsProxy.new(self)
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
# Subscribe the channel to `port`, delivering each decoded message to the block.
|
|
10
|
+
#
|
|
11
|
+
# `on_live`, when given, runs once the pubsub adapter has confirmed the subscription — the
|
|
12
|
+
# first moment a message published to the port is guaranteed to reach the block. Subscribing
|
|
13
|
+
# is asynchronous, so anything published before then can be lost.
|
|
14
|
+
def stream_port(port, auto_close: true, on_live: nil, &blk)
|
|
15
|
+
broadcasting = room_port_key(port)
|
|
16
|
+
if on_live
|
|
17
|
+
_stream_from_observing_liveness(broadcasting, on_live, &blk)
|
|
18
|
+
else
|
|
19
|
+
@cable_channel.stream_from(broadcasting, coder: ActiveSupport::JSON, &blk)
|
|
20
|
+
end
|
|
11
21
|
_streamed_ports << port if auto_close
|
|
12
22
|
end
|
|
13
23
|
|
|
@@ -32,6 +42,34 @@ module CableRoom
|
|
|
32
42
|
@_streamed_ports ||= Set.new
|
|
33
43
|
end
|
|
34
44
|
|
|
45
|
+
# ActionCable::Channel::Streams#stream_from confirms the channel subscription once every one of
|
|
46
|
+
# its streams is live, but offers no per-stream hook. This is its body — unchanged from Rails
|
|
47
|
+
# 6.1 through 8.1 — with `on_live` added to the adapter's success callback.
|
|
48
|
+
#
|
|
49
|
+
# When the channel has replaced `stream_from` (ActionCable's channel test stubs, recorders,
|
|
50
|
+
# `StubRoomChannel`) there is no subscription to observe: defer to it and report live at once.
|
|
51
|
+
def _stream_from_observing_liveness(broadcasting, on_live, &blk)
|
|
52
|
+
chan = @cable_channel
|
|
53
|
+
unless chan.method(:stream_from).owner == ActionCable::Channel::Streams
|
|
54
|
+
chan.stream_from(broadcasting, coder: ActiveSupport::JSON, &blk)
|
|
55
|
+
on_live.call
|
|
56
|
+
return
|
|
57
|
+
end
|
|
58
|
+
return if chan.respond_to?(:unsubscribed?, true) && chan.send(:unsubscribed?)
|
|
59
|
+
|
|
60
|
+
chan.send(:defer_subscription_confirmation!)
|
|
61
|
+
handler = chan.send(:worker_pool_stream_handler, broadcasting, blk, coder: ActiveSupport::JSON)
|
|
62
|
+
chan.send(:streams)[broadcasting] = handler
|
|
63
|
+
|
|
64
|
+
chan.connection.server.event_loop.post do
|
|
65
|
+
chan.send(:pubsub).subscribe(broadcasting, handler, lambda do
|
|
66
|
+
chan.send(:ensure_confirmation_sent)
|
|
67
|
+
chan.logger.info "#{chan.class.name} is streaming from #{broadcasting}"
|
|
68
|
+
on_live.call
|
|
69
|
+
end)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
35
73
|
class PortsProxy
|
|
36
74
|
def initialize(ports_concern)
|
|
37
75
|
@ports_concern = ports_concern
|
|
@@ -56,7 +56,7 @@ module CableRoom
|
|
|
56
56
|
class_attribute :_system_message_types, instance_writer: false, default: Set.new
|
|
57
57
|
|
|
58
58
|
set_callback(:receive_message, :before) do
|
|
59
|
-
logger.debug "Received message: #{message.inspect}"
|
|
59
|
+
logger.debug { "Received message: #{message.inspect}" }
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
set_callback(:receive_message, :before) do
|
|
@@ -103,13 +103,13 @@ module CableRoom
|
|
|
103
103
|
return if left?
|
|
104
104
|
if @has_established
|
|
105
105
|
port_transmit(room_class::ROOM_IN_CHANNEL, { type: 'port_ping' }, secure_context: true)
|
|
106
|
-
|
|
107
|
-
#
|
|
108
|
-
#
|
|
109
|
-
#
|
|
110
|
-
# (see #<<)
|
|
111
|
-
# idempotent on the room side (the port is merged,
|
|
112
|
-
#
|
|
106
|
+
elsif @streams_live
|
|
107
|
+
# Announced but never acknowledged. Our streams were already live when we announced (see
|
|
108
|
+
# initiate_connection), so this is not the subscribe race — the announcement or the
|
|
109
|
+
# acknowledgement was lost in transit, or the room did not exist yet. An unestablished
|
|
110
|
+
# membership silently drops everything the client sends (see #<<), so re-announce instead
|
|
111
|
+
# of pinging: port_connected is idempotent on the room side (the port is merged,
|
|
112
|
+
# user_joined fires only once).
|
|
113
113
|
transmit_port_connected
|
|
114
114
|
end
|
|
115
115
|
@mutex.synchronize do
|
|
@@ -140,6 +140,23 @@ module CableRoom
|
|
|
140
140
|
|
|
141
141
|
def key; @room_key; end
|
|
142
142
|
|
|
143
|
+
# Every stream this membership opens reports back once it is live; the last one to come up
|
|
144
|
+
# sends port_connected (see initiate_connection).
|
|
145
|
+
#
|
|
146
|
+
# The adapter runs the liveness callback on its own terms — the async and inline adapters call
|
|
147
|
+
# it while holding their subscriber-map lock, the Redis adapter on the event loop — so nothing
|
|
148
|
+
# may happen inside it except a hop to a worker thread: taking @mutex there deadlocks against
|
|
149
|
+
# an initiate_connection that is mid-broadcast, and broadcasting from it re-enters the lock.
|
|
150
|
+
def stream_port(port, on_live: nil, **kwargs, &blk)
|
|
151
|
+
generation = @stream_generation
|
|
152
|
+
@streams_pending += 1
|
|
153
|
+
connection = @cable_channel.connection
|
|
154
|
+
super(port, **kwargs, on_live: lambda do
|
|
155
|
+
connection.worker_pool.async_invoke(self, :_stream_became_live, generation, connection: connection)
|
|
156
|
+
on_live&.call
|
|
157
|
+
end, &blk)
|
|
158
|
+
end
|
|
159
|
+
|
|
143
160
|
protected
|
|
144
161
|
|
|
145
162
|
def port_transmit(port, data, secure_context: false)
|
|
@@ -170,7 +187,9 @@ module CableRoom
|
|
|
170
187
|
@has_established = true
|
|
171
188
|
@on_joined&.call(self) if first_acknowledgement
|
|
172
189
|
when 'room_opened'
|
|
173
|
-
|
|
190
|
+
# If our streams are not all live yet, the pending announcement covers this (and an early
|
|
191
|
+
# one would be acknowledged into a stream nobody is listening to yet).
|
|
192
|
+
transmit_port_connected if @streams_live
|
|
174
193
|
@on_room_opened&.call(self)
|
|
175
194
|
when 'room_closed'
|
|
176
195
|
leave!
|
|
@@ -192,6 +211,18 @@ module CableRoom
|
|
|
192
211
|
@has_established = false
|
|
193
212
|
@cable_channel._room_memberships << self
|
|
194
213
|
|
|
214
|
+
# port_connected must not go out until every stream below is live on the pubsub adapter.
|
|
215
|
+
# Subscribing is asynchronous and the room acknowledges on the private @token stream, so
|
|
216
|
+
# announcing first lets a fast room — in practice one on another server — reply into a
|
|
217
|
+
# stream nobody is listening to yet; the acknowledgement is lost and the membership never
|
|
218
|
+
# establishes. stream_port (overridden above) counts the streams it opens and the last one
|
|
219
|
+
# to come up announces (_stream_became_live). The generation discards confirmations from
|
|
220
|
+
# a previous connection that land after a rejoin!.
|
|
221
|
+
@stream_generation = (@stream_generation || 0) + 1
|
|
222
|
+
@streams_pending = 0
|
|
223
|
+
@streams_armed = false
|
|
224
|
+
@streams_live = false
|
|
225
|
+
|
|
195
226
|
# Listen to public/broadcast channel
|
|
196
227
|
stream_port(room_class::ROOM_OUT_CHANNEL) do |message|
|
|
197
228
|
handle_received_message(message)
|
|
@@ -217,12 +248,33 @@ module CableRoom
|
|
|
217
248
|
|
|
218
249
|
@preconfigure&.call(self)
|
|
219
250
|
|
|
220
|
-
|
|
251
|
+
# Streams whose adapter confirmed synchronously are already counted down; announce now if
|
|
252
|
+
# that was all of them, otherwise the last confirmation will.
|
|
253
|
+
@streams_armed = true
|
|
254
|
+
_announce_if_streams_live
|
|
221
255
|
|
|
222
256
|
maybe_provision_room
|
|
223
257
|
end
|
|
224
258
|
end
|
|
225
259
|
|
|
260
|
+
def _stream_became_live(generation)
|
|
261
|
+
@mutex.synchronize do
|
|
262
|
+
return unless generation == @stream_generation
|
|
263
|
+
|
|
264
|
+
@streams_pending -= 1
|
|
265
|
+
_announce_if_streams_live
|
|
266
|
+
end
|
|
267
|
+
end
|
|
268
|
+
|
|
269
|
+
# Under @mutex. Sends port_connected once per connection, when every stream opened by
|
|
270
|
+
# initiate_connection has been confirmed live.
|
|
271
|
+
def _announce_if_streams_live
|
|
272
|
+
return if @streams_live || !@streams_armed || @streams_pending > 0 || left?
|
|
273
|
+
|
|
274
|
+
@streams_live = true
|
|
275
|
+
transmit_port_connected
|
|
276
|
+
end
|
|
277
|
+
|
|
226
278
|
def transmit_port_connected
|
|
227
279
|
msg = {
|
|
228
280
|
type: 'port_connected',
|
data/lib/cable_room/version.rb
CHANGED