Rails set(wait: 2.minutes) method for active job does not works - ruby-on-rails

Creating an background job with the resque_scheduler gem on Redis server.
class Estamps::OrderAssignment < ActiveRecord::Base
after_save :enqueue_check_status
def enqueue_check_status
AutoRejectionJob.set(wait: 2.minutes).perform_later(self.id)
end
end
class AutoRejectionJob < ActiveJob::Base
queue_as :default
def perform(*args)
order_assignment_id = args[0]
order_assignment = Estamps::OrderAssignment.find(order_assignment_id)
if order_assignment.status_id == 1 || order_assignment.status_id == nil
order_assignment.status_id = 3
order_assignment.save!
end
end
end
On creation of OrderAssignment record or when updated after 2 minutes it should run AutoRejection Job. Here the prob is the set(wait: 2.minutes) does not seem to run, i.e.
AutoRejectionJob.perform_later(self.id)
works perfectly fine, but
AutoRejectionJob.set(wait: 2.minutes).perform_later(self.id)
does nothing. Haven't been able to rectify the issue. Newbie to Rails so please help.

I see no problem with your code.
I checked : .set(wait: 2.minutes) works as expected in rails 5.0.2 on top of ruby 2.4.0
So does your call of the job.
The way I see it, you're trying to set a status used elsewhere.
Probably, the error is due to the OrderAssignment being manipulated in an outside treatment (destroyed ?)
Since you said you're new to rails (I suppose that's what "newbie" means) I'm going to make some suggestions. Disregard them if you're past that ...
There also are some great debugging tools out there to help you find what's going on : byebug, better_errors, pry and of course, the rails console.
Do yourself a favor : try them.
When I can't find my way around some behavior that goes beyond my grasp, I use some "puts", and some "try / catch errors" structures (begin rescue ensure in ruby)... :
def perform(*args)
puts "####### JOB TRIGGERED ######"
begin
order_assignment_id = args[0]
order_assignment = Estamps::OrderAssignment.find(order_assignment_id)
puts "#{order_assignment.inspect}"
if order_assignment.status_id == 1 || order_assignment.status_id == nil
order_assignment.status_id = 3
order_assignment.save!
end
puts "####### JOB DONE ######"
rescue StandardError => e
# ... display e.message ...
ensure
#...
end
end

check your rails version.
check your rails logs ( log folder, all the jobs will write message to log files when performed )

Related

How to test whether any exception was rescued?

Is there a way to find out whether any exception was raised and rescued during the execution of some code?
Preferably in tests written with ActiveSupport::TestCase and not RSpec
Is there any global ruby exception stack or something, which I could check?
If you want to be a monster, you can instrument the errors themselves:
class StandardError
##called = false
def initialize
##called = true
super
end
def self.called
##called
end
end
#test it out like so:
def raise_arg_error
raise ArgumentError
rescue
end
puts ArgumentError.called #false
raise_arg_error
puts ArgumentError.called #true
Great for ad hoc sanity checks. Terrible for production code.
As clarified in the comments, OP needed it for debug purposes as opposed to write tests with it.
Kernel#set_trace_func lets you intercept low level events such as an error being raised:
set_trace_func(proc do |event, *_|
puts 'Raised!' if event == 'raise'
end)
raise 'Oh, no!' rescue :foo
You can run #set_trace_func before the code you were trying to debug. If no exception was raised, but a raise was registered by the hook - someone rescued it.
This can create some noise depending on what you are executing. Fortunately, you can filter it down by file and line:
set_trace_func(proc do |event, file, line, *_|
if event == 'raise' && file == 'test.rb' && line == 42
puts "Raised on #{file}:#{line}!"
end
end)
It even gives you the binding, so you can drop down a debugger:
require 'irb'
set_trace_func(proc do |event, *_, error_binding, _|
error_binding.irb if event == 'raise'
end)
def foo
bar = 42
raise 'Oh, no!' rescue :baz
end
foo
# Opens an irb
# > bar # => 42

How can I avoid deadlocks on my database when using ActiveJob in Rails?

I haven't had a lot of experience with deadlocking issues in the past, but the more I try to work with ActiveJob and concurrently processing those jobs, I'm running into this problem. An example of one Job that is creating it is shown below. The way it operates is I start ImportGameParticipationsJob and it queues up a bunch of CreateOrUpdateGameParticipationJobs.
When attempting to prevent my SQL Server from alerting me to a ton of deadlock errors, where is the cause likely happening below? Can I get a deadlock from simply selecting records to populate an object? Or can it really only happen when I'm attempting to save/update the record within my process_records method below when saving?
ImportGameParticipationsJob
class ImportGameParticipationsJob < ActiveJob::Base
queue_as :default
def perform(*args)
import_participations(args.first.presence)
end
def import_participations(*args)
games = Game.where(season: 2016)
games.each do |extract_record|
CreateOrUpdateGameParticipationJob.perform_later(extract_record.game_key)
end
end
end
CreateOrUpdateGameParticipationJob
class CreateOrUpdateGameParticipationJob < ActiveJob::Base
queue_as :import_queue
def perform(*args)
if args.first.present?
game_key = args.first
# get all particpations for a given game
game_participations = GameRoster.where(game_key: game_key)
process_records(game_participations)
end
end
def process_records(participations)
# Loop through participations and build record for saving...
participations.each do |participation|
if participation.try(:player_id)
record = create_or_find(participation)
record = update_record(record, participation)
end
begin
if record.valid?
record.save
else
end
rescue Exception => e
end
end
end
def create_or_find(participation)
participation_record = GameParticipation.where(
game_id: participation.game.try(:id),
player_id: participation.player.try(:id))
.first_or_initialize do |record|
record.game = Game.find_by(game_key: participation.game_key)
record.player = Player.find_by(id: participation.player_id)
record.club = Club.find_by(club_id: participation.club_id)
record.status = parse_status(participation.player_status)
end
return participation_record
end
def update_record(record, record)
old_status = record.status
new_status = parse_status(record.player_status)
if old_status != new_status
record.new_status = record.player_status
record.comment = "status was updated via participations import job"
end
return record
end
end
They recently updated and added an additional option you can set that should help with the deadlocking. I had the same issue and was on 4.1, moving to 4.1.1 fixed this issue for me.
https://github.com/collectiveidea/delayed_job_active_record
https://rubygems.org/gems/delayed_job_active_record
Problems locking jobs
You can try using the legacy locking code. It is usually slower but works better for certain people.
Delayed::Backend::ActiveRecord.configuration.reserve_sql_strategy = :default_sql

Errors like "NoMethodError: undefined method `sweep' for #<Hash …" after downgrading to Rails 3 from Rails 4

We upgraded to Rails 4, had some major issues and downgraded again (reverted the upgrade commit).
We then got errors like
NoMethodError (undefined method `sweep' for #<Hash:0x007f01ab44a940>):
seemingly because Rails 4 stores a flash in the session in a way that Rails 3 can't read.
What is a good way to solve this?
We ended up solving this by patching Rails itself to catch this error and delete the borked flash. This means that it self-heals quite transparently.
We also made sure to only apply this patch on Rails 3, so it doesn't cause issues when we make another attempt at upgrading to Rails 4.
We stuck this in config/initializers/rails4_to_rails3_downgradability.rb:
if Rails::VERSION::MAJOR == 3
module ActionDispatch
class Flash
def call(env)
if (session = env['rack.session']) && (flash = session['flash'])
# Beginning of change!
if flash.respond_to?(:sweep)
flash.sweep
else
session.delete("flash")
end
# End of change!
end
#app.call(env)
ensure
session = env['rack.session'] || {}
flash_hash = env[KEY]
if flash_hash
if !flash_hash.empty? || session.key?('flash')
session["flash"] = flash_hash
new_hash = flash_hash.dup
else
new_hash = flash_hash
end
env[KEY] = new_hash
end
if session.key?('flash') && session['flash'].empty?
session.delete('flash')
end
end
end
end
end
Faced the same issue, resolved by telling users to clear cookies. Might not a solution for everyone, just pointing out that it's an option.

Check whether I am in a delayed_job process or not

I have a Rails app in which I use delayed_job. I want to detect whether I am in a delayed_job process or not; something like
if in_delayed_job?
# do something only if it is a delayed_job process...
else
# do something only if it is not a delayed_job process...
end
But I can't figure out how. This is what I'm using now:
IN_DELAYED_JOB = begin
basename = File.basename $0
arguments = $*
rake_args_regex = /\Ajobs:/
( basename == 'delayed_job' ) ||
( basename == 'rake' && arguments.find{ |v| v =~ rake_args_regex } )
end
Another solution is, as #MrDanA said:
$ DELAYED_JOB=true script/delayed_job start
# And in the app:
IN_DELAYED_JOB = ENV['DELAYED_JOB'].present?
but they are IMHO weak solutions. Can anyone suggest a better solution?
The way that I handle these is through a Paranoid worker. I use delayed_job for video transcoding that was uploaded to my site. Within the model of the video, I have a field called video_processing which is set to 0/null by default. Whenever the video is being transcoded by the delayed_job (whether on create or update of the video file), it will use the hooks from delayed_job and will update the video_processing whenever the job starts. Once the job is completed, the completed hook will update the field to 0.
In my view/controller I can do video.video_processing? ? "Video Transcoding in Progress" : "Video Fished Transcoding"
Maybe something like this. Add a field to your class and set it when your invoke the method that does all your work from delayed job:
class User < ActiveRecord::Base
attr_accessor :in_delayed_job
def queue_calculation_request
Delayed::Job.enqueue(CalculationRequest.new(self.id))
end
def do_the_work
if (in_delayed_job)
puts "Im in delayed job"
else
puts "I was called directly"
end
end
class CalculationRequest < Struct.new(:id)
def perform
user = User.find(id)
user.in_delayed_job = true
user.do_the_work
end
def display_name
"Perform the needeful user Calculations"
end
end
end
Here is how it looks:
From Delayed Job:
Worker(host:Johns-MacBook-Pro.local pid:67020)] Starting job worker
Im in delayed job
[Worker(host:Johns-MacBook-Pro.local pid:67020)] Perform the needeful user Calculations completed after 0.2787
[Worker(host:Johns-MacBook-Pro.local pid:67020)] 1 jobs processed at 1.5578 j/s, 0 failed ...
From the console
user = User.first.do_the_work
User Load (0.8ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 101]]
I was called directly
This works for me:
def delayed_job_worker?
(ENV["_"].include? "delayed_job")
end
Unix will set the "_" environment variable to the current command.
It'll be wrong if you have a bin script called "not_a_delayed_job", but don't do that.
How about ENV['PROC_TYPE']
Speaking only of heroku... but when you're a worker dyno, this is set to 'worker'
I use it as my "I'm in a DJ"
You can create a plugin for delayed job, e.g. create the file is_dj_job_plugin.rb in the config/initializers directory.
class IsDjJobPlugin < Delayed::Plugin
callbacks do |lifecycle|
lifecycle.around(:invoke_job) do |job, *args, &block|
begin
old_is_dj_job = Thread.current[:is_dj_job]
Thread.current[:is_dj_job] = true
block.call(job, *args) # Forward the call to the next callback in the callback chain
Thread.current[:is_dj_job] = old_is_dj_job
end
end
end
def self.is_dj_job?
Thread.current[:is_dj_job] == true
end
end
Delayed::Worker.plugins << IsDjJobPlugin
You can then test in the following way:
class PrintDelayedStatus
def run
puts IsDjJobPlugin.is_dj_job? ? 'delayed' : 'not delayed'
end
end
PrintDelayedStatus.new.run
PrintDelayedStatus.new.delay.run

How do you access database error information when using Rails and Postgres

I am using find_by_sql to connect to a Postgres database and execute a database function. The database function executes a number of SQL statements and raises exceptions as required.
How do I trap the error code and error message raised by the Postgres function in Rails?
def self.validate_email(systemuserid, emailaddress)
begin
result = (self.find_by_sql(["SELECT fn_systemuser_validate_email(?, ?) AS returncode",
systemuserid, emailaddress])).first
rescue => err
# I want to get access to the error code and error message here and act accordingly
# errno = ??
# errmsg = ??
if errno == 10000
end
end
return result[:returncode]
end
I started by trying to find this information in the connection object - no such luck.
Any help much appreciated.
Currently active record replaces the original error with an internal one without passing on the original with the new error. I cant understand why any one would want this.
So the only solution right now is to monkey patch ;)
module ActiveRecord
module ConnectionAdapters
class AbstractAdapter
def translate_exception(e, message)
ActiveRecord::WrappedDatabaseException.new(message,e)
end
# Replaces
# def translate_exception(e, message)
# # override in derived class
# ActiveRecord::StatementInvalid.new(message)
# end
end
end
end
Now you can get the original_exception.
def self.validate_email(systemuserid, emailaddress)
begin
result = (self.find_by_sql(["SELECT fn_systemuser_validate_email(?, ?) AS returncode", systemuserid, emailaddress])).first
rescue ActiveRecord::WrappedDatabaseException => e
pgerror = e.original_exception
# Exact api depends on PG version, check the docs for your version.
puts "Doing my stuff: #{pgerror.result.result_error_message}"
end
end
This works with pg version 0.11 and Rails 3.0.9. Will probably work with later versions.
I let this one go for a while, (9 months!) but picked it up again due to a new impetus.
I used the monkey patch suggested by Darwin (sorry that the pull request didnt get the vote) and have then discovered that the code I need (with reference to http://deveiate.org/code/pg/PG/Result.html) is as follows:
rescue ActiveRecord::WrappedDatabaseException => e
pgerror = e.original_exception
sqlstate = pgerror.result.error_field(PG::Result::PG_DIAG_SQLSTATE )
end
Just look at .cause.
begin
# whatever.
rescue => err
p err.cause
end
You can user the errors array of your model, like others database:
errmsg = YourModel.errors[0].full_messages

Resources