anycable 0.6.0.rc1 → 1.0.0.preview1

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 (78) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +97 -5
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +13 -6
  5. data/bin/anycable +1 -1
  6. data/bin/anycabled +30 -0
  7. data/lib/anycable.rb +16 -11
  8. data/lib/anycable/broadcast_adapters.rb +3 -3
  9. data/lib/anycable/broadcast_adapters/redis.rb +2 -2
  10. data/lib/anycable/cli.rb +20 -4
  11. data/lib/anycable/config.rb +10 -5
  12. data/lib/anycable/exceptions_handling.rb +13 -9
  13. data/lib/anycable/health_server.rb +2 -3
  14. data/lib/anycable/middleware.rb +3 -0
  15. data/lib/anycable/middleware_chain.rb +2 -2
  16. data/lib/anycable/middlewares/check_version.rb +24 -0
  17. data/lib/anycable/rpc.rb +76 -0
  18. data/lib/anycable/rpc/rpc_pb.rb +54 -39
  19. data/lib/anycable/rpc/rpc_services_pb.rb +4 -3
  20. data/lib/anycable/rpc_handler.rb +77 -24
  21. data/lib/anycable/rspec.rb +6 -0
  22. data/lib/anycable/rspec/rpc_command_context.rb +20 -0
  23. data/lib/anycable/rspec/rpc_stub_context.rb +13 -0
  24. data/lib/anycable/rspec/with_grpc_server.rb +15 -0
  25. data/lib/anycable/server.rb +4 -46
  26. data/lib/anycable/socket.rb +39 -2
  27. data/lib/anycable/version.rb +1 -1
  28. metadata +32 -70
  29. data/.github/ISSUE_TEMPLATE.md +0 -25
  30. data/.github/PULL_REQUEST_TEMPLATE.md +0 -31
  31. data/.gitignore +0 -40
  32. data/.hound.yml +0 -3
  33. data/.rubocop.yml +0 -71
  34. data/.travis.yml +0 -12
  35. data/Gemfile +0 -8
  36. data/Makefile +0 -5
  37. data/PITCHME.md +0 -139
  38. data/PITCHME.yaml +0 -1
  39. data/Rakefile +0 -8
  40. data/anycable.gemspec +0 -35
  41. data/assets/Memory3.png +0 -0
  42. data/assets/Memory5.png +0 -0
  43. data/assets/RTT3.png +0 -0
  44. data/assets/RTT5.png +0 -0
  45. data/assets/Scheme1.png +0 -0
  46. data/assets/Scheme2.png +0 -0
  47. data/assets/cpu_chart.gif +0 -0
  48. data/assets/cpu_chart2.gif +0 -0
  49. data/assets/evlms.png +0 -0
  50. data/benchmarks/.gitignore +0 -2
  51. data/benchmarks/2017-02-12.md +0 -308
  52. data/benchmarks/2018-03-04.md +0 -192
  53. data/benchmarks/2018-05-27-rpc-bench.md +0 -57
  54. data/benchmarks/2018-10-27.md +0 -181
  55. data/benchmarks/HowTo.md +0 -23
  56. data/benchmarks/ansible.cfg +0 -9
  57. data/benchmarks/assets/2018-10-27-action-cable-rss.png +0 -0
  58. data/benchmarks/assets/2018-10-27-action-cable-rtt.png +0 -0
  59. data/benchmarks/assets/2018-10-27-anycable-rss.png +0 -0
  60. data/benchmarks/assets/2018-10-27-anycable-rtt.png +0 -0
  61. data/benchmarks/assets/2018-10-27-async-rss.png +0 -0
  62. data/benchmarks/assets/2018-10-27-async-rtt.png +0 -0
  63. data/benchmarks/assets/2018-10-27-falcon-cable-rss.png +0 -0
  64. data/benchmarks/assets/2018-10-27-falcon-cable-rtt.png +0 -0
  65. data/benchmarks/assets/2018-10-27-iodine-cable-rss.png +0 -0
  66. data/benchmarks/assets/2018-10-27-iodine-cable-rtt.png +0 -0
  67. data/benchmarks/assets/2018-10-27-plezi-rss.png +0 -0
  68. data/benchmarks/assets/2018-10-27-plezi-rtt.png +0 -0
  69. data/benchmarks/bench.png +0 -0
  70. data/benchmarks/benchmark.yml +0 -69
  71. data/benchmarks/hosts +0 -5
  72. data/benchmarks/rtt_plot.py +0 -74
  73. data/benchmarks/rtt_plot_test.py +0 -16
  74. data/benchmarks/servers.yml +0 -58
  75. data/circle.yml +0 -8
  76. data/etc/bug_report_template.rb +0 -76
  77. data/lib/anycable/handler/capture_exceptions.rb +0 -39
  78. data/protos/rpc.proto +0 -55
