actioncable-next 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +22 -0
- data/lib/action_cable/channel/streams.rb +12 -1
- data/lib/action_cable/connection/base.rb +5 -1
- data/lib/action_cable/connection/internal_channel.rb +1 -1
- data/lib/action_cable/server/configuration.rb +3 -0
- data/lib/action_cable/server/connections.rb +8 -6
- data/lib/action_cable/server/socket.rb +6 -0
- data/lib/action_cable/subscription_adapter/subscriber_map.rb +16 -1
- data/lib/action_cable.rb +1 -0
- data/lib/actioncable-next.rb +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f5831ed0f565c3bc58899a26ec3097bf47ea7cb518f380e0db05a8bad6fcc5dd
|
4
|
+
data.tar.gz: 92aae3e36883530c9b51ee7acdb0e7df231aed27d60b5f8a4d31426c7158822e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ef8e96d02d595e9004acc936acc8c116d1ac846664dbdf3455b0cfb8f872ba3dbbecfd3dfb3772a53b796505c02a592947bf838690d9c5f0b2c844a40e70e7a4
|
7
|
+
data.tar.gz: bf36b4a97cd76f02d4162a70d27751ae15333d5c1c23654199bd59214dc061b0b779aa9dd28c73ca080f075fc5bc8972db86d6e98ce137c4b78f330bd3e5540c
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,14 @@
|
|
2
2
|
|
3
3
|
## main
|
4
4
|
|
5
|
+
## 0.2.0
|
6
|
+
|
7
|
+
- Performance: store connections in a Hash, not an Array.
|
8
|
+
|
9
|
+
- Add `Connection#broadcast` as an interface for broadcasting from channels.
|
10
|
+
|
11
|
+
- Add `config.fastlane_broadcasts_enabled` to opt-in for optimized broadcasts (no double JSON encoding).
|
12
|
+
|
5
13
|
## 0.1.2
|
6
14
|
|
7
15
|
- Added a hack to prevent third-party extensions from changing the methods visibility.
|
data/README.md
CHANGED
@@ -4,6 +4,8 @@ This gem provides the functionality of the _server adapterization_ PR: [rails/ra
|
|
4
4
|
|
5
5
|
See the PR description for more information on the purpose of this refactoring.
|
6
6
|
|
7
|
+
There are also some additional improvements added on top of the PR (see below).
|
8
|
+
|
7
9
|
## Usage
|
8
10
|
|
9
11
|
Add this line to your application's Gemfile **before Rails or Action Cable**:
|
@@ -26,3 +28,23 @@ by adding the following line to your `spec/rails_helper.rb`:
|
|
26
28
|
require "rspec/rails"
|
27
29
|
require "action_cable/next/rspec"
|
28
30
|
```
|
31
|
+
|
32
|
+
### New features
|
33
|
+
|
34
|
+
#### `ActionCable.server.config.fastlane_broadcasts_enabled = true`
|
35
|
+
|
36
|
+
This option allows you to enable the _fast lane_ for broadcasting messages. When enabled, messages sent via `ActionCable.server.broadcast` are only decoded-encoded from/to JSON once per channel identifier and not for every connected client. This significantly reduces the broadcasting latency (up to 2x faster).
|
37
|
+
|
38
|
+
#### `Connection#broadcast`
|
39
|
+
|
40
|
+
You can now broadcast message from connection or channel instances using the connection `#broadcast` method. For example:
|
41
|
+
|
42
|
+
```ruby
|
43
|
+
class TestChannel < ActionCable::Channel::Base
|
44
|
+
def notify_all(data)
|
45
|
+
connection.broadcast("testing-#{test_id}", data)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
The purpose of this interface is to avoid direct usage of `server` or `ActionCable.server` in channels code.
|
@@ -157,10 +157,21 @@ module ActionCable
|
|
157
157
|
# Always wrap the outermost handler to invoke the user handler on the worker
|
158
158
|
# pool rather than blocking the event loop.
|
159
159
|
def worker_pool_stream_handler(broadcasting, user_handler, coder: nil)
|
160
|
+
if !user_handler && !coder && connection.config.fastlane_broadcasts_enabled
|
161
|
+
return opt_worker_pool_stream_handler(broadcasting)
|
162
|
+
end
|
163
|
+
|
160
164
|
handler = stream_handler(broadcasting, user_handler, coder: coder)
|
161
165
|
|
162
166
|
-> message do
|
163
|
-
connection.perform_work handler, :call, message
|
167
|
+
connection.perform_work handler, :call, message.data
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# Optimized stream handler that avoids double JSON encoding/decoding on broadcast
|
172
|
+
def opt_worker_pool_stream_handler(broadcasting)
|
173
|
+
-> (message) do
|
174
|
+
connection.raw_transmit message.encoded_for(@identifier)
|
164
175
|
end
|
165
176
|
end
|
166
177
|
|
@@ -62,7 +62,7 @@ module ActionCable
|
|
62
62
|
attr_reader :subscriptions, :logger
|
63
63
|
private attr_reader :server, :socket
|
64
64
|
|
65
|
-
delegate :pubsub, :executor, :config, to: :server
|
65
|
+
delegate :pubsub, :executor, :config, :broadcast, to: :server
|
66
66
|
delegate :env, :request, :protocol, :perform_work, to: :socket, allow_nil: true
|
67
67
|
|
68
68
|
def initialize(server, socket)
|
@@ -117,6 +117,10 @@ module ActionCable
|
|
117
117
|
socket.transmit(data)
|
118
118
|
end
|
119
119
|
|
120
|
+
def raw_transmit(data) # :nodoc:
|
121
|
+
socket.raw_transmit(data)
|
122
|
+
end
|
123
|
+
|
120
124
|
# Close the connection.
|
121
125
|
def close(reason: nil, reconnect: true)
|
122
126
|
transmit(
|
@@ -18,7 +18,7 @@ module ActionCable
|
|
18
18
|
|
19
19
|
def subscribe_to_internal_channel
|
20
20
|
if connection_identifier.present?
|
21
|
-
callback = -> (message) { process_internal_message ActiveSupport::JSON.decode(message) }
|
21
|
+
callback = -> (message) { process_internal_message ActiveSupport::JSON.decode(message.data) }
|
22
22
|
@_internal_subscriptions ||= []
|
23
23
|
@_internal_subscriptions << [ internal_channel, callback ]
|
24
24
|
|
@@ -19,6 +19,7 @@ module ActionCable
|
|
19
19
|
attr_accessor :precompile_assets
|
20
20
|
attr_accessor :health_check_path, :health_check_application
|
21
21
|
attr_writer :pubsub_adapter
|
22
|
+
attr_accessor :fastlane_broadcasts_enabled
|
22
23
|
|
23
24
|
def initialize
|
24
25
|
@log_tags = []
|
@@ -31,6 +32,8 @@ module ActionCable
|
|
31
32
|
@allow_same_origin_as_host = true
|
32
33
|
@filter_parameters = []
|
33
34
|
|
35
|
+
@fastlane_broadcasts_enabled = false
|
36
|
+
|
34
37
|
@health_check_application = ->(env) {
|
35
38
|
[200, { Rack::CONTENT_TYPE => "text/html", "date" => Time.now.httpdate }, []]
|
36
39
|
}
|
@@ -13,16 +13,18 @@ module ActionCable
|
|
13
13
|
module Connections # :nodoc:
|
14
14
|
BEAT_INTERVAL = 3
|
15
15
|
|
16
|
-
def
|
17
|
-
@
|
16
|
+
def connections_map
|
17
|
+
@connections_map ||= {}
|
18
18
|
end
|
19
19
|
|
20
|
+
def connections = connections_map.values
|
21
|
+
|
20
22
|
def add_connection(connection)
|
21
|
-
|
23
|
+
connections_map[connection.object_id] = connection
|
22
24
|
end
|
23
25
|
|
24
26
|
def remove_connection(connection)
|
25
|
-
|
27
|
+
connections_map.delete connection.object_id
|
26
28
|
end
|
27
29
|
|
28
30
|
# WebSocket connection implementations differ on when they'll mark a connection
|
@@ -32,12 +34,12 @@ module ActionCable
|
|
32
34
|
# disconnect.
|
33
35
|
def setup_heartbeat_timer
|
34
36
|
@heartbeat_timer ||= executor.timer(BEAT_INTERVAL) do
|
35
|
-
executor.post {
|
37
|
+
executor.post { connections_map.each_value(&:beat) }
|
36
38
|
end
|
37
39
|
end
|
38
40
|
|
39
41
|
def open_connections_statistics
|
40
|
-
|
42
|
+
connections_map.each_value.map(&:statistics)
|
41
43
|
end
|
42
44
|
end
|
43
45
|
end
|
@@ -47,6 +47,12 @@ module ActionCable
|
|
47
47
|
websocket.transmit encode(cable_message)
|
48
48
|
end
|
49
49
|
|
50
|
+
def raw_transmit(message)
|
51
|
+
return unless websocket.alive?
|
52
|
+
|
53
|
+
websocket.transmit message
|
54
|
+
end
|
55
|
+
|
50
56
|
# Close the WebSocket connection.
|
51
57
|
def close(...)
|
52
58
|
websocket.close(...) if websocket.alive?
|
@@ -5,6 +5,19 @@
|
|
5
5
|
module ActionCable
|
6
6
|
module SubscriptionAdapter
|
7
7
|
class SubscriberMap
|
8
|
+
class Message < Struct.new(:data)
|
9
|
+
def initialize(...)
|
10
|
+
super
|
11
|
+
@cache = Concurrent::Map.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def encoded_for(identifier)
|
15
|
+
@cache.compute_if_absent(identifier) do
|
16
|
+
ActiveSupport::JSON.encode({ identifier: identifier, message: ActiveSupport::JSON.decode(data) })
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
8
21
|
def initialize
|
9
22
|
@subscribers = Hash.new { |h, k| h[k] = [] }
|
10
23
|
@sync = Mutex.new
|
@@ -41,8 +54,10 @@ module ActionCable
|
|
41
54
|
@subscribers[channel].dup
|
42
55
|
end
|
43
56
|
|
57
|
+
msg = Message.new(message)
|
58
|
+
|
44
59
|
list.each do |subscriber|
|
45
|
-
invoke_callback(subscriber,
|
60
|
+
invoke_callback(subscriber, msg)
|
46
61
|
end
|
47
62
|
end
|
48
63
|
|
data/lib/action_cable.rb
CHANGED
data/lib/actioncable-next.rb
CHANGED
metadata
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: actioncable-next
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pratik Naik
|
8
8
|
- David Heinemeier Hansson
|
9
9
|
- Vladimir Dementyev
|
10
|
-
autorequire:
|
10
|
+
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2025-
|
13
|
+
date: 2025-02-04 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|
@@ -167,10 +167,10 @@ licenses:
|
|
167
167
|
- MIT
|
168
168
|
metadata:
|
169
169
|
bug_tracker_uri: https://github.com/anycable/actioncable-next
|
170
|
-
changelog_uri: https://github.com/anycable/actioncable-next/blob/v0.
|
170
|
+
changelog_uri: https://github.com/anycable/actioncable-next/blob/v0.2.0/CHANGELOG.md
|
171
171
|
source_code_uri: https://github.com/anycable/actioncable-next
|
172
172
|
rubygems_mfa_required: 'true'
|
173
|
-
post_install_message:
|
173
|
+
post_install_message:
|
174
174
|
rdoc_options: []
|
175
175
|
require_paths:
|
176
176
|
- lib
|
@@ -186,7 +186,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
186
186
|
version: '0'
|
187
187
|
requirements: []
|
188
188
|
rubygems_version: 3.5.22
|
189
|
-
signing_key:
|
189
|
+
signing_key:
|
190
190
|
specification_version: 4
|
191
191
|
summary: Next-gen version of Action Cable
|
192
192
|
test_files: []
|