We have been using ActionCable on Heroku for some time, and overall it works well. However, we see the H15 Idle Connection
error many times per day. They always have path=/cable
and a long service
time, so the connection was definitely live and healthy for a while.
Dec 2016 08:32:22.057 heroku router - - at=error code=H15 desc="Idle connection"
method=GET path="/cable" host=<our host> dyno=web.2 connect=1ms service=928755ms status=503
I believe our setup is very standard, and closely follows the Rails docs for ActionCable:
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
self.current_user = find_verified_user
end
protected
def find_verified_user
if current_user = User.find_by(id: cookies.signed[:user_id])
current_user
else
# reject_unauthorized_connection
end
end
end
end
We have three simple channels like this:
class ActivitiesChannel < ApplicationCable::Channel
def subscribed
stream_from "activities_#{current_user.id}" if current_user
end
end
Edit to Add - Javascript code:
app/assets/javascripts/channels/setup.js
:
//= require cable
this.App || (this.App = {});
App.cable = ActionCable.createConsumer();
app/assets/javascripts/channels/notifications.js
:
App.notifications = App.cable.subscriptions.create('NotificationsChannel', {
received: function(data) {
return this.showMessage(data);
},
showMessage: function(data) {
showNotice(data.message);
}
});
I'm fairly new with ActionCable and WebSockets, so I'm not sure how to troubleshoot this one. We are running Rails 5.0.0.1 with Ruby 2.3.1
Any help, context, or troubleshooting tips would be much appreciated!
EDIT: I just realized that it is an old question. Not sure, why this popped up on my feed. Anyway, it might help someone in the future.
I believe this is happening when someone keeps the page open without any activity on the channel and when the connection stays open but idle for 55 seconds, Heroku closes the connection with the error Idle connection
. You have to keep the connection alive, by implementing some sort of ping system which periodically sends a message to the server and the server simply sends an ok
in response. A simple example is:
app/assets/javascripts/channels/notifications.js
:
App.notifications = App.cable.subscriptions.create('NotificationsChannel', {
received: function(data) {
// ignore the data when the type is 'pong' as it is just server's response to our 'ping'
if(data.type !== 'pong'){
return this.showMessage(data);
}
},
showMessage: function(data) {
showNotice(data.message);
},
ping: function() {
return this.perform('ping', {
message: {"event": "ping"}
});
}
});
document.addEventListener("DOMContentLoaded", function(event) {
// this will send a ping event every 30 seconds
setInterval(function(){
App.notifications.ping()
}, 30000);
});
On the server-side, you need to send a response message e.g 'pong' to the 'ping' event. You can send that response to the same user, or broadcast to all users, depending on your channels setup and requirement.
class NotificationsChannel < ApplicationCable::Channel
def subscribed
stream_from "notifications_#{current_user.id}" if current_user
end
# data is not required, you can choose to ignore it or not send it at all from the client-side.
def ping(data)
NotificationsChannel.broadcast_to("notifications_#{current_user.id}", { message: 'pong' })
end
end
This should keep the connection alive even if the user is idle.
I have not worked with ActionCable since it's first release. Let me know if something does not work or any syntax error, I will try to fix.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With