anycable 0.5.2 → 0.6.4

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.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +166 -2
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +19 -60
  5. data/bin/anycable +13 -0
  6. data/bin/anycabled +30 -0
  7. data/lib/anycable.rb +69 -18
  8. data/lib/anycable/broadcast_adapters.rb +33 -0
  9. data/lib/anycable/broadcast_adapters/redis.rb +42 -0
  10. data/lib/anycable/cli.rb +329 -0
  11. data/lib/anycable/config.rb +93 -17
  12. data/lib/anycable/exceptions_handling.rb +37 -0
  13. data/lib/anycable/health_server.rb +52 -31
  14. data/lib/anycable/middleware.rb +19 -0
  15. data/lib/anycable/middleware_chain.rb +58 -0
  16. data/lib/anycable/rpc/rpc_pb.rb +1 -1
  17. data/lib/anycable/rpc/rpc_services_pb.rb +1 -1
  18. data/lib/anycable/rpc_handler.rb +77 -32
  19. data/lib/anycable/server.rb +132 -39
  20. data/lib/anycable/socket.rb +1 -1
  21. data/lib/anycable/version.rb +2 -2
  22. metadata +34 -59
  23. data/.gitignore +0 -40
  24. data/.hound.yml +0 -3
  25. data/.rubocop.yml +0 -71
  26. data/.travis.yml +0 -13
  27. data/Gemfile +0 -8
  28. data/Makefile +0 -5
  29. data/PITCHME.md +0 -139
  30. data/PITCHME.yaml +0 -1
  31. data/Rakefile +0 -8
  32. data/anycable.gemspec +0 -32
  33. data/assets/Memory3.png +0 -0
  34. data/assets/Memory5.png +0 -0
  35. data/assets/RTT3.png +0 -0
  36. data/assets/RTT5.png +0 -0
  37. data/assets/Scheme1.png +0 -0
  38. data/assets/Scheme2.png +0 -0
  39. data/assets/cpu_chart.gif +0 -0
  40. data/assets/cpu_chart2.gif +0 -0
  41. data/assets/evlms.png +0 -0
  42. data/benchmarks/.gitignore +0 -1
  43. data/benchmarks/2017-02-12.md +0 -308
  44. data/benchmarks/2018-03-04.md +0 -192
  45. data/benchmarks/2018-05-27-rpc-bench.md +0 -57
  46. data/benchmarks/HowTo.md +0 -23
  47. data/benchmarks/ansible.cfg +0 -9
  48. data/benchmarks/benchmark.yml +0 -67
  49. data/benchmarks/hosts +0 -5
  50. data/benchmarks/servers.yml +0 -36
  51. data/circle.yml +0 -8
  52. data/etc/bug_report_template.rb +0 -76
  53. data/lib/anycable/handler/exceptions_handling.rb +0 -43
  54. data/lib/anycable/pubsub.rb +0 -26
  55. data/protos/rpc.proto +0 -55
@@ -1,57 +0,0 @@
1
- # AnyCable RPC benchmarks
2
-
3
- AnyCable RPC server benchmarks.
4
-
5
- The benchmark is pretty simple:
6
- - Create N concurrent _clients_ (authenticate and subscribe to `BenchmarkChannel` within the RPC app)
7
- - Every client starts performing an action (`echo`) in a loop
8
- - Every call is collected (request time)
9
- - Wait for the specified amount of seconds and aggragate the results.
10
-
11
- Measuring **requests per seconds**.
12
-
13
- **NOTE:** this benchmark shows performance characteristics for both _clients_ and the server.
14
- The purpose of this benchmark is to measure the thoughout of RPC connection between AnyCable services.
15
-
16
- **NOTE 2:** "Noop RPC" is just a gRPC handling doing nothing except from responding with success result.
17
-
18
- Sources:
19
- - [Golang](https://github.com/anycable/anycable-go/blob/chore/benchmarking/cmd/rpc-bench/main.go)
20
- - [Erlang](https://github.com/anycable/simple-cable-app/tree/master/benchmarks)
21
-
22
- ## Erlang single connection
23
-
24
- RPC type \ Number of clients | 1 | 10 | 50
25
- --------------------------------|------|------|----
26
- AnyCable RPC | 100 | 110 | 130
27
- Noop RPC | 100 | 115 | 125
28
-
29
- Erlang client definitely has problems(
30
-
31
- Looks like HTTP2 streaming is working in _blocking mode_.
32
-
33
- Requires investigation.
34
-
35
- ## Erlycable connection pool (5-50)
36
-
37
- RPC type \ Number of clients | 1 | 10 | 50
38
- --------------------------------|------|------|----
39
- AnyCable RPC | 100 | 800 | 1700
40
- Noop RPC | 100 | 900 | 2700
41
-
42
- ## Golang single connection
43
-
44
- RPC type \ Number of clients | 1 | 10 | 50
45
- --------------------------------|------|------|----
46
- AnyCable RPC | 1600 | 2200 | 2400
47
- Noop RPC with connection object building\* | 2200 | 3500 | 4000
48
- Noop RPC | 3000 | 4600 | 5500
49
-
50
- \* Only build Action Cable connection object without performing an action.
51
-
52
- ## Golang connection pool (5-50)
53
-
54
- RPC type \ Number of clients | 1 | 10 | 50
55
- --------------------------------|------|------|----
56
- AnyCable RPC | 1400 | 1500 | 1700
57
- Noop RPC | 2100 | 3200 | 4000
@@ -1,23 +0,0 @@
1
- ## Hot to run benchmarks
2
-
3
- ### Installation
4
-
5
- - Install [Ansible](http://ansible.com) (`brew install ansible`)
6
-
7
- - Launch two EC2 instances from `anycable-benchmark-xxx` AMI
8
-
9
- - Attach more local network interfaces to the _client_ instance
10
-
11
- - Update `hosts` file and playbooks with required information (IPs, etc)
12
-
13
- - Prepare the _client_ instance: `ansible-playbook --tags prepare benchmark.yml`
14
-
15
- ### Running benchmarks
16
-
17
- To run a server, e.g. Action Cable: `ansible-playbook --tags action_cable servers.yml`
18
-
19
- To run a benchmark against it: `ansible-playbook --tags action_cable benchmark.yml`
20
-
21
- You can also specify benchmark parameters: `ansible-playbook --extra-vars "step_size=1000 steps=10 sample_size=10" benchmark.yml`.
22
-
23
- **NOTE**: Ansible doesn't support command output streaming, so we can only see the results at the end of the run.
@@ -1,9 +0,0 @@
1
- [defaults]
2
- deprecation_warnings = False
3
- inventory = ./hosts
4
- allow_world_readable_tmpfiles = True
5
- gathering=smart
6
- transport=paramiko
7
-
8
- [ssh_connection]
9
- pipelining = True
@@ -1,67 +0,0 @@
1
- ---
2
- - name: Benchmark Client
3
- hosts: benchmark
4
- sudo: yes
5
- remote_user: ubuntu
6
- gather_facts: False
7
- vars:
8
- server_host: '172.31.17.82'
9
- hostname: ws-bench-client
10
- local_ips: ['172.31.17.191', '172.31.17.192', '172.31.17.193', '172.31.17.194']
11
- local_ips_str: '-l 172.31.17.191 -l 172.31.17.192 -l 172.31.17.193 -l 172.31.17.194'
12
- steps: 20
13
- step_size: 1000
14
- sample_size: 100
15
- concurrency: 8
16
- payload_size: 200
17
- prepare: False
18
- tasks:
19
- - name: Prepare the machine
20
- tags: prepare
21
- block:
22
- - hostname:
23
- name: "{{ hostname }}"
24
- - lineinfile:
25
- path: /etc/hosts
26
- line: '127.0.0.1 {{ hostname }}'
27
- state: present
28
- - shell: ip addr add {{ item }}/20 dev eth0
29
- with_items: "{{ local_ips }}"
30
-
31
- - name: Print Action Cable command
32
- debug: msg="bin/websocket-bench broadcast {{ local_ips_str }} --concurrent {{ concurrency }} --sample-size {{ sample_size }} --step-size {{ step_size }} --payload-padding {{ payload_size }} --total-steps {{ steps }} --origin http://0.0.0.0 ws://{{ server_host }}:3334/cable --server-type=actioncable"
33
- tags: action_cable
34
-
35
- - name: Action Cable benchmark
36
- become_user: deplo
37
- shell: bin/websocket-bench broadcast {{ local_ips_str }} --concurrent {{ concurrency }} --sample-size {{ sample_size }} --step-size {{ step_size }} --payload-padding {{ payload_size }} --total-steps {{ steps }} --origin http://0.0.0.0 ws://{{ server_host }}:3334/cable --server-type=actioncable
38
- register: bench
39
- tags: action_cable
40
- args:
41
- chdir: /webapps/anycable_bench
42
- ignore_errors: yes
43
-
44
- - name: Print Plezi command
45
- debug: msg="bin/websocket-bench broadcast {{ local_ips_str }} --concurrent {{ concurrency }} --sample-size {{ sample_size }} --step-size {{ step_size }} --payload-padding {{ payload_size }} --total-steps {{ steps }} ws://{{ server_host }}:3334/cable"
46
- tags: plezi
47
-
48
- - name: Plezi benchmark
49
- become_user: deplo
50
- shell: bin/websocket-bench broadcast {{ local_ips_str }} --concurrent {{ concurrency }} --sample-size {{ sample_size }} --step-size {{ step_size }} --payload-padding {{ payload_size }} --total-steps {{ steps }} ws://{{ server_host }}:3334/cable
51
- register: bench
52
- tags: plezi
53
- args:
54
- chdir: /webapps/anycable_bench
55
- ignore_errors: yes
56
-
57
- - name: Benchmark results (stdout)
58
- debug: var=bench.stdout_lines
59
- tags:
60
- - action_cable
61
- - plezi
62
-
63
- - name: Benchmark results (stderr)
64
- debug: var=bench.stderr_lines
65
- tags:
66
- - action_cable
67
- - plezi
@@ -1,5 +0,0 @@
1
- [benchmark]
2
- ec2-52-19-57-142.eu-west-1.compute.amazonaws.com ansible_ssh_private_key_file=/Users/palkan/.ssh/macos-dev
3
-
4
- [servers]
5
- ec2-34-252-33-102.eu-west-1.compute.amazonaws.com ansible_ssh_private_key_file=/Users/palkan/.ssh/macos-dev
@@ -1,36 +0,0 @@
1
- ---
2
- - name: Benchmark Servers
3
- hosts: servers
4
- sudo: yes
5
- remote_user: ubuntu
6
- gather_facts: False
7
- vars:
8
- web_concurrency: 8
9
- tasks:
10
- - name: Kill servers
11
- shell: pid=$(lsof -i:{{item}} -t); kill -TERM $pid || kill -KILL $pid
12
- tags:
13
- - action_cable
14
- - anycable
15
- - plezi
16
- with_items:
17
- - "3334"
18
- ignore_errors: true
19
- - name: Run Action Cable
20
- become_user: deplo
21
- tags: action_cable
22
- shell: WEB_CONCURRENCY={{ web_concurrency }} bundle exec rails s -p 3334 -e production
23
- args:
24
- chdir: /webapps/anycable_bench/ruby/action-cable-server
25
- - name: Run Anycable Go
26
- become_user: deplo
27
- tags: anycable
28
- shell: ANYCABLE_GO_BIN="anycable-go-0.6.0" ANYCABLE_PORT="3334" bundle exec bin/anycable
29
- args:
30
- chdir: /webapps/anycable_bench/ruby/action-cable-server
31
- - name: Run Iodine/Plezi
32
- become_user: deplo
33
- tags: plezi
34
- shell: iodine -p 3334
35
- args:
36
- chdir: /webapps/anycable_bench/ruby/plezi-iodine
data/circle.yml DELETED
@@ -1,8 +0,0 @@
1
- machine:
2
- ruby:
3
- version: 2.3.0
4
-
5
- dependencies:
6
- pre:
7
- - gem install bundler -v 1.11.2
8
-
@@ -1,76 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/inline"
4
-
5
- # This reproduction script is based on `anyt` gem
6
- # (https://github.com/anycable/anyt).
7
- #
8
- # See more test examples here:
9
- # https://github.com/anycable/anyt/tree/master/lib/anyt/tests
10
-
11
- gemfile(true) do
12
- source "https://rubygems.org"
13
-
14
- gem "anyt"
15
- end
16
-
17
- require "anyt"
18
- require "anyt/cli"
19
- require "anycable-rails"
20
-
21
- # WebSocket server url
22
- ENV['ANYT_TARGET_URL'] ||= "ws://localhost:8080/cable"
23
-
24
- # Command to launch WebSocket server.
25
- # Comment this line if you want to run WebSocket server manually
26
- ENV['ANYT_COMMAND'] ||= "anycable-go"
27
-
28
- ActionCable.server.config.logger = Rails.logger = Anycable.logger
29
-
30
- # Test scenario
31
- feature "issue_xyz" do
32
- # This block defines an anonymous channel to test against
33
- channel do
34
- # def subscribed
35
- # stream_from "a"
36
- # end
37
- #
38
- # def perform(data)
39
- # # ...
40
- # end
41
- end
42
-
43
- # You can use minitest/spec features here
44
- before do
45
- # `channel` contains identifier of the anonymous channel defined above
46
- subscribe_request = { command: "subscribe", identifier: { channel: channel }.to_json }
47
-
48
- # `client` represents a websocket client connected to a server
49
- client.send(subscribe_request)
50
-
51
- ack = {
52
- "identifier" => { channel: channel }.to_json, "type" => "confirm_subscription"
53
- }
54
-
55
- # `receive` method returns the first message from the incoming messages queue;
56
- # waits 5s when no messages available
57
- assert_equal ack, client.receive
58
- end
59
-
60
- # describe you bug scenario here
61
- scenario %{
62
- Should work
63
- } do
64
- # ...
65
- end
66
- end
67
-
68
- # Required setup/teardown
69
- begin
70
- Anyt::RPC.start
71
- Anyt::Command.run if Anyt.config.command
72
- Anyt::Tests.run
73
- ensure
74
- Anyt::Command.stop if Anyt.config.command
75
- Anyt::RPC.stop
76
- end
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Anycable
4
- module Handler # :nodoc:
5
- # Handle app-level errors
6
- module ExceptionsHandling
7
- def connect(*)
8
- super
9
- rescue StandardError => ex
10
- handle_exception(ex)
11
- Anycable::ConnectionResponse.new(status: Anycable::Status::ERROR, error_msg: ex.message)
12
- end
13
-
14
- def disconnect(*)
15
- super
16
- rescue StandardError => ex
17
- handle_exception(ex)
18
- Anycable::DisconnectResponse.new(status: Anycable::Status::ERROR, error_msg: ex.message)
19
- end
20
-
21
- def command(*)
22
- super
23
- rescue StandardError => ex
24
- handle_exception(ex)
25
- Anycable::CommandResponse.new(status: Anycable::Status::ERROR, error_msg: ex.message)
26
- end
27
-
28
- def handle_exception(ex)
29
- Anycable.error_handlers.each do |handler|
30
- begin
31
- handler.call(ex)
32
- rescue StandardError => ex
33
- Anycable.logger.error "!!! ERROR HANDLER THREW AN ERROR !!!"
34
- Anycable.logger.error ex
35
- Anycable.logger.error ex.backtrace.join("\n") unless ex.backtrace.nil?
36
- end
37
- end
38
- end
39
- end
40
-
41
- Anycable.error_handlers << proc { |e| Anycable.logger.error(e.message) }
42
- end
43
- end
@@ -1,26 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "redis"
4
- require "json"
5
-
6
- module Anycable
7
- # PubSub for broadcasting
8
- class PubSub
9
- attr_reader :redis_conn
10
-
11
- def initialize
12
- redis_config = { url: Anycable.config.redis_url }
13
- unless Anycable.config.redis_sentinels.empty?
14
- redis_config[:sentinels] = Anycable.config.redis_sentinels
15
- end
16
- @redis_conn = Redis.new(redis_config)
17
- end
18
-
19
- def broadcast(channel, payload)
20
- redis_conn.publish(
21
- Anycable.config.redis_channel,
22
- { stream: channel, data: payload }.to_json
23
- )
24
- end
25
- end
26
- end
@@ -1,55 +0,0 @@
1
- syntax = "proto3";
2
-
3
- package anycable;
4
-
5
- service RPC {
6
- rpc Connect (ConnectionRequest) returns (ConnectionResponse) {}
7
- rpc Command (CommandMessage) returns (CommandResponse) {}
8
- rpc Disconnect (DisconnectRequest) returns (DisconnectResponse) {}
9
- }
10
-
11
- enum Status {
12
- ERROR = 0;
13
- SUCCESS = 1;
14
- FAILURE = 2;
15
- }
16
-
17
- message ConnectionRequest {
18
- string path = 1;
19
- map<string,string> headers = 2;
20
- }
21
-
22
- message ConnectionResponse {
23
- Status status = 1;
24
- string identifiers = 2;
25
- repeated string transmissions = 3;
26
- string error_msg = 4;
27
- }
28
-
29
- message CommandMessage {
30
- string command = 1;
31
- string identifier = 2;
32
- string connection_identifiers = 3;
33
- string data = 4;
34
- }
35
-
36
- message CommandResponse {
37
- Status status = 1;
38
- bool disconnect = 2;
39
- bool stop_streams = 3;
40
- repeated string streams = 4;
41
- repeated string transmissions = 5;
42
- string error_msg = 6;
43
- }
44
-
45
- message DisconnectRequest {
46
- string identifiers = 1;
47
- repeated string subscriptions = 2;
48
- string path = 3;
49
- map<string,string> headers = 4;
50
- }
51
-
52
- message DisconnectResponse {
53
- Status status = 1;
54
- string error_msg = 2;
55
- }