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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +97 -5
- data/MIT-LICENSE +1 -1
- data/README.md +13 -6
- data/bin/anycable +1 -1
- data/bin/anycabled +30 -0
- data/lib/anycable.rb +16 -11
- data/lib/anycable/broadcast_adapters.rb +3 -3
- data/lib/anycable/broadcast_adapters/redis.rb +2 -2
- data/lib/anycable/cli.rb +20 -4
- data/lib/anycable/config.rb +10 -5
- data/lib/anycable/exceptions_handling.rb +13 -9
- data/lib/anycable/health_server.rb +2 -3
- data/lib/anycable/middleware.rb +3 -0
- data/lib/anycable/middleware_chain.rb +2 -2
- data/lib/anycable/middlewares/check_version.rb +24 -0
- data/lib/anycable/rpc.rb +76 -0
- data/lib/anycable/rpc/rpc_pb.rb +54 -39
- data/lib/anycable/rpc/rpc_services_pb.rb +4 -3
- data/lib/anycable/rpc_handler.rb +77 -24
- data/lib/anycable/rspec.rb +6 -0
- data/lib/anycable/rspec/rpc_command_context.rb +20 -0
- data/lib/anycable/rspec/rpc_stub_context.rb +13 -0
- data/lib/anycable/rspec/with_grpc_server.rb +15 -0
- data/lib/anycable/server.rb +4 -46
- data/lib/anycable/socket.rb +39 -2
- data/lib/anycable/version.rb +1 -1
- metadata +32 -70
- data/.github/ISSUE_TEMPLATE.md +0 -25
- data/.github/PULL_REQUEST_TEMPLATE.md +0 -31
- data/.gitignore +0 -40
- data/.hound.yml +0 -3
- data/.rubocop.yml +0 -71
- data/.travis.yml +0 -12
- data/Gemfile +0 -8
- data/Makefile +0 -5
- data/PITCHME.md +0 -139
- data/PITCHME.yaml +0 -1
- data/Rakefile +0 -8
- data/anycable.gemspec +0 -35
- data/assets/Memory3.png +0 -0
- data/assets/Memory5.png +0 -0
- data/assets/RTT3.png +0 -0
- data/assets/RTT5.png +0 -0
- data/assets/Scheme1.png +0 -0
- data/assets/Scheme2.png +0 -0
- data/assets/cpu_chart.gif +0 -0
- data/assets/cpu_chart2.gif +0 -0
- data/assets/evlms.png +0 -0
- data/benchmarks/.gitignore +0 -2
- data/benchmarks/2017-02-12.md +0 -308
- data/benchmarks/2018-03-04.md +0 -192
- data/benchmarks/2018-05-27-rpc-bench.md +0 -57
- data/benchmarks/2018-10-27.md +0 -181
- data/benchmarks/HowTo.md +0 -23
- data/benchmarks/ansible.cfg +0 -9
- data/benchmarks/assets/2018-10-27-action-cable-rss.png +0 -0
- data/benchmarks/assets/2018-10-27-action-cable-rtt.png +0 -0
- data/benchmarks/assets/2018-10-27-anycable-rss.png +0 -0
- data/benchmarks/assets/2018-10-27-anycable-rtt.png +0 -0
- data/benchmarks/assets/2018-10-27-async-rss.png +0 -0
- data/benchmarks/assets/2018-10-27-async-rtt.png +0 -0
- data/benchmarks/assets/2018-10-27-falcon-cable-rss.png +0 -0
- data/benchmarks/assets/2018-10-27-falcon-cable-rtt.png +0 -0
- data/benchmarks/assets/2018-10-27-iodine-cable-rss.png +0 -0
- data/benchmarks/assets/2018-10-27-iodine-cable-rtt.png +0 -0
- data/benchmarks/assets/2018-10-27-plezi-rss.png +0 -0
- data/benchmarks/assets/2018-10-27-plezi-rtt.png +0 -0
- data/benchmarks/bench.png +0 -0
- data/benchmarks/benchmark.yml +0 -69
- data/benchmarks/hosts +0 -5
- data/benchmarks/rtt_plot.py +0 -74
- data/benchmarks/rtt_plot_test.py +0 -16
- data/benchmarks/servers.yml +0 -58
- data/circle.yml +0 -8
- data/etc/bug_report_template.rb +0 -76
- data/lib/anycable/handler/capture_exceptions.rb +0 -39
- data/protos/rpc.proto +0 -55
data/benchmarks/ansible.cfg
DELETED
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
Binary file
|
data/benchmarks/bench.png
DELETED
Binary file
|
data/benchmarks/benchmark.yml
DELETED
@@ -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
|
data/benchmarks/hosts
DELETED
data/benchmarks/rtt_plot.py
DELETED
@@ -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')
|
data/benchmarks/rtt_plot_test.py
DELETED
@@ -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()
|
data/benchmarks/servers.yml
DELETED
@@ -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
data/etc/bug_report_template.rb
DELETED
@@ -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
|
data/protos/rpc.proto
DELETED
@@ -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
|
-
}
|