Skip to content

Commit 9e81d56

Browse files
authored
Anycable (the 1 year return) (#1019)
* Resurrect / un-revert anycable changes - Original PR was #457
1 parent 9274909 commit 9e81d56

19 files changed

+241
-24
lines changed

Gemfile

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ end
99
# NOTE: IMPORTANT for this to be first so that gems e.g. omniauth-ideo
1010
# can pull in the right ENV vars
1111
# ENV variables in dev
12-
gem 'dotenv-rails', require: 'dotenv/rails-now'
12+
gem 'dotenv-rails', groups: %i[development test], require: 'dotenv/rails-now'
1313

1414
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
1515
gem 'rails', '~> 5.2'
@@ -147,6 +147,11 @@ gem 'schmooze', require: false
147147
# Faker needed for generating fake names
148148
gem 'faker', '~> 1.9.5'
149149

150+
# Anycable for more performant ActionCable
151+
# Note: you must `brew install anycable-go` if you want to run it locally
152+
# See: https://docs.anycable.io/#/using_with_rails
153+
gem 'anycable-rails', '>= 0.6.2'
154+
150155
group :development, :test do
151156
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
152157
gem 'byebug', platforms: %i[mri mingw x64_mingw]

Gemfile.lock

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,13 @@ GEM
9292
annotate (2.7.5)
9393
activerecord (>= 3.2, < 7.0)
9494
rake (>= 10.4, < 13.0)
95+
anycable (0.6.4)
96+
anyway_config (~> 1.4.2)
97+
grpc (~> 1.17)
98+
anycable-rails (0.6.5)
99+
anycable (~> 0.6.0)
100+
rails (>= 5)
101+
anyway_config (1.4.4)
95102
appsignal (2.5.1)
96103
rack
97104
arel (9.0.0)
@@ -591,6 +598,7 @@ DEPENDENCIES
591598
acts-as-taggable-on (~> 6.0.0)
592599
amoeba
593600
annotate
601+
anycable-rails (>= 0.6.2)
594602
appsignal
595603
autoprefixer-rails
596604
barnes

Procfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
web: bundle exec puma -C config/puma.rb
1+
web: ANYCABLE_REDIS_URL=$REDIS_URL REDIS=$REDIS_URL bin/heroku-web
22
release: bundle exec rake db:migrate
33
worker: bundle exec sidekiq -e ${RACK_ENV:-development} -C config/sidekiq.yml
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,16 @@
11
module ApplicationCable
22
class Channel < ActionCable::Channel::Base
3+
after_subscribe :track_user
4+
after_unsubscribe :untrack_user
35
delegate :current_ability, to: :connection
46
protected :current_ability
7+
8+
def track_user
9+
ChannelPresenceTracker.track(self.class.name, current_user.id)
10+
end
11+
12+
def untrack_user
13+
ChannelPresenceTracker.untrack(self.class.name, current_user.id)
14+
end
515
end
616
end

app/channels/application_cable/connection.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ class Connection < ActionCable::Connection::Base
33
identified_by :current_user
44

55
def connect
6-
self.current_user = find_verified_user
6+
self.current_user = find_verified_user || reject_unauthorized_connection
77
end
88

99
def current_ability
@@ -13,7 +13,11 @@ def current_ability
1313
private
1414

1515
def find_verified_user
16-
env['warden'].user
16+
app_cookies_key = Rails.application.config.session_options[:key] ||
17+
raise('Anycable: no session cookies key in config')
18+
19+
env['rack.session'] = cookies.encrypted[app_cookies_key]
20+
Warden::SessionSerializer.new(env).fetch(:user)
1721
end
1822
end
1923
end

app/models/app_metric.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# == Schema Information
2+
#
3+
# Table name: app_metrics
4+
#
5+
# id :bigint(8) not null, primary key
6+
# metric :string
7+
# value :float
8+
# created_at :datetime not null
9+
# updated_at :datetime not null
10+
#
11+
# Indexes
12+
#
13+
# index_app_metrics_on_metric_and_created_at (metric,created_at)
14+
#
15+
16+
class AppMetric < ApplicationRecord
17+
validates :metric, :value, presence: true
18+
end
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
class ChannelPresenceTracker
2+
CACHE_KEY = 'channel_subcriptions'.freeze
3+
cattr_accessor :redis_client
4+
5+
def self.client
6+
self.redis_client ||= Redis.new(url: ENV['REDIS_URL'])
7+
end
8+
9+
def self.current_subscriptions
10+
client.smembers(CACHE_KEY)
11+
end
12+
13+
def self.track(channel_name, user_id)
14+
client.sadd(CACHE_KEY, "#{channel_name}-#{user_id}")
15+
end
16+
17+
def self.untrack(channel_name, user_id)
18+
client.srem(CACHE_KEY, "#{channel_name}-#{user_id}")
19+
end
20+
end
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
class LogChannelSubscribersWorker
2+
include Sidekiq::Worker
3+
4+
def perform
5+
create_app_metrics
6+
end
7+
8+
private
9+
10+
def create_app_metrics
11+
subscriptions_by_channel.each do |channel_name, count|
12+
AppMetric.create(
13+
metric: "subscribers-#{channel_name}",
14+
value: count,
15+
)
16+
end
17+
end
18+
19+
def subscriptions_by_channel
20+
subscriptions = ChannelPresenceTracker.current_subscriptions
21+
subscriptions.each_with_object({}) do |key, h|
22+
channel_key, _user_id = key.to_s.split('-')
23+
h[channel_key] ||= 0
24+
h[channel_key] += 1
25+
end
26+
end
27+
end

bin/heroku-web

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/bin/bash
2+
3+
if [ "$ANYCABLE_DEPLOYMENT" == "true" ]; then
4+
bundle exec anycable --server-command="anycable-go"
5+
else
6+
bundle exec puma -C config/puma.rb
7+
fi

config/anycable.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
production:
2+
# production ANYCABLE_REDIS_URL is set via Procfile to $REDIS_URL
3+
access_logs_disabled: false
4+
development:
5+
redis_url: redis://localhost:6379/1

0 commit comments

Comments
 (0)