I'm using Rails 5 in a docker environment and I can get Action Cable to broadcast on a Sidekiq worker perfectly fine, using worker.new.perform.
But for the life of me, I cannot get it to broadcast while using worker.perform_async.
Here is my cable.yml:
default: &default
adapter: redis
url: <%= ENV['REDIS_PROVIDER'] %>
development:
<<: *default
test:
<<: *default
production:
<<: *default
Here is my worker:
class AdminWorker
include Sidekiq::Worker
def perform
ActionCable.server.broadcast 'admin_channel', content: 'hello'
end
end
My Admin Channel:
class AdminChannel < ApplicationCable::Channel
def subscribed
stream_from "admin_channel"
end
def unsubscribed
# Any cleanup needed when channel is unsubscribed
end
end
As I mentioned earlier, this works just fine when calling AdminWorker.new.perform. As soon as I try to run AdminWorker.perform_async, the cable will not broadcast and nothing helpful regarding action cable in the logs. What am I missing here?
I had the same problem. Came across this answer: ActionCable.server.broadcast from the console - worked for me. Basically just changed cable.yml
development:
adapter: async
to
development:
adapter: redis
url: redis://localhost:6379/1
and I was able to broadcast from console, models, Sidekiq worker classes, etc.
For those who wants to dockerize rails and sidekiq, you should run action_cable server separately like
puma -p 28080 cable/config.ru
https://nickjanetakis.com/blog/dockerize-a-rails-5-postgres-redis-sidekiq-action-cable-app-with-docker-compose
https://github.com/kchrismucheke/dockersample/blob/da5923899fb17682fabed041bef5381ed3fd91ab/config/application.rb#L57-L62
In development environment
By default, ActionCable would run only on the rails server only. And it would not work in Sidekiq, console, cron jobs, etc.
Because in config/cable.yml development server adapter is set to async
Solution:
For ActionCable inter-process broadcasting, you have to set the adapter to Redis
In config/cable.yml
development:
adapter: redis
url: redis://localhost:6379/1
channel_prefix: project_name_development
Related
I have been trying to set up an action cable with ruby on rails version 7.0.4 and fail to do
In the app/channels/application_cable.rb my configuration file is as below. As per official documentation, every connection should be identified, so I decided to identify that via random string:
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by: random_string
def random_string
return SecureRandom.base64()
end
end
end
I have only a single channel, called room_channel for learning purposes:
class RoomChannel < ApplicationCable::Channel
def subscribed
stream_from "room_channel"
end
def unsubscribed
# Any cleanup needed when channel is unsubscribed
end
end
in the app/javascript/channels/consumer.js I created below configuration and set up at least 1 consumer, so my app could work
import { createConsumer } from "#rails/actioncable"
export default createConsumer()
createConsumer(getWebSocketURL)
function getWebSocketURL() {
return "ws://159.61.241.9:8000/cable"
}
My config/environments/development.rb file is as below for action cable related settings:
config.action_cable.url = "ws://localhost:6379/cable"
config.action_cable.allowed_request_origins = ['http://159.65.241.9:8000']
config.action_cable.mount_path="ws://localhost:6379/cable"
my cable.yml file in the config as below:
development:
adapter: redis
url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
test:
adapter: redis
url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
production:
adapter: redis
url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
channel_prefix: action_cable_chat_app_production
I started up the redis server already.
The error I end up with is as below:
Can someone please help me why WebSocket tries to start at 8000 port even though I mount the server in 6379 in the config file ?
Problem is in your JS
function getWebSocketURL() {
return "ws://159.61.241.9:8000/cable"
}
It tries to connect to "ws://159.61.241.9:8000/cable", but you started at 6379 port. Change it to "ws://159.61.241.9:6379/cable". If 159.61.241.9 is not your public IP, than change to "ws://localhost:6379/cable"
Last week RedisToGo was terminated on Herokum, leaving me with no choice but to find an alternative. I got a new subscription from Heroku: Heroku Redis.
Everything seems to work fine (all tasks/jobs) except things related to ActionCable.
[ActionCable] [#######] Registered connection (#########)
2022-08-17T00:40:23.213184+00:00 app[web.1]: #<Thread:0x00007e######/app/vendor/bundle/ruby/2.6.0/gems/actioncable-5.1.7/lib/action_cable/subscription_adapter/redis.rb:144 run> terminated with exception (report_on_exception is true):
2022-08-17T00:40:23.213206+00:00 app[web.1]: /app/vendor/bundle/ruby/2.6.0/gems/redis-4.1.4/lib/redis/client.rb:268:in `rescue in io': Connection lost (ECONNRESET) (Redis::ConnectionError)
I spent the last few days trying all sort of trick, but nothing work.
My cable.yml file looks like:
production:
adapter: redis
url: <%= ENV['REDIS_URL'] %>
my redis.rb file looks like this:
uri = if Rails.env == 'production'
URI.parse(ENV["REDIS_URL"])
else
URI.parse("redis://localhost:6379")
end
Resque.redis = Redis.new(host: uri.host, port: uri.port, password: uri.password, ssl_params: { verify_mode: OpenSSL::SSL::VERIFY_NONE })
Am I missing certain parameters that results in the error ?
I use Heroku Redis and here's what my cable.yml looks like:
development:
adapter: redis
url: redis://localhost:6379/1
channel_prefix: myapp_development
test:
adapter: redis
url: redis://localhost:6379/1
channel_prefix: myapp_test
production:
adapter: redis
url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
channel_prefix: myapp_production
ssl_params:
verify_mode: <%= OpenSSL::SSL::VERIFY_NONE %>
If your tasks and jobs don't use Redis (if ActionCable is the only thing trying to use Redis) then your Redis might not actually be set up properly. Here's the documentation.
Check to make sure Heroku actually set a REDIS_URL env variable:
$ heroku config | grep REDIS
> REDIS_URL: redis://h:asdfqwer1234asdf#ec2-111-1-1-1.compute-1.amazonaws.com:111
Make sure your instance has a normal configuration:
https://devcenter.heroku.com/articles/heroku-redis#configuring-your-instance
Two other helpful sources:
How to solve the SSL error for Redis 6 on Heroku
ActionCable Redis adapter crashes with Heroku Redis 6
rails 7
AWS Aurora RDS Cluster with one write endpoint and one read endpoint
I would like to configure my Rails application to use one endpoint to write and one endpoint to read for the production environment, and a single endpoint for reading/writing in the test environment.
Reading through the documentation, it seems as what I need to do, is that I need to do the following (assuming a production .
In /config/database.yml:
production:
primary:
database: my_primary_database
username: root
password: <%= ENV['PRIDUCTION_ROOT_PASSWORD'] %>
adapter: mysql2
primary_replica:
database: my_primary_database
username: root
password: <%= ENV['PRIDUCTION_ROOT_PASSWORD'] %>
adapter: mysql2
replica: true
test:
database: my_test_database
username: root
password: <%= ENV['TEST_PASSWORD'] %>
adapter: mysql2
Note: the documentation mentions that the username for the writers and replicas should be different, but the AWS Aurora cluster will not allow me to do that. Is this a recommendation or a must?
And this is what I should have in my /app/models/application_record.rb:
class ApplicationRecord < ActiveRecord::Base
primary_abstract_class
if Rails.env == 'production'
connects_to database: { writing: :primary, reading: :primary_replica }
end
end
Then, run the following command:
rails g active_record:multi_db
Which will generate the file: config/initializers/multi_db.rb and inside that file do:
Rails.application.configure do
if Rails.env == 'production'
config.active_record.database_selector = { delay: 2.seconds }
config.active_record.database_resolver =
ActiveRecord::Middleware::DatabaseSelector::Resolver
config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
end
end
Am I missing anything, or do I need to add the following to config/environments/production.rb?
config.active_record.writing_role = :default
config.active_record.reading_role = :readonly
Anything else?
I used actioncable on rails 5 app. Below code work in controller but not in console.
ActionCable.server.broadcast "connector_app", content: "test message"
Response:
[ActionCable] Broadcasting to connector_app: {:content=>"test message"}
=> nil
cable.yml
development:
adapter: redis
url: redis://localhost:6379/1
test:
adapter: async
production:
adapter: redis
url: redis://localhost:6379/1
Contoller code( It works properly):
def sample_socket_message
ActionCable.server.broadcast "connector_app",
content: "test message",
head :ok
end
Problem Solved:
Forget to add below code in config/initializer/redis.rb
$redis = Redis.new(:host => 'localhost', :port => 6379)
The default behavior for ActionCable in development and test mode is to use the async adapter, which operates within the same process only. For inter-process broadcasting, you will need to switch to the redis adapter.
Since you are using redis adapter in development that's why it is working in controller and not working in console.
cable.yml
test:
adapter: redis
url: redis://localhost:6379/1
I'm currently trying to perform some tests on a rails app that has two database connections.
As of course I wouldn't like to delete the 2nd database each time I perform a test, I have set up a mechanism to connect to another 2nd-DB if the environment is "test" (see below).
My question: How could I tell rake test to also perform a rake db:test:prepare on the 2nd DB?
This query already touched my problem but couldn't help me completely.
Some code to explain:
My ActiveRecord-Classes that connect to the 2nd db inherit from the following class InformixConnect:
class InformixConnect < ActiveRecord::Base
self.abstract_class = true
case Rails.env
when 'production', 'development'
establish_connection :development_informix
when 'test'
establish_connection :test_informix_dummy
else
raise "Please specify a correct informix Environment."
end
end
like
class RenewalNotify < InformixConnect
set_table_name :renewal_notify
set_primary_key :renewal_notify_id
end
(yes, I know... The schema does not follow the rails conventions. It's a legacy one)
database.yml contains
...
development:
adapter: postgresql
database: rails_development
host: 127.0.0.1
reconnect: true
username: rails
password: guessone
...
development_informix:
adapter: informix
database: SOMETHING
username: yyy
password: yyy
test_informix_dummy:
adapter: sqlite3
database: db/test_informix_dummy.sqlite3
...
# Warning: The database defined as 'test' will be erased and
# re-generated from your development database when you run 'rake'.
# Do not set this db to the same as development or production.
test: &TEST
adapter: sqlite3
database: db/test.db
verbosity: silent
timeout: 5000
So I'd need to setup the "test_informix_dummy" db each time I perform a rake test