Deploying ActionCable on Heroku with Sidekiq
ActionCable is WebSockets on rails. This lets you create realtime, interactive systems, where you can push data from one client to another client without reloading or polling. But how do we deploy it on heroku?
Let’s go through the steps of how to deploy things on heroku.
First, the app
I’m going to use an app that DHH made as a walk through:
Action Cable is the highlight feature of Rails 5. It makes it very easy to work with WebSockets in Rails. You have…medium.com
I’ve created the code for you below, but what happens in the tutorial is that he
- creates a message model
- creates a rooms controller with a show action
- opens a websocket with the server
- that receives rendered messages on the `rooms_channel`
- and accepts messages to be broadcast to everyone else
- uses a background worker to render the message and broadcast it back to everyone
Now lets talk about deployment
The app we generated was done using basic rails 5 commands, and it’s out of the box stuff. (Unlike most of the walkthroughs on this site which were done with http://seed.happyfuncorp.com). It’s setup to use puma (good), which will handle the WebSockets. It’s also setup with ActiveJob and ActionCable, both on which are in async local mode and won’t use redis in development. ActionCable will also need to know where it’s websocket is going to connect to. Lets walk through everything that needs to get done.
Set up the Gemfile
First we need to add some things to the Gemfile, specifically redis, postgres, and the sidekiq gems. Add the following to your Gemfile
gem ‘redis’, ‘~> 3.0’
gem ‘pg’, group: :production
gem ‘sqlite3’, group: :development
gem ‘sinatra’, git: ‘https://github.com/sinatra/sinatra', :require => nil
Make sure you remove the `sqlite3` on the top of the file, we only want it in development. We are installing `sinatra` out of the master branch since the current release doesn’t play well with Rails 5. (If it tries to install Sinatra 1.0 you’ll get rake exception errors.) If you don’t want the web interface to sidekiq you can omit.
First we need to create a `Procfile` that defines our dynos
web: bundle exec puma -C config/puma.rb
worker: bundle exec sidekiq -c 2
I’m running sidekiq with a concurrency of 2 to limit the number of redis connections. You’ll probably want that a lot higher in real life if you are using Sidekiq for other things..
Now create config/initializers/active_job.rb and tell ActiveJob to use sidekiq
Rails.application.config.active_job.queue_adapter = :sidekiq
And let’s add the sidekiq web interace to your config/routes.rb
mount Sidekiq::Web => ‘/sidekiq’
If this was a real app, you’d limit who can get to that engine. If you installed devise, the way to do that is:
authenticate :admin_user do
mount Sidekiq::Web => ‘/sidekiq’
A brief digression to test locally
It’s always nice to change as little as we can, so when everything blows up in our face we can limit our investigations to a small change set. So lets first migrate our development environment to communicate over redis, so the ActiveJobs are run in a seperate process, and that ActionCable will be able to communicate from that process, back to the main web server, back to the client.
Edit `config/cable.yml` to set the development environment use to use redis.
If you don’t have redis installed, use homebrew to do that now:
$ brew install redis
If you don’t have foreman installed, do that now:
$ gem install foreman
Now lets run that app!
$ rails db:migrate
$ foreman start
Using foreman will start both the web interace as well as the sidekiq worker.
Now you should be able to go to http://localhost:3000 in multiple windows and talk with yourself. I really like talking with myself, and I assume that you do as well.
And http://localhost:3000/sidekiq should load up the sidekiq admin console. Validate that everything is working, since now we are going to spin up everything on heroku!
Heroku and Redis
Ok, onto the real deploy now. Create a new app:
$ heroku create
Then we create a redis instance
$ heroku addons:create heroku-redis:hobby-dev
We now need to tell ActionCable where it’s redis server is. Lets find out the answer and put in into the production section of config/cable.yml.
$ heroku config | grep REDIS
Set the right ActionCable websocket address
We need to tell rails where it’s expected to receive the websocket connection from. heroku info will show us our external application. If you deploy on a custom url, you’ll need to add that as well.
$ heroku info
Dynos: web: 1, worker: 1
Git URL: https://git.heroku.com/aqueous-thicket-49913.git
Repo Size: 34 KB
Slug Size: 29 MB
Web URL: https://aqueous-thicket-49913.herokuapp.com/
Lets tell rails about it. Edit `config/environments/production.rb`. Obviously update the urls for your application.
config.action_cable.url = ‘wss://aqueous-thicket-49913.herokuapp.com/cable’
config.action_cable.allowed_request_origins = [ ‘https://aqueous-thicket-49913.herokuapp.com']
Note that wss is WebSockets over over SSL. You should use that, for a number of reasons including but not limited to security. However, if you don’t use SSL, use ws instead.
<%= action_cable_meta_tag %>
Commit and push
First we add the code to repo
$ git add .
$ git commit -a -m “Added heroku configuration”
Now we push to heroku itself:
$ git push heroku master
If that goes well, lets create the database tables and spin up the worker process
$ heroku run rake db:migrate
$ heroku ps:scale worker=1
And now, lets run the app and look at the logs:
$ heroku open
$ heroku open
$ heroku log — tail
We ran open twice, so you could see what was there in two windows. Does it work? Do 3 windows work?
Do you like talking to yourself as much as I do?
But does it scale
Right now you have 1 web dyno, running puma with 5 threads, and 1 worker dyno running sidekiq with 2 concurrent worker. This should be at least 5 redis connections, up to 2 more depending upon how many jobs have gone through sidekiq. Lets look at the redis info:
$ heroku redis:cli
So we see that we have 7 active connections now. As you get more people listening to redis, those will go up. Adding more dynos will push the number of active connections up, so you need to be careful in sizing both your postgres install as well as your redis install.
Let me know how it works!
Originally published: http://willschenk.com/deploying-actioncable-on-heroku/