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

Sign up to get free protection for your applications and to get access to all the features.
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