anycable-core 1.3.0 → 1.4.0.pre.rc.1

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: 215d37d67fe10f536e6eb2a9b8ae5e53603d4653fb2034aff9abece234da7097
4
- data.tar.gz: 50457dfd126925e875f3bb1ab323df90a0381f13de399f07e20a8cd4f2cab5d7
3
+ metadata.gz: 4ffabf719d153bb5390a13125389bf92b8d11a1b0a0f500774a95f50fd3cee14
4
+ data.tar.gz: 9db169beafb99939716eca55267c0b1df57fd1a91dd6f98edc156070b5809b6f
5
5
  SHA512:
6
- metadata.gz: 43eac0f870ba33726095d8801772d3b18dd2a909a937c9c03177aab163d2ee93f92979bcfcf4cb3eb2039e80e3f28ec123d9e215ee07fe4b87fb152510f7b87d
7
- data.tar.gz: be1a5de79d480aa5d0b081354103f325cb3f997d8d134ba325b92d233408696236afedd600c7133ccb307ac7ceb9b16c60e284588942aa83886007384002a372
6
+ metadata.gz: bbfa0d23d3fcd4d395b66845169f286edf5c087303581c83f5163d647b8e5955fb3902139592e2ad15a469a00d619c99e70d3213195e1a52d08cb52c528a1520
7
+ data.tar.gz: dd417c36b177f39aa2c8ac0ce72d0f5fe56197dfa3432d8242f074733c3d6fe8fd4085334c39df14f5afb71937acab1e12d96b5dbf2105a45177698d795d0f4b
data/CHANGELOG.md CHANGED
@@ -2,6 +2,20 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 1.4.0-rc.1 (2023-06-01)
6
+
7
+ - Add `redisx` broadcast adapter. ([@palkan][])
8
+
9
+ A next-gen (Broker-compatible) broadcasting adapter using Redis Streams.
10
+
11
+ ## 1.3.1 (2023-05-12)
12
+
13
+ - Fix gRPC health check response for an empty service. ([@palkan][])
14
+
15
+ As per [docs](https://github.com/grpc/grpc/blob/master/doc/health-checking.md), an empty service is used as the key for server's overall health status. So, if we serve any services, we should return `SERVING` status.
16
+
17
+ - Add gRPC health check when using grpc_kit. ([@palkan][])
18
+
5
19
  ## 1.3.0 (2023-02-28)
6
20
 
7
21
  - Add configuration presets. ([@palkan][])
data/README.md CHANGED
@@ -28,6 +28,8 @@ Check out our 📑 [Documentation](https://docs.anycable.io/v1).
28
28
 
29
29
  ## Links
30
30
 
31
+ - [AnyCable off Rails: connecting Twilio streams with Hanami](https://evilmartians.com/chronicles/anycable-goes-off-rails-connecting-twilio-streams-with-hanami)
32
+
31
33
  - [AnyCable 1.0: Four years of real-time web with Ruby and Go](https://evilmartians.com/chronicles/anycable-1-0-four-years-of-real-time-web-with-ruby-and-go)
32
34
 
33
35
  - [AnyCable: Action Cable on steroids!](https://evilmartians.com/chronicles/anycable-actioncable-on-steroids)
@@ -118,7 +118,7 @@ module AnyCable
118
118
  yield http
119
119
  rescue Timeout::Error, *RECOVERABLE_EXCEPTIONS => e
120
120
  retry_count += 1
121
- if MAX_ATTEMPTS < retry_count
121
+ if retry_count > MAX_ATTEMPTS
122
122
  logger.error("Broadcast request failed: #{e.message}")
123
123
  return
124
124
  end
@@ -31,6 +31,7 @@ module AnyCable
31
31
  )
32
32
  options = AnyCable.config.to_nats_params.merge(options)
33
33
  @nats_conn = ::NATS.connect(nil, options)
34
+ setup_listeners(nats_conn)
34
35
  @channel = channel
35
36
  end
36
37
 
@@ -41,6 +42,19 @@ module AnyCable
41
42
  def announce!
42
43
  logger.info "Broadcasting NATS channel: #{channel}"
43
44
  end
45
+
46
+ private
47
+
48
+ def setup_listeners(nats_client)
49
+ nats_client.on_disconnect { logger.info "NATS client disconnected" }
50
+ nats_client.on_reconnect do
51
+ info = nats_client.server_info
52
+ logger.info "NATS client reconnected: host=#{info[:host]}:#{info[:port]} cluster=#{info[:cluster]}"
53
+ end
54
+ nats_client.on_error do |err|
55
+ logger.warn "NATS client error: #{err.message}"
56
+ end
57
+ end
44
58
  end
45
59
  end
46
60
  end
@@ -21,7 +21,7 @@ module AnyCable
21
21
  #
22
22
  # You can override these params:
23
23
  #
24
- # AnyCable.broadcast_adapter = :redis, url: "redis://my_redis", channel: "_any_cable_"
24
+ # AnyCable.broadcast_adapter = :redis, { url: "redis://my_redis", channel: "_any_cable_" }
25
25
  class Redis < Base
26
26
  attr_reader :redis_conn, :channel
27
27
 
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "anycable/broadcast_adapters/redis"
4
+
5
+ module AnyCable
6
+ module BroadcastAdapters
7
+ # Next-gen Redis adapter for broadcasting over Redis streams.
8
+ #
9
+ # Unlike Redis adapter, RedisX adapter delivers each broadcast message only
10
+ # to a single WS server, which is responsible for re-broadcasting it within the cluster.
11
+ # It's required for the Broker (hot streams cache) support.
12
+ #
13
+ # Example:
14
+ #
15
+ # AnyCable.broadcast_adapter = :redisx
16
+ #
17
+ # It uses Redis configuration from global AnyCable config
18
+ # by default.
19
+ # NOTE: The `redis_channel` config param is used as a stream name.
20
+ #
21
+ # You can override these params:
22
+ #
23
+ # AnyCable.broadcast_adapter = :redisx, { url: "redis://my_redis", stream_name: "_any_cable_" }
24
+ class Redisx < Redis
25
+ def raw_broadcast(payload)
26
+ redis_conn.xadd(channel, {payload: payload})
27
+ end
28
+
29
+ def announce!
30
+ logger.info "Broadcasting Redis stream: #{channel}"
31
+ end
32
+ end
33
+ end
34
+ end
@@ -27,7 +27,7 @@ module AnyCable
27
27
  broadcast_adapter: :redis,
28
28
 
29
29
  ### Redis options
30
- redis_url: ENV.fetch("REDIS_URL", "redis://localhost:6379/5"),
30
+ redis_url: ENV.fetch("REDIS_URL", "redis://localhost:6379"),
31
31
  redis_sentinels: nil,
32
32
  redis_channel: "__anycable__",
33
33
  redis_tls_verify: false,
@@ -71,7 +71,7 @@ module AnyCable
71
71
  flag_options :debug, :nats_dont_randomize_servers
72
72
  ignore_options :nats_options
73
73
 
74
- def load(*)
74
+ def load(*_args)
75
75
  super.tap { load_presets }
76
76
  end
77
77
 
@@ -98,7 +98,7 @@ module AnyCable
98
98
  )
99
99
  health_checker.add_status(
100
100
  "",
101
- Grpc::Health::V1::HealthCheckResponse::ServingStatus::NOT_SERVING
101
+ Grpc::Health::V1::HealthCheckResponse::ServingStatus::SERVING
102
102
  )
103
103
  health_checker
104
104
  end
@@ -0,0 +1,72 @@
1
+ # Copyright 2015 gRPC authors.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Grpc
16
+ # Health contains classes and modules that support providing a health check
17
+ # service.
18
+ module Health
19
+ # Checker is implementation of the schema-specified health checking service.
20
+ class Checker < V1::Health::Service
21
+ StatusCodes = GRPC::Core::StatusCodes
22
+ HealthCheckResponse = V1::HealthCheckResponse
23
+
24
+ # Initializes the statuses of participating services
25
+ def initialize
26
+ @statuses = {}
27
+ @status_mutex = Mutex.new # guards access to @statuses
28
+ end
29
+
30
+ # Implements the rpc IDL API method
31
+ def check(req, _call)
32
+ status = nil
33
+ @status_mutex.synchronize do
34
+ status = @statuses["#{req.service}"]
35
+ end
36
+ if status.nil?
37
+ fail GRPC::NotFound.new("Service is not found: #{req.service}")
38
+ end
39
+ HealthCheckResponse.new(status: status)
40
+ end
41
+
42
+ # Adds the health status for a given service.
43
+ def add_status(service, status)
44
+ @status_mutex.synchronize { @statuses["#{service}"] = status }
45
+ end
46
+
47
+ # Adds given health status for all given services
48
+ def set_status_for_services(status, *services)
49
+ @status_mutex.synchronize do
50
+ services.each { |service| @statuses["#{service}"] = status }
51
+ end
52
+ end
53
+
54
+ # Adds health status for each service given within hash
55
+ def add_statuses(service_statuses = {})
56
+ @status_mutex.synchronize do
57
+ service_statuses.each_pair { |service, status| @statuses["#{service}"] = status }
58
+ end
59
+ end
60
+
61
+ # Clears the status for the given service.
62
+ def clear_status(service)
63
+ @status_mutex.synchronize { @statuses.delete("#{service}") }
64
+ end
65
+
66
+ # Clears alls the statuses.
67
+ def clear_all
68
+ @status_mutex.synchronize { @statuses = {} }
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
4
+ # source: grpc/health/v1/health.proto
5
+
6
+ require 'google/protobuf'
7
+
8
+ Google::Protobuf::DescriptorPool.generated_pool.build do
9
+ add_message "grpc.health.v1.HealthCheckRequest" do
10
+ optional :service, :string, 1
11
+ end
12
+ add_message "grpc.health.v1.HealthCheckResponse" do
13
+ optional :status, :enum, 1, "grpc.health.v1.HealthCheckResponse.ServingStatus"
14
+ end
15
+ add_enum "grpc.health.v1.HealthCheckResponse.ServingStatus" do
16
+ value :UNKNOWN, 0
17
+ value :SERVING, 1
18
+ value :NOT_SERVING, 2
19
+ end
20
+ end
21
+
22
+ module Grpc
23
+ module Health
24
+ module V1
25
+ HealthCheckRequest = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.health.v1.HealthCheckRequest").msgclass
26
+ HealthCheckResponse = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.health.v1.HealthCheckResponse").msgclass
27
+ HealthCheckResponse::ServingStatus = Google::Protobuf::DescriptorPool.generated_pool.lookup("grpc.health.v1.HealthCheckResponse.ServingStatus").enummodule
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,41 @@
1
+ # Generated by the protocol buffer compiler. DO NOT EDIT!
2
+ # Source: grpc/health/v1/health.proto for package 'grpc.health.v1'
3
+ # Original file comments:
4
+ # Copyright 2015 The gRPC Authors
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ # The canonical version of this proto can be found at
19
+ # https://github.com/grpc/grpc-proto/blob/master/grpc/health/v1/health.proto
20
+ #
21
+
22
+ module Grpc
23
+ module Health
24
+ module V1
25
+ module Health
26
+ class Service
27
+
28
+ include GRPC::GenericService
29
+
30
+ self.marshal_class_method = :encode
31
+ self.unmarshal_class_method = :decode
32
+ self.service_name = 'grpc.health.v1.Health'
33
+
34
+ rpc :Check, HealthCheckRequest, HealthCheckResponse
35
+ end
36
+
37
+ Stub = Service.rpc_stub_class
38
+ end
39
+ end
40
+ end
41
+ end
@@ -2,6 +2,10 @@
2
2
 
3
3
  require "anycable/grpc/handler"
4
4
 
5
+ require_relative "./health_pb"
6
+ require_relative "./health_services_pb"
7
+ require_relative "./health_checker"
8
+
5
9
  module AnyCable
6
10
  module GRPC
7
11
  raise LoadError, "AnyCable::GRPC::Server has been already loaded!" if defined?(AnyCable::GRPC::Server)
@@ -77,8 +81,8 @@ module AnyCable
77
81
 
78
82
  loop do
79
83
  sock = TCPSocket.new(hostname, port, connect_timeout: 1)
80
- stub = AnyCable::GRPC::Stub.new(sock)
81
- stub.connect(AnyCable::ConnectionRequest.new(env: {}))
84
+ stub = ::Grpc::Health::V1::Health::Stub.new(sock)
85
+ stub.check(::Grpc::Health::V1::HealthCheckRequest.new)
82
86
  sock.close
83
87
  break
84
88
  rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, SocketError
@@ -126,8 +130,22 @@ module AnyCable
126
130
 
127
131
  ::GrpcKit::Server.new(min_pool_size: pool_size, max_pool_size: pool_size).tap do |server|
128
132
  server.handle(AnyCable::GRPC::Handler)
133
+ server.handle(build_health_checker)
129
134
  end
130
135
  end
136
+
137
+ def build_health_checker
138
+ health_checker = ::Grpc::Health::Checker.new
139
+ health_checker.add_status(
140
+ "anycable.RPC",
141
+ ::Grpc::Health::V1::HealthCheckResponse::ServingStatus::SERVING
142
+ )
143
+ health_checker.add_status(
144
+ "",
145
+ ::Grpc::Health::V1::HealthCheckResponse::ServingStatus::SERVING
146
+ )
147
+ health_checker
148
+ end
131
149
  end
132
150
  end
133
151
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AnyCable
4
- VERSION = "1.3.0"
4
+ VERSION = "1.4.0-rc.1"
5
5
  end
@@ -5,6 +5,10 @@ module AnyCable
5
5
  attr_reader channel: String
6
6
 
7
7
  def initialize: (?channel: String channel, **untyped options) -> void
8
+
9
+ private
10
+
11
+ def setup_listeners: (untyped nats_conn) -> void
8
12
  end
9
13
  end
10
14
  end
@@ -0,0 +1,6 @@
1
+ module AnyCable
2
+ module BroadcastAdapters
3
+ class Redisx < Redis
4
+ end
5
+ end
6
+ end
data/sig/manifest.yml ADDED
@@ -0,0 +1,6 @@
1
+ dependencies:
2
+ - name: logger
3
+ - name: openssl
4
+ - name: optparse
5
+ - name: json
6
+ - name: pathname
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: anycable-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.4.0.pre.rc.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - palkan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-03-01 00:00:00.000000000 Z
11
+ date: 2023-06-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: anyway_config
@@ -201,6 +201,7 @@ files:
201
201
  - lib/anycable/broadcast_adapters/http.rb
202
202
  - lib/anycable/broadcast_adapters/nats.rb
203
203
  - lib/anycable/broadcast_adapters/redis.rb
204
+ - lib/anycable/broadcast_adapters/redisx.rb
204
205
  - lib/anycable/cli.rb
205
206
  - lib/anycable/config.rb
206
207
  - lib/anycable/exceptions_handling.rb
@@ -210,6 +211,9 @@ files:
210
211
  - lib/anycable/grpc/rpc_services_pb.rb
211
212
  - lib/anycable/grpc/server.rb
212
213
  - lib/anycable/grpc_kit.rb
214
+ - lib/anycable/grpc_kit/health_checker.rb
215
+ - lib/anycable/grpc_kit/health_pb.rb
216
+ - lib/anycable/grpc_kit/health_services_pb.rb
213
217
  - lib/anycable/grpc_kit/server.rb
214
218
  - lib/anycable/health_server.rb
215
219
  - lib/anycable/middleware.rb
@@ -233,6 +237,7 @@ files:
233
237
  - sig/anycable/broadcast_adapters/http.rbs
234
238
  - sig/anycable/broadcast_adapters/nats.rbs
235
239
  - sig/anycable/broadcast_adapters/redis.rbs
240
+ - sig/anycable/broadcast_adapters/redisx.rbs
236
241
  - sig/anycable/cli.rbs
237
242
  - sig/anycable/config.rbs
238
243
  - sig/anycable/exceptions_handling.rbs
@@ -253,6 +258,7 @@ files:
253
258
  - sig/anycable/rpc/handlers/disconnect.rbs
254
259
  - sig/anycable/socket.rbs
255
260
  - sig/anycable/version.rbs
261
+ - sig/manifest.yml
256
262
  homepage: http://github.com/anycable/anycable
257
263
  licenses:
258
264
  - MIT
@@ -274,11 +280,11 @@ required_ruby_version: !ruby/object:Gem::Requirement
274
280
  version: 2.7.0
275
281
  required_rubygems_version: !ruby/object:Gem::Requirement
276
282
  requirements:
277
- - - ">="
283
+ - - ">"
278
284
  - !ruby/object:Gem::Version
279
- version: '0'
285
+ version: 1.3.1
280
286
  requirements: []
281
- rubygems_version: 3.4.6
287
+ rubygems_version: 3.4.8
282
288
  signing_key:
283
289
  specification_version: 4
284
290
  summary: AnyCable core RPC implementation