cable_room 0.7.0.beta2 → 0.8.0.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/README.md +0 -1256
- data/cable_room.gemspec +2 -5
- data/lib/cable_room/host/action_cable_inbound.rb +35 -0
- data/lib/cable_room/host/runner.rb +108 -341
- data/lib/cable_room/host.rb +18 -364
- data/lib/cable_room/ports.rb +9 -19
- data/lib/cable_room/railtie.rb +11 -3
- data/lib/cable_room/room/base.rb +41 -40
- data/lib/cable_room/room/host_adapter.rb +4 -9
- data/lib/cable_room/room/lifecycle.rb +6 -23
- data/lib/cable_room/room/port_management.rb +0 -50
- data/lib/cable_room/room/reaping.rb +0 -33
- data/lib/cable_room/room/user_management.rb +0 -27
- data/lib/cable_room/room.rb +1 -4
- data/lib/cable_room/room_member.rb +84 -275
- data/lib/cable_room/room_proxy_channel.rb +2 -13
- data/lib/cable_room/version.rb +1 -1
- data/lib/cable_room.rb +0 -55
- metadata +7 -21
- data/CHANGELOG.md +0 -122
- data/exe/cable_room +0 -8
- data/lib/cable_room/broadcaster.rb +0 -116
- data/lib/cable_room/bus.rb +0 -372
- data/lib/cable_room/cli.rb +0 -237
- data/lib/cable_room/config.rb +0 -112
- data/lib/cable_room/host/bus_inbound.rb +0 -36
- data/lib/cable_room/host/supervisor.rb +0 -275
- data/lib/cable_room/membership_store.rb +0 -105
- data/lib/cable_room/migration.rb +0 -586
- data/lib/cable_room/placement.rb +0 -260
- data/lib/cable_room/room/snapshotting.rb +0 -82
- data/lib/cable_room/room_harness.rb +0 -168
- data/lib/cable_room/snapshot.rb +0 -136
|
@@ -23,27 +23,22 @@ module CableRoom
|
|
|
23
23
|
end
|
|
24
24
|
end
|
|
25
25
|
|
|
26
|
-
# The room
|
|
27
|
-
#
|
|
28
|
-
# subscription is confirmed, the same as it does for members.
|
|
26
|
+
# The room side of Ports#stream_port: listen on this room's inbound stream for `port`.
|
|
27
|
+
# `on_live` runs once the subscription is confirmed.
|
|
29
28
|
def stream_port(port, auto_close: true, on_live: nil, &blk)
|
|
30
|
-
@runner.subscribe(
|
|
29
|
+
@runner.subscribe(room_port_key(port), coder: ActiveSupport::JSON, on_live: on_live, &blk)
|
|
31
30
|
_streamed_ports << port if auto_close
|
|
32
31
|
end
|
|
33
32
|
|
|
34
33
|
def close_streamed_ports!
|
|
35
34
|
_streamed_ports.each do |port|
|
|
36
|
-
@runner.unsubscribe(
|
|
35
|
+
@runner.unsubscribe(room_port_key(port))
|
|
37
36
|
end
|
|
38
37
|
_streamed_ports.clear
|
|
39
38
|
end
|
|
40
39
|
|
|
41
40
|
protected
|
|
42
41
|
|
|
43
|
-
def inbound_channel(port)
|
|
44
|
-
self.class.inbound_channel(key, port)
|
|
45
|
-
end
|
|
46
|
-
|
|
47
42
|
def start_periodic_timer(callback, every:)
|
|
48
43
|
@runner.start_periodic_timer(-> { instance_exec(&callback) }, every: every)
|
|
49
44
|
end
|
|
@@ -5,10 +5,8 @@ module CableRoom
|
|
|
5
5
|
|
|
6
6
|
included do
|
|
7
7
|
after_startup do
|
|
8
|
-
logger.info
|
|
9
|
-
|
|
10
|
-
# would make every one of them re-announce for nothing.
|
|
11
|
-
self << { type: 'room_opened' } unless restored?
|
|
8
|
+
logger.info "Started"
|
|
9
|
+
self << { type: 'room_opened' }
|
|
12
10
|
end
|
|
13
11
|
|
|
14
12
|
before_shutdown do
|
|
@@ -27,8 +25,7 @@ module CableRoom
|
|
|
27
25
|
@runner.state
|
|
28
26
|
end
|
|
29
27
|
|
|
30
|
-
# Requests that the Room shut down gracefully, processing any pending messages
|
|
31
|
-
# room (mid-migration) shuts down the same way; that's the "no peer took it" path.
|
|
28
|
+
# Requests that the Room shut down gracefully, processing any pending messages
|
|
32
29
|
def shutdown!(reason = "Room requested shutdown")
|
|
33
30
|
@shutdown_reason = reason
|
|
34
31
|
logger.info "Shutdown requested: #{reason}"
|
|
@@ -42,24 +39,10 @@ module CableRoom
|
|
|
42
39
|
|
|
43
40
|
private
|
|
44
41
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
def _shutdown_reason=(reason)
|
|
48
|
-
@shutdown_reason = reason
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# The startup chain. A restore (see Snapshotting#_restore) runs the same callbacks — they
|
|
52
|
-
# are what subscribe the inbound port and start the reapers — but swaps `startup` for
|
|
53
|
-
# `restore_state(app_state)` when the room defines it.
|
|
54
|
-
def _startup(restoring: false, app_state: nil)
|
|
55
|
-
event = restoring ? "room_restored.cable_room" : "room_opened.cable_room"
|
|
56
|
-
ActiveSupport::Notifications.instrument(event, { room: self }) do
|
|
42
|
+
def _startup
|
|
43
|
+
ActiveSupport::Notifications.instrument("room_opened.cable_room", { room: self }) do
|
|
57
44
|
run_callbacks :startup do
|
|
58
|
-
if
|
|
59
|
-
restore_state(app_state)
|
|
60
|
-
else
|
|
61
|
-
startup if respond_to?(:startup)
|
|
62
|
-
end
|
|
45
|
+
startup if respond_to?(:startup)
|
|
63
46
|
end
|
|
64
47
|
end
|
|
65
48
|
end
|
|
@@ -158,29 +158,6 @@ module CableRoom
|
|
|
158
158
|
end
|
|
159
159
|
end
|
|
160
160
|
|
|
161
|
-
# -- Snapshot and restore (see CableRoom::Snapshot) ----------------------------------------
|
|
162
|
-
|
|
163
|
-
def _snapshot_port_clients
|
|
164
|
-
@_port_clients.values.map(&:to_snapshot)
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
# Re-create every port exactly as it was, without running port_connected callbacks: the
|
|
168
|
-
# members are still there and were already acknowledged. A port whose user can't be found
|
|
169
|
-
# any more (its record was deleted) is dropped rather than failing the whole room; that
|
|
170
|
-
# member will be re-acknowledged when it next pings.
|
|
171
|
-
def _restore_port_clients(entries)
|
|
172
|
-
Array(entries).each do |entry|
|
|
173
|
-
client = PortClient.new(self, entry["token"])
|
|
174
|
-
begin
|
|
175
|
-
client.restore!(entry)
|
|
176
|
-
rescue ::ActiveJob::DeserializationError => e
|
|
177
|
-
logger.warn "Dropping port #{entry['token']} from the snapshot: #{e.message}"
|
|
178
|
-
next
|
|
179
|
-
end
|
|
180
|
-
@_port_clients[client.token] = client
|
|
181
|
-
end
|
|
182
|
-
end
|
|
183
|
-
|
|
184
161
|
def check_port_inactivity
|
|
185
162
|
return unless @_port_clients
|
|
186
163
|
|
|
@@ -238,33 +215,6 @@ module CableRoom
|
|
|
238
215
|
def tag!(*tags)
|
|
239
216
|
self[:tags].merge(tags.flatten.map(&:to_sym))
|
|
240
217
|
end
|
|
241
|
-
|
|
242
|
-
SNAPSHOT_OWN_KEYS = %w[tags as last_seen_at].freeze
|
|
243
|
-
|
|
244
|
-
# This port as one `port_clients` entry of a CableRoom::Snapshot. `as` goes through the
|
|
245
|
-
# same serializer the member used to send it (a GlobalID for a record). Everything else on
|
|
246
|
-
# the port — what the member's `extra:` merged in, plus anything the room stored with
|
|
247
|
-
# `message_origin[:x] = ...` — travels under `metadata`, the same way, so records survive.
|
|
248
|
-
def to_snapshot
|
|
249
|
-
metadata = @metadata.to_h.except(*SNAPSHOT_OWN_KEYS)
|
|
250
|
-
{
|
|
251
|
-
token: token,
|
|
252
|
-
tags: self[:tags].map(&:to_s),
|
|
253
|
-
as: Snapshot.serialize_argument(self[:as]),
|
|
254
|
-
last_seen_at: Snapshot.encode_time(self[:last_seen_at]),
|
|
255
|
-
metadata: Snapshot.serialize_argument(metadata),
|
|
256
|
-
}
|
|
257
|
-
end
|
|
258
|
-
|
|
259
|
-
# The inverse of `to_snapshot`. `last_seen_at` is kept, not reset: the port is exactly as
|
|
260
|
-
# old as it was, and its member's next ping refreshes it as usual.
|
|
261
|
-
def restore!(entry)
|
|
262
|
-
merge!(Snapshot.deserialize_argument(entry["metadata"]))
|
|
263
|
-
tag!(entry["tags"])
|
|
264
|
-
self[:as] = Snapshot.deserialize_argument(entry["as"])
|
|
265
|
-
self[:last_seen_at] = Snapshot.decode_time(entry["last_seen_at"]) || self[:last_seen_at]
|
|
266
|
-
self
|
|
267
|
-
end
|
|
268
218
|
end
|
|
269
219
|
end
|
|
270
220
|
end
|
|
@@ -74,39 +74,6 @@ module CableRoom
|
|
|
74
74
|
ping_watchdog
|
|
75
75
|
end
|
|
76
76
|
|
|
77
|
-
# -- Snapshot and restore (see CableRoom::Snapshot) ----------------------------------------
|
|
78
|
-
|
|
79
|
-
# One entry per `reap_when`, in declaration order. `last_keep_at` is the wall-clock moment
|
|
80
|
-
# the reaper's grace period started counting from (nil until it has run once), so the
|
|
81
|
-
# deadline — `last_keep_at + grace` — is the same on whichever host restores it.
|
|
82
|
-
def _snapshot_reaper_state
|
|
83
|
-
self.class.reaper_checkers.each_with_index.map do |cfg, index|
|
|
84
|
-
state = _reaper_states[cfg] || {}
|
|
85
|
-
{ key: cfg[:key]&.to_s, index: index, last_keep_at: Snapshot.encode_time(state[:last_keep_at]) }
|
|
86
|
-
end
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
# Runs before the startup callbacks, which add the timer to each state and leave the rest
|
|
90
|
-
# alone. A named reaper matches by name and an anonymous one by position, so a Room whose
|
|
91
|
-
# reapers changed between the two hosts' code versions keeps what still lines up and drops
|
|
92
|
-
# the rest.
|
|
93
|
-
def _restore_reaper_state(entries)
|
|
94
|
-
checkers = self.class.reaper_checkers
|
|
95
|
-
Array(entries).each do |entry|
|
|
96
|
-
cfg =
|
|
97
|
-
if entry["key"].present?
|
|
98
|
-
checkers.find { |c| c[:key].to_s == entry["key"] }
|
|
99
|
-
else
|
|
100
|
-
candidate = checkers[entry["index"].to_i]
|
|
101
|
-
candidate if candidate && candidate[:key].nil?
|
|
102
|
-
end
|
|
103
|
-
next unless cfg
|
|
104
|
-
|
|
105
|
-
last_keep_at = Snapshot.decode_time(entry["last_keep_at"])
|
|
106
|
-
(_reaper_states[cfg] ||= {})[:last_keep_at] = last_keep_at if last_keep_at
|
|
107
|
-
end
|
|
108
|
-
end
|
|
109
|
-
|
|
110
77
|
private
|
|
111
78
|
|
|
112
79
|
def _reaper_states
|
|
@@ -98,33 +98,6 @@ module CableRoom
|
|
|
98
98
|
tags
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
-
# -- Snapshot and restore (see CableRoom::Snapshot) ----------------------------------------
|
|
102
|
-
|
|
103
|
-
# The user map is carried on its own rather than rebuilt from the ports: a port whose join
|
|
104
|
-
# the tag policy refused has no user entry, and a rebuild would invent one.
|
|
105
|
-
def _snapshot_user_state
|
|
106
|
-
@_user_map_mutex.synchronize do
|
|
107
|
-
@_user_state_map.map do |user, usm|
|
|
108
|
-
{ user: Snapshot.serialize_argument(user), port_tokens: usm[:port_tokens].to_a }
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
|
|
113
|
-
# No user_joined callbacks here: these users joined long ago, on the previous host.
|
|
114
|
-
def _restore_user_state(entries)
|
|
115
|
-
@_user_map_mutex.synchronize do
|
|
116
|
-
Array(entries).each do |entry|
|
|
117
|
-
begin
|
|
118
|
-
user = Snapshot.deserialize_argument(entry["user"])
|
|
119
|
-
rescue ::ActiveJob::DeserializationError => e
|
|
120
|
-
logger.warn "Dropping a user from the snapshot: #{e.message}"
|
|
121
|
-
next
|
|
122
|
-
end
|
|
123
|
-
@_user_state_map[user] = { port_tokens: Set.new(entry["port_tokens"]) }
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
|
|
128
101
|
def _apply_port_scope(user: nil, tag: nil, **kwargs)
|
|
129
102
|
if user && tag
|
|
130
103
|
user_tags = all_user_tags(user) || []
|
data/lib/cable_room/room.rb
CHANGED
|
@@ -10,7 +10,6 @@ module CableRoom
|
|
|
10
10
|
autoload :HostAdapter
|
|
11
11
|
|
|
12
12
|
autoload :Lifecycle
|
|
13
|
-
autoload :Snapshotting
|
|
14
13
|
autoload :Reaping
|
|
15
14
|
autoload :InputHandling
|
|
16
15
|
|
|
@@ -21,10 +20,8 @@ module CableRoom
|
|
|
21
20
|
autoload :Broadcasting
|
|
22
21
|
end
|
|
23
22
|
|
|
24
|
-
# Every room running in this process. Asking never creates a Host, so a process that hosts
|
|
25
|
-
# no rooms (a web process in :remote) just gets an empty list.
|
|
26
23
|
def self.locally_open_rooms
|
|
27
|
-
Host.
|
|
24
|
+
Host.instance.rooms
|
|
28
25
|
end
|
|
29
26
|
end
|
|
30
27
|
end
|