I am using 'Sidekiq' to schedule reminder about any task on given time. Its working perfect. Now i want to append it to notify on my navbar, for it i am using 'Private Pub' to publish the reminder message.
Here is code of Sidekiq Worker.
class ReminderWorker
include Sidekiq::Worker
def perform(args)
reminder = Reminder.find(args['id'])
reminder.activate = true
PrivatePub.publish_to("reminder", message: reminder)
reminder.save
end
end
and inside "application.js" i am using alert for testing but it is not working.
PrivatePub.subscribe("reminder", function(data, channel) {
return alert('Remarks ='+ data.message.remarks);
});
Am i missing some thing? As it is possible to publish data through Private Pub in rb file accourding to Ryan http://railscasts.com/episodes/316-private-pub?autoplay=true
This gem has been updated last time three years ago and seems not maintained.
Maybe it's better to use newest rails solution like ActionCabble? https://github.com/rails/rails/tree/master/actioncable
I found this issue, that proofs that ActionCabble can be integrated to ActiveJob or other background workers:
https://github.com/rails/rails/issues/22897
Related
I understand that from the console I can run heroku restart. What I'd like to do is to have a button in my app (admin console), where pushing that button runs a heroku restart. Does anyone know how to do that and if it's possible? So the code would look something like this:
<button id="heroku_restart">Restart</button>
$("#heroku_restart").click(function() {
$.post('/restart', {}).done(function(response) {
alert(response)
})
})
class AdminsController
# this is the action mapped to the route /restart
def restart
# code for heroku restart
end
end
So per #vpibano, as of this writing, doing it with the platform-api is a breeze. Here's the action POSTed to by a button on my website:
def restart
heroku = PlatformAPI.connect_oauth(ENV["heroku_oauth_token"])
heroku.dyno.restart_all("lastmingear")
render nothing: true
end
As per the description mentioned in the post, the one way of doing it is :
1) First locate the server.pid file
pid_file = Rails.root.join("tmp", "pids", "server.opid")
2) Now, truncate the contents of the file
File.open(pid_file, "w") {|f| f.truncate(0)}
3) Finally, run the server using Kernel module:
Kernel.exec("rails s")
Note: As rightly, mentioned by #vpibano you will need authentication to access your app.
This is not a working model but a way to achieve the requirement.
Hope it helps!!
I have an issue with importing a lot of records from a user provided excel file into a database. The logic for this is working fine, and I’m using ActiveRecord-import to cut down on the number of database calls. However, when a file is too large, the processing can take too long and Heroku will return a timeout. Solution: Resque and moving the processing to a background job.
So far, so good. I’ve needed to add CarrierWave to upload the files to S3 because I can’t just hold the file in memory for the background job. The upload portion is also working fine, I created a model for them and am passing the IDs through to the queued job to retrieve the file later as I understand I can’t pass a whole ActiveRecord object through to the job.
I’ve installed Resque and Redis locally, and everything seems to be setup correctly in that regard. I can see the jobs I’m creating being queued and then run without failing. The job seems to run fine, but no records are added to the database. If I run the code from my job line by line in the console, the records are added to the database as I would expect. But when the queued jobs I’m creating run, nothing happens.
I can’t quite work out where the problem might be.
Here’s my upload controller’s create action:
def create
#upload = Upload.new(upload_params)
if #upload.save
Resque.enqueue(ExcelImportJob, #upload.id)
flash[:info] = 'File uploaded.
Data will be processed and added to the database.'
redirect_to root_path
else
flash[:warning] = 'Upload failed. Please try again.'
render :new
end
end
This is a simplified version of the job with fewer sheet columns for clarity:
class ExcelImportJob < ApplicationJob
#queue = :default
def perform(upload_id)
file = Upload.find(upload_id).file.file.file
data = parse_excel(file)
if header_matches? data
# Create a database entry for each row, ignoring the first header row
# using activerecord-import
sales = []
data.drop(1).each_with_index do |row, index|
sales << Sale.new(row)
if index % 2500 == 0
Sale.import sales
sales = []
end
end
Sale.import sales
end
def parse_excel(upload)
# Open the uploaded excel document
doc = Creek::Book.new upload
# Map rows to the hash keys from the database
doc.sheets.first.rows.map do |row|
{ date: row.values[0],
title: row.values[1],
author: row.values[2],
isbn: row.values[3],
release_date: row.values[5],
units_sold: row.values[6],
units_refunded: row.values[7],
net_units_sold: row.values[8],
payment_amount: row.values[9],
payment_amount_currency: row.values[10] }
end
end
# Returns true if header matches the expected format
def header_matches?(data)
data.first == {:date => 'Date',
:title => 'Title',
:author => 'Author',
:isbn => 'ISBN',
:release_date => 'Release Date',
:units_sold => 'Units Sold',
:units_refunded => 'Units Refunded',
:net_units_sold => 'Net Units Sold',
:payment_amount => 'Payment Amount',
:payment_amount_currency => 'Payment Amount Currency'}
end
end
end
I can probably have some improved logic anyway as right now I’m holding the whole file in memory, but that isn’t the issue I’m having – even with a small file that has only 500 or so rows, the job doesn’t add anything to the database.
Like I said my code worked fine when I wasn’t using a background job, and still works if I run it in the console. But for some reason the job is doing nothing.
This is my first time using Resque so I don’t know if I’m missing something obvious? I did create a worker and as I said it does seem to run the job. Here’s the output from Resque’s verbose formatter:
*** resque-1.27.4: Waiting for default
*** Checking default
*** Found job on default
*** resque-1.27.4: Processing default since 1508342426 [ExcelImportJob]
*** got: (Job{default} | ExcelImportJob | [15])
*** Running before_fork hooks with [(Job{default} | ExcelImportJob | [15])]
*** resque-1.27.4: Forked 63706 at 1508342426
*** Running after_fork hooks with [(Job{default} | ExcelImportJob | [15])]
*** done: (Job{default} | ExcelImportJob | [15])
In the Resque dashboard the jobs aren’t logged as failed. They get executed and I can see an increment in the ‘processed’ jobs on the stats page. But as I say the DB remains untouched. What’s going on? How can I debug the job more clearly? Is there a way to get into it with Pry?
It looks like my problem was with Resque.enqueue(ExcelImportJob, #upload.id).
I changed my code to ExcelImportJob.perform_later(#upload.id) and now my code actually runs!
I also added a resque.rake task to lib/tasks as described here: http://bica.co/2015/01/20/active-job-resque/.
That link also notes how to use rails runner to call the job without running the full Rails server and triggering the job, which is useful for debugging.
Strangely, I didn't quite manage to get the job to print anything to STDOUT as suggested by #hoffm but at least it led me down a good avenue of inquiry.
I still don't fully understand the difference between why calling Resqueue.enqueue still added my jobs to the queue and indeed seemed to run them, but the code wasn't executed, so if someone has a better grasp and an explanation, that would be much appreciated.
TL;DR: calling perform_later rather than Resque.enqueue fixed the problem but I don't know why.
I have 2 Sidekiq workers:
Foo:
# frozen_string_literal: true
class FooWorker
include Sidekiq::Worker
sidekiq_options queue: :foo
def perform
loop do
File.open(File.join(Rails.root, 'foo.txt'), 'w') { |file| file.write('FOO') }
end
end
end
Bar:
# frozen_string_literal: true
class BarWorker
include Sidekiq::Worker
sidekiq_options queue: :bar
def perform
loop do
File.open(File.join(Rails.root, 'bar.txt'), 'w') { |file| file.write('BAR') }
end
end
end
Which has pretty the same functionality, both runs on different queues and the yaml file looks like this:
---
:queues:
- foo
- bar
development:
:concurrency: 5
The problem is, even both are running and showing in the Busy page of Sidekiq UI, only one of them will actually create a file and put contents in. Shouldn't Sidekiq be multi-threaded?
Update:
this happens only on my machine
i created a new project with rails new and same
i cloned a colleague project and ran his sidekiq and is working!!!
i used his sidekiq version, not working!
New Update:
this happens also on my colleague machine if he clone my project
if I run 2 jobs with a finite loop ( like 10 times do something with a sleep), first job will be executed and then the second, but after the second finishes and start again both will work on same time as expected -- everyone who cloned the project from: github.com/ArayB/sidekiq-test encountered the problem.
It's not an issue with Sidekiq. It's an issue somewhere in Ruby/MRI/Thread/GIL. Google for more info, but my understanding is that sometimes threads aren't real threads (see "green threads") so really just simulate threading. The important thing is that only one thread can execute at a time.
It's interesting that with only two threads the system isn't giving time to the second thread. No idea why, but it must realize it's mistake when you run it again.
Interestingly if you run your same app but instead fire off 10 TestWorkers (and tweak the output so you can tell the difference) sidekiq will run all 10 "at once".
10.times {|i| TestWorker.perform_async(i) }
Here is the tweaked worker. Be sure to flush the output cause that can also cause issues with threading and TTY buffering not reflecting reality.
class TestWorker
include Sidekiq::Worker
def perform(n)
10.times do |i|
puts "#{n} - #{i} - #{Time.current}"
$stdout.flush
sleep 1
end
end
end
Some interesting links:
https://en.wikipedia.org/wiki/Green_threads
http://ruby-doc.org/core-2.4.1/Thread.html#method-c-pass
https://github.com/ruby/ruby/blob/v2_4_1/thread.c
Does ruby have real multithreading?
I am currently enabling queueing in on of my app wrote in Ruby. I use sidekiq and have define a worker class as below:
class Worker
include Sidekiq::Worker
def perform(params)
sleep 10
##logger.info("--------------------------")
##logger.info("Request received")
##logger.info("--------------------------")
end
end
I am calling it from my main entry point called 'api.rb'
post '/receive/?' do
##logger.debug("/retrieve endpoint received")
Worker.perform_async #params
end
this working fine and each time the sleep is done, the next queued task is started.
In my case, I need to unqueued or start the next item queued only when I decide it. it will be triggered by an external event.
in my 'api.rb', I have added:
post '/response/?' do
next_task
end
The way the code works is that '/receive' can queued 10 requests. the first request will triggered a specific action (sent a post command to a server).
I expect the remote server to send me back a request through '/response' to tell me that the action is finished. when this response is received, I use the 'next_task' api to remove the previous task which was running and now completed and move to the next queued one.
Any idea, how to create a custom trigger to unqueue and start the new job. Is there SIGNAL which allow me to avoid the sidekiq framework to unqueue until I send a specific signal.
Merci
To delete a job in a Sidekiq queue, you would have to iterate the whole queue. It is not an idiomatic use of the queue.
I am afraid I don't understand what exactly you are trying to do. Just remember that you can store state outside of the Sidekiq queue, for example you can have a model for the Job:
post '/receive/?' do
job = Job.create(#params)
Worker.perform_in(10.seconds)
end
and then in the worker:
def perform
job = Job.find_oldest_unexecuted
if job
job.execute!
else
# wait another 10 seconds
Worker.perform_in(10.seconds)
end
end
And in Job:
class Job < ActiveRecord::Base
def self.find_oldest_unexecuted
where(executed_at: nil).order(:id).first
end
def execute!
# do what needs to be done here
update_attribute(:executed_at, Time.zone.now)
end
end
I'am new in ruby and i want to send mail every days at 9PM with data so any ideas to do this in ruby ( Thread )
this my code to send mail :
message = <<MESSAGE_END
From: TEST <test#test.com>
To: A Test User <tes2t#test.com>
Subject: Feedback mail status reminder
This is a test e-mail message.
MESSAGE_END
Net::SMTP.start('localhost') do |smtp|
smtp.send_message message, 'test#test.com',
'test2#test.com'
end
Thanks
Sidekiq is also a better option to do it. You can run your job at specific timings and it will run using redis server in the backgorund.
Create Workers in 'app/' folder and call it from the controller/Model method. Setup an initializer file sidekiq.rb to specify the Redis queue for all environments to boot up.
Check the below given links for reference.
https://github.com/mperham/sidekiq
http://sidekiq.org/
http://railscasts.com/episodes/366-sidekiq
Hope it helps.
you can use the gem 'whenever', like this:
every :day, :at => '12:00am' do
runner "AAA.xxx"
end
and your code:
message = <<MESSAGE_END
From: TEST
To: A Test User
Subject: Feedback mail status reminder
This is a test e-mail message.
MESSAGE_END
as a method xxx
In ruby on rails, maybe you can use rufus-scheduler. Create a rb file under config\initializers. Add some code like:
scheduler.cron '0 21 * * *' do
# call your function method or implment the function
end
For more detail, please refer to rufus-scheduler