diff --git a/rails/Gemfile b/rails/Gemfile index 9ec4929..20bb325 100644 --- a/rails/Gemfile +++ b/rails/Gemfile @@ -25,3 +25,7 @@ gem 'sqlite3', '1.3.10' gem 'mysql2', '0.3.18' gem 'pg', '0.18.1' gem 'benchmark-ips', '~> 2.2.0' +gem 'em-hiredis', '~> 0.3.0' +gem 'redis', '~> 3.0' +gem 'faye-websocket' +gem 'puma' diff --git a/rails/benchmarks/actioncable_postgres.rb b/rails/benchmarks/actioncable_postgres.rb new file mode 100644 index 0000000..0887c85 --- /dev/null +++ b/rails/benchmarks/actioncable_postgres.rb @@ -0,0 +1,21 @@ +require 'bundler/setup' +require 'rails' + +require_relative 'support/benchmark_rails.rb' +require_relative 'support/actioncable_helpers.rb' + +PG_FAYE = Faye::WebSocket::Client.new("ws://127.0.0.1:4001/") + +pg_tcp_addr = ENV['POSTGRES_PORT_5432_TCP_ADDR'] || 'localhost' +pg_port = ENV['POSTGRES_PORT_5432_TCP_PORT'] || 5432 +PG_DB_URL = "postgres://postgres@#{pg_tcp_addr}:#{pg_port}/rubybench", + +Benchmark.rails("actioncable_postgres", time: 10) do + pg_config = { adapter: 'postgresql', url: PG_DB_URL }.with_indifferent_access + with_puma_server(ActionCable.server, 4001, pg_config) do |port| + 500.times do + PG_FAYE.send(SUB) + PG_FAYE.send(MSG) + end + end +end diff --git a/rails/benchmarks/actioncable_redis.rb b/rails/benchmarks/actioncable_redis.rb new file mode 100644 index 0000000..9b06df5 --- /dev/null +++ b/rails/benchmarks/actioncable_redis.rb @@ -0,0 +1,19 @@ +require 'bundler/setup' +require 'rails' + +require_relative 'support/benchmark_rails.rb' +require_relative 'support/actioncable_helpers.rb' + +REDIS_FAYE = Faye::WebSocket::Client.new("ws://127.0.0.1:4002/") + +REDIS_DB_URL = ENV["REDIS_PORT_6379_TCP_ADDR"] + +Benchmark.rails("actioncable_redis", time: 10) do + redis_config = { adapter: 'redis', url: REDIS_DB_URL }.with_indifferent_access + with_puma_server(ActionCable.server, 4002, redis_config) do |port| + 500.times do + REDIS_FAYE.send(SUB) + REDIS_FAYE.send(MSG) + end + end +end diff --git a/rails/benchmarks/support/actioncable_helpers.rb b/rails/benchmarks/support/actioncable_helpers.rb new file mode 100644 index 0000000..b2381db --- /dev/null +++ b/rails/benchmarks/support/actioncable_helpers.rb @@ -0,0 +1,53 @@ +require 'action_cable' +require 'rack/mock' +require 'faye/websocket' +require 'active_support/core_ext/hash/indifferent_access' +require 'pathname' +require 'puma' + +module Rails + def self.root + Pathname.pwd + end +end + +::Object.const_set(:ApplicationCable, Module.new) + +class ApplicationCable::Channel < ActionCable::Channel::Base +end + +class ApplicationCable::Connection < ActionCable::Connection::Base +end + +ActionCable.instance_variable_set(:@server, nil) +server = ActionCable.server +server.config = ActionCable::Server::Configuration.new +inner_logger = ActiveSupport::Logger.new(STDOUT) +server.config.logger = ActionCable::Connection::TaggedLoggerProxy.new(inner_logger, tags: []) + +# and now the "real" setup for our test: +server.config.disable_request_forgery_protection = true + +if Dir.pwd.include?('support') + server.config.channel_load_paths = [File.expand_path(Dir.pwd, __dir__)] +else + server.config.channel_load_paths = [File.expand_path('support', __dir__)] +end + +def with_puma_server(rack_app = ActionCable.server, port, config) + ActionCable.server.config.cable = config + server = ::Puma::Server.new(rack_app, ::Puma::Events.strings) + server.add_tcp_listener '127.0.0.1', port + server.min_threads = 1 + server.max_threads = 4 + + t = Thread.new { server.run.join } + yield port + +ensure + server.stop(true) + t.join +end + +MSG = JSON.dump command: 'message', identifier: JSON.dump(channel: 'EchoChannel'), data: JSON.dump(action: 'ding', message: 'hello') +SUB = JSON.dump command: 'subscribe', identifier: JSON.dump(channel: 'EchoChannel') diff --git a/rails/benchmarks/support/echo_channel.rb b/rails/benchmarks/support/echo_channel.rb new file mode 100644 index 0000000..d98b7f4 --- /dev/null +++ b/rails/benchmarks/support/echo_channel.rb @@ -0,0 +1,13 @@ +class EchoChannel < ApplicationCable::Channel + def subscribed + stream_from "echo_channel" + end + + def unsubscribed + # Any cleanup needed when channel is unsubscribed + end + + def ding(data) + transmit(dong: data['message']) + end +end