@@ -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
Binary file
@@ -1,69 +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.21.207'
9
- hostname: ws-bench-client
10
- local_ips: ['172.31.19.119', '172.31.19.120', '172.31.17.121', '172.31.17.122']
11
- # local_ips_str: '-l 172.31.19.119 -l 172.31.19.120 -l 172.31.17.121 -l 172.31.17.122'
12
- local_ips_str: ''
13
- steps: 10
14
- step_size: 1000
15
- sample_size: 100
16
- concurrency: 8
17
- payload_size: 200
18
- prepare: False
19
- log_file: "./last_bench.log"
20
- tasks:
21
- - name: Prepare the machine
22
- tags: prepare
23
- block:
24
- - hostname:
25
- name: "{{ hostname }}"
26
- - lineinfile:
27
- path: /etc/hosts
28
- line: '127.0.0.1 {{ hostname }}'
29
- state: present
30
- - shell: ip addr add {{ item }}/20 dev eth0
31
- with_items: "{{ local_ips }}"
32
-
33
- - name: Print Action Cable command
34
- 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"
35
- tags: action_cable
36
-
37
- - name: Action Cable benchmark
38
- become_user: deplo
39
- 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
40
- register: bench
41
- tags: action_cable
42
- args:
43
- chdir: /webapps/anycable_bench
44
- ignore_errors: yes
45
-
46
- - name: Print base command
47
- 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"
48
- tags: base
49
-
50
- - name: Base benchmark
51
- become_user: deplo
52
- 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
53
- register: bench
54
- tags: base
55
- args:
56
- chdir: /webapps/anycable_bench
57
- ignore_errors: yes
58
-
59
- - name: Benchmark results (stdout)
60
- debug: var=bench.stdout_lines
61
- tags:
62
- - action_cable
63
- - base
64
-
65
- - name: Benchmark results (stderr)
66
- debug: var=bench.stderr_lines
67
- tags:
68
- - action_cable
69
- - base
@@ -1,5 +0,0 @@
1
- [benchmark]
2
- ec2-18-202-197-244.eu-west-1.compute.amazonaws.com ansible_ssh_private_key_file=/Users/palkan/.ssh/macos-dev
3
-
4
- [servers]
5
- ec2-34-255-216-103.eu-west-1.compute.amazonaws.com ansible_ssh_private_key_file=/Users/palkan/.ssh/macos-dev
@@ -1,74 +0,0 @@
1
- #coding: utf-8
2
-
3
- import sys
4
- import os
5
- import re
6
- import argparse
7
-
8
- def process_file(input_file):
9
- log = {}
10
- log['clients'] = []
11
- log['95per'] = []
12
- log['min'] = []
13
- log['med'] = []
14
- log['max'] = []
15
-
16
- for line in input_file:
17
- point = parse_line(line)
18
- if point:
19
- log['clients'].append(point['clients'])
20
- log['95per'].append(point['95per'])
21
- log['min'].append(point['min'])
22
- log['med'].append(point['med'])
23
- log['max'].append(point['max'])
24
- return log
25
-
26
- def parse_line(line):
27
- # clients: 1000 95per-rtt: 1328ms min-rtt: 2ms median-rtt: 457ms max-rtt: 1577ms
28
- matches = re.search('clients:\s+(\d+)\s+95per\-rtt:\s+(\d+)ms\s+min\-rtt:\s+(\d+)ms\s+median\-rtt:\s+(\d+)ms\s+max\-rtt:\s+(\d+)ms', line)
29
- if matches:
30
- return {
31
- 'clients': int(matches.group(1)),
32
- '95per': int(matches.group(2)),
33
- 'min': int(matches.group(3)),
34
- 'med': int(matches.group(4)),
35
- 'max': int(matches.group(5))
36
- }
37
- return False
38
-
39
- def generate_plot(log, output):
40
- import matplotlib.patches as mpatches
41
- import matplotlib.pyplot as plt
42
-
43
- with plt.rc_context({'backend': 'Agg'}):
44
-
45
- fig = plt.figure()
46
- ax = fig.add_subplot(1, 1, 1)
47
-
48
- ax.plot(log['clients'], log['95per'], '-', lw=1, color='r', label='95 percentile')
49
- ax.plot(log['clients'], log['med'], '-', lw=1, color='green', dashes=[10, 5], label='Median')
50
- ax.plot(log['clients'], log['max'], '-', lw=1, color='grey', label='Max')
51
-
52
- ax.set_ylabel('RTT ms', color='r')
53
- ax.set_xlabel('clients num')
54
- ax.set_ylim(0., max(log['max']) * 1.1)
55
-
56
- handles, labels = ax.get_legend_handles_labels()
57
- ax.legend(handles, labels, bbox_to_anchor=(0.4, 1))
58
-
59
- ax.grid()
60
-
61
- fig.savefig(output)
62
-
63
- if __name__ == "__main__":
64
- parser = argparse.ArgumentParser(description='Generate RTT chart')
65
- parser.add_argument('-i', dest='inputfile', type=argparse.FileType('r'), help='input file containing benchmark results', required=True)
66
- parser.add_argument('-o', dest='outputfile', type=argparse.FileType('w'), help='output file to write resulted chart PNG', required=True)
67
-
68
- args = parser.parse_args()
69
-
70
- data = process_file(args.inputfile)
71
-
72
- generate_plot(data, args.outputfile)
73
-
74
- print('Done')
@@ -1,16 +0,0 @@
1
- import unittest
2
- import os
3
- import rtt_plot
4
-
5
- class TestRttPlot(unittest.TestCase):
6
- def test_parse_line(self):
7
- self.assertEqual(
8
- { 'clients': 1000, '95per': 1328, 'min': 2, 'med': 457, 'max': 1577 },
9
- rtt_plot.parse_line(' "clients: 1000 95per-rtt: 1328ms min-rtt: 2ms median-rtt: 457ms max-rtt: 1577ms"')
10
- )
11
- self.assertFalse(
12
- rtt_plot.parse_line('2018/10/28 02:24:13 Missing received broadcasts: expected 23100000, got 23005351')
13
- )
14
-
15
- if __name__ == '__main__':
16
- unittest.main()
@@ -1,58 +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
- - iodine_cable
17
- - falcon_cable
18
- - falcon_async
19
- - kill
20
- with_items:
21
- - "3334"
22
- ignore_errors: true
23
- - name: Run Action Cable
24
- become_user: deplo
25
- tags: action_cable
26
- command: bash -lc "WEB_CONCURRENCY={{ web_concurrency }} bundle exec rails s -p 3334 -e production"
27
- args:
28
- chdir: /webapps/anycable_bench/ruby/action-cable-server
29
- - name: Run Anycable Go
30
- become_user: deplo
31
- tags: anycable
32
- shell: bash -lc "ANYCABLE_GO_BIN="anycable-go-0.6.0-alpha" ANYCABLE_PORT="3334" bundle exec bin/anycable"
33
- args:
34
- chdir: /webapps/anycable_bench/ruby/action-cable-server
35
- - name: Run Iodine/Plezi
36
- become_user: deplo
37
- tags: plezi
38
- shell: bash -lc "bundle exec iodine -p 3334 -w {{ web_concurrency }} -t 16"
39
- args:
40
- chdir: /webapps/anycable_bench/ruby/plezi-iodine
41
- - name: Run Iodine/ActionCable
42
- become_user: deplo
43
- tags: iodine_cable
44
- shell: bash -lc "RAILS_ENV=production bundle exec iodine -p 3334 -w {{ web_concurrency }} -t 16"
45
- args:
46
- chdir: /webapps/anycable_bench/ruby/action-cable-server
47
- - name: Run Falcon/ActionCable
48
- become_user: deplo
49
- tags: falcon_cable
50
- shell: bash -lc "RAILS_ENV=production bundle exec falcon serve -b http://0.0.0.0:3334"
51
- args:
52
- chdir: /webapps/anycable_bench/ruby/action-cable-server
53
- - name: Run Falcon/Async
54
- become_user: deplo
55
- tags: falcon_async
56
- shell: bash -lc "bundle exec falcon serve -b http://0.0.0.0:3334"
57
- args:
58
- chdir: /webapps/anycable_bench/ruby/falcon
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,39 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "anycable/rpc/rpc_pb"
4
-
5
- module AnyCable
6
- module Handler # :nodoc:
7
- # Handle app-level errors.
8
- #
9
- # NOTE: this functionality couldn't be implemeted
10
- # as middleware, 'cause interceptors do not support
11
- # aborting the call and returning a data
12
- module CaptureExceptions
13
- RESPONSE_CLASS = {
14
- command: AnyCable::CommandResponse,
15
- connect: AnyCable::ConnectionResponse,
16
- disconnect: AnyCable::DisconnectResponse
17
- }.freeze
18
-
19
- RESPONSE_CLASS.keys.each do |mid|
20
- module_eval <<~CODE, __FILE__, __LINE__ + 1
21
- def #{mid}(*)
22
- capture_exceptions(:#{mid}) { super }
23
- end
24
- CODE
25
- end
26
-
27
- def capture_exceptions(method_name)
28
- yield
29
- rescue StandardError => exp
30
- AnyCable::ExceptionsHandling.notify(exp)
31
-
32
- RESPONSE_CLASS.fetch(method_name).new(
33
- status: AnyCable::Status::ERROR,
34
- error_msg: exp.message
35
- )
36
- end
37
- end
38
- end
39
- 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
- }