anycable-rails-core 1.4.1 → 1.4.3

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: d59e7b74ab037c4e2dfdf18b5fb289fad3f7eac9d1a08df798f656ed35dda95c
4
- data.tar.gz: a1304fbd9db3f26c92ef5008b196135a2f09a5922bde5137d7e604e652250354
3
+ metadata.gz: 8ac8286d286d0df2da0d7dba9d8234cf874f1e6bb42a1a4239d026ed928e7dd7
4
+ data.tar.gz: 469e98720377a3d65bddd63f921a3faf78706d1c7f335b447ff0939719d0bd15
5
5
  SHA512:
6
- metadata.gz: 8a7eebcf805f8c72c300b734fe52a7ce952b60527b8644f004ca7bb3414cc04da49b8ee2ce36d84f6f7d8bf0438450dfdffb019610b091894c3dcf7e65ec4897
7
- data.tar.gz: 488065254235777ef04b781ef98f1c20c8c12f2f6c268c6044fb683e4a9774f1ae5abbdcbec53199065e57ec8df1df15d13f85c152471a5a54ceb23e6ddbd832
6
+ metadata.gz: 28eb7aaba4c993e055f40bbfd8f00d52d85f1dc1d95b952891c8ef99122f483b90851d4b5627477f9c63efa581acdd2770acaa13f06ea88c5d4ef0e33b48958f
7
+ data.tar.gz: a89a9448aff1a8e03fc6e95f332b40df1fc295fcc3a406c393e1aeb312fdd947aba2c5cecd9978e03a84e422915e94ef34f7d30effa8398b4b401671d3d6ef8a
data/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 1.4.3 (2023-12-13)
6
+
7
+ - Fix console logging in Rails 7.1 when the app's logger has no broadcast support. ([@palkan][])
8
+
9
+ ## 1.4.2 (2023-10-15)
10
+
11
+ - Print warning if the database pool size is less than RPC pool size. ([@palkan][])
12
+
13
+ - Add support for broadcast options (e.g., `exclude_socket`) and `broadcast ..., to_others: true`. ([@palkan][])
14
+
15
+ - Add `batch_broadcasts` option to automatically batch broadcasts for code wrapped in Rails executor. ([@palkan][])
16
+
17
+ - Fix broadcast logging in Rails 7.1. ([@iuri-gg][])
18
+
5
19
  ## 1.4.1 (2023-09-27)
6
20
 
7
21
  - Fix compatibility with Rails 7.1. ([@palkan][])
@@ -18,8 +18,11 @@ module ActionCable
18
18
  def initialize(*)
19
19
  end
20
20
 
21
- def broadcast(channel, payload)
22
- ::AnyCable.broadcast(channel, payload)
21
+ def broadcast(channel, payload, **options)
22
+ options.merge!(::AnyCable::Rails.current_broadcast_options || {})
23
+ to_others = options.delete(:to_others)
24
+ options[:exclude_socket] ||= ::AnyCable::Rails.current_socket_id if to_others
25
+ ::AnyCable.broadcast(channel, payload, **options.compact)
23
26
  end
24
27
 
25
28
  def subscribe(*)
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_cable"
4
+
5
+ ActionCable::Server::Base.prepend(Module.new do
6
+ def broadcast(channel, payload, **options)
7
+ return super if options.empty?
8
+
9
+ AnyCable::Rails.with_broadcast_options(**options) do
10
+ super(channel, payload)
11
+ end
12
+ end
13
+ end)
14
+
15
+ ActionCable::Channel::Base.singleton_class.prepend(Module.new do
16
+ def broadcast_to(target, payload, **options)
17
+ return super if options.empty?
18
+
19
+ AnyCable::Rails.with_broadcast_options(**options) do
20
+ super(target, payload)
21
+ end
22
+ end
23
+ end)
@@ -10,10 +10,14 @@ require "anyway/rails"
10
10
  # - `persistent_session_enabled` (defaults to false) — whether to store session changes in the connection state
11
11
  # - `embedded` (defaults to false) — whether to run RPC server inside a Rails server process
12
12
  # - `http_rpc_mount_path` (default to nil) — path to mount HTTP RPC server
13
+ # - `batch_broadcasts` (defaults to false) — whether to batch broadcasts automatically for code wrapped with Rails executor
13
14
  AnyCable::Config.attr_config(
14
15
  access_logs_disabled: true,
15
16
  persistent_session_enabled: false,
16
17
  embedded: false,
17
- http_rpc_mount_path: nil
18
+ http_rpc_mount_path: nil,
19
+ batch_broadcasts: false,
20
+ socket_id_header: "X-Socket-ID",
21
+ disable_rpc_pool_size_warning: false
18
22
  )
19
23
  AnyCable::Config.ignore_options :access_logs_disabled, :persistent_session_enabled
@@ -13,16 +13,16 @@ module AnyCable
13
13
  end
14
14
 
15
15
  def call(method, message, metadata)
16
+ sid = metadata["sid"]
17
+
16
18
  if ::Rails.respond_to?(:error)
17
19
  executor.wrap do
18
- sid = metadata["sid"]
19
-
20
20
  ::Rails.error.record(context: {method: method, payload: message.to_h, sid: sid}) do
21
- yield
21
+ Rails.with_socket_id(sid) { yield }
22
22
  end
23
23
  end
24
24
  else
25
- executor.wrap { yield }
25
+ executor.wrap { Rails.with_socket_id(sid) { yield } }
26
26
  end
27
27
  end
28
28
  end
@@ -3,6 +3,7 @@
3
3
  require "anycable/rails/action_cable_ext/connection"
4
4
  require "anycable/rails/action_cable_ext/channel"
5
5
  require "anycable/rails/action_cable_ext/remote_connections"
6
+ require "anycable/rails/action_cable_ext/broadcast_options"
6
7
 
7
8
  require "anycable/rails/channel_state"
8
9
  require "anycable/rails/connection_factory"
@@ -30,10 +31,12 @@ module AnyCable
30
31
  console.level = ::Rails.logger.level if ::Rails.logger.respond_to?(:level)
31
32
 
32
33
  # Rails 7.1+
33
- if defined?(ActiveSupport::BroadcastLogger)
34
- AnyCable.logger = ActiveSupport::BroadcastLogger.new(AnyCable.logger, console)
35
- else
34
+ if AnyCable.logger.respond_to?(:broadcast_to)
35
+ AnyCable.logger.broadcast_to(console)
36
+ elsif ActiveSupport::Logger.respond_to?(:broadcast)
36
37
  AnyCable.logger.extend(ActiveSupport::Logger.broadcast(console))
38
+ elsif defined?(ActiveSupport::BroadcastLogger)
39
+ AnyCable.logger = ActiveSupport::BroadcastLogger.new(AnyCable.logger, console)
37
40
  end
38
41
  end
39
42
 
@@ -59,6 +62,27 @@ module AnyCable
59
62
  ::Rails.error.report(ex, handled: false, context: {method: method.to_sym, payload: message})
60
63
  end
61
64
  end
65
+
66
+ if AnyCable.config.batch_broadcasts?
67
+ if AnyCable.broadcast_adapter.respond_to?(:start_batching)
68
+ app.executor.to_run { AnyCable.broadcast_adapter.start_batching }
69
+ app.executor.to_complete { AnyCable.broadcast_adapter.finish_batching }
70
+ else
71
+ warn "[AnyCable] Auto-batching is enabled for broadcasts but your anycable version doesn't support it. Please, upgrade"
72
+ end
73
+ end
74
+ end
75
+
76
+ initializer "anycable.socket_id_tracking" do
77
+ ActiveSupport.on_load(:action_controller) do
78
+ require "anycable/rails/socket_id_tracking"
79
+ include AnyCable::Rails::SocketIdTrackingController
80
+ end
81
+
82
+ ActiveSupport.on_load(:active_job) do
83
+ require "anycable/rails/socket_id_tracking"
84
+ include AnyCable::Rails::SocketIdTrackingJob
85
+ end
62
86
  end
63
87
 
64
88
  initializer "anycable.connection_factory", after: "action_cable.set_configs" do |app|
@@ -86,6 +110,27 @@ module AnyCable
86
110
  end
87
111
  end
88
112
 
113
+ initializer "anycable.verify_pool_sizes" do
114
+ next if AnyCable.config.disable_rpc_pool_size_warning?
115
+ # Skip if non-gRPC server is used
116
+ next unless AnyCable.config.respond_to?(:rpc_pool_size)
117
+
118
+ # Log current db vs. gRPC pool sizes
119
+ AnyCable.configure_server do
120
+ ActiveSupport.on_load(:active_record) do
121
+ db_pool_size = ::ActiveRecord::Base.connection_pool.size
122
+ rpc_pool_size = AnyCable.config.rpc_pool_size
123
+
124
+ if rpc_pool_size > db_pool_size
125
+ ::Kernel.warn(
126
+ "\n⛔️ WARNING: AnyCable RPC pool size (#{rpc_pool_size}) is greater than DB pool size (#{db_pool_size})\n" \
127
+ "Please, consider adjusting the database pool size to avoid connection wait times and increase throughput of your RPC server\n\n"
128
+ )
129
+ end
130
+ end
131
+ end
132
+ end
133
+
89
134
  # Since Rails 6.1
90
135
  if respond_to?(:server)
91
136
  server do
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ module AnyCable
4
+ module Rails
5
+ module SocketIdTrackingController
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ around_action :anycable_tracking_socket_id
10
+ end
11
+
12
+ private
13
+
14
+ def anycable_tracking_socket_id(&block)
15
+ Rails.with_socket_id(request.headers[AnyCable.config.socket_id_header], &block)
16
+ end
17
+ end
18
+
19
+ module SocketIdTrackingJob
20
+ extend ActiveSupport::Concern
21
+
22
+ attr_accessor :cable_socket_id
23
+
24
+ def serialize
25
+ return super unless Rails.current_socket_id
26
+
27
+ super.merge("cable_socket_id" => Rails.current_socket_id)
28
+ end
29
+
30
+ def deserialize(job_data)
31
+ super
32
+ self.cable_socket_id = job_data["cable_socket_id"]
33
+ end
34
+
35
+ included do
36
+ around_perform do |job, block|
37
+ Rails.with_socket_id(job.cable_socket_id, &block)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module AnyCable
4
4
  module Rails
5
- VERSION = "1.4.1"
5
+ VERSION = "1.4.3"
6
6
  end
7
7
  end
@@ -6,6 +6,7 @@ require "anycable/rails/config"
6
6
  require "anycable/rails/rack"
7
7
 
8
8
  require "globalid"
9
+ require "active_support/core_ext/module/attribute_accessors_per_thread"
9
10
 
10
11
  module AnyCable
11
12
  # Rails handler for AnyCable
@@ -14,6 +15,9 @@ module AnyCable
14
15
 
15
16
  ADAPTER_ALIASES = %w[any_cable anycable].freeze
16
17
 
18
+ thread_mattr_accessor :current_socket_id
19
+ thread_mattr_accessor :current_broadcast_options
20
+
17
21
  class << self
18
22
  def enabled?
19
23
  adapter = ::ActionCable.server.config.cable&.fetch("adapter", nil)
@@ -24,6 +28,29 @@ module AnyCable
24
28
  ADAPTER_ALIASES.include?(adapter)
25
29
  end
26
30
 
31
+ def with_socket_id(socket_id)
32
+ old_socket_id, self.current_socket_id = current_socket_id, socket_id
33
+ yield
34
+ ensure
35
+ self.current_socket_id = old_socket_id
36
+ end
37
+
38
+ def with_broadcast_options(**options)
39
+ old_options = current_broadcast_options
40
+ self.current_broadcast_options = options.reverse_merge(old_options || {})
41
+ yield
42
+ ensure
43
+ self.current_broadcast_options = old_options
44
+ end
45
+
46
+ def broadcasting_to_others(socket_id: nil, &block)
47
+ if socket_id
48
+ with_socket_id(socket_id) { with_broadcast_options(to_others: true, &block) }
49
+ else
50
+ with_broadcast_options(to_others: true, &block)
51
+ end
52
+ end
53
+
27
54
  # Serialize connection/channel state variable to string
28
55
  # using GlobalID where possible or JSON (if json: true)
29
56
  def serialize(obj, json: false)
@@ -50,9 +77,9 @@ module AnyCable
50
77
  end
51
78
 
52
79
  module Extension
53
- def broadcast(channel, payload)
80
+ def broadcast(...)
54
81
  super
55
- ::AnyCable.broadcast(channel, payload)
82
+ ::AnyCable.broadcast(...)
56
83
  end
57
84
  end
58
85
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: anycable-rails-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.1
4
+ version: 1.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - palkan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-09-28 00:00:00.000000000 Z
11
+ date: 2023-12-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: anycable-core
@@ -66,6 +66,7 @@ files:
66
66
  - lib/action_cable/subscription_adapter/anycable.rb
67
67
  - lib/anycable-rails.rb
68
68
  - lib/anycable/rails.rb
69
+ - lib/anycable/rails/action_cable_ext/broadcast_options.rb
69
70
  - lib/anycable/rails/action_cable_ext/channel.rb
70
71
  - lib/anycable/rails/action_cable_ext/connection.rb
71
72
  - lib/anycable/rails/action_cable_ext/remote_connections.rb
@@ -86,6 +87,7 @@ files:
86
87
  - lib/anycable/rails/middlewares/log_tagging.rb
87
88
  - lib/anycable/rails/rack.rb
88
89
  - lib/anycable/rails/railtie.rb
90
+ - lib/anycable/rails/socket_id_tracking.rb
89
91
  - lib/anycable/rails/version.rb
90
92
  - lib/generators/anycable/download/USAGE
91
93
  - lib/generators/anycable/download/download_generator.rb
@@ -122,7 +124,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
122
124
  - !ruby/object:Gem::Version
123
125
  version: '0'
124
126
  requirements: []
125
- rubygems_version: 3.4.8
127
+ rubygems_version: 3.4.20
126
128
  signing_key:
127
129
  specification_version: 4
128
130
  summary: AnyCable integration for Rails (w/o RPC dependencies)