anycable 0.5.2 → 0.6.4

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