Reproduce ActiveJob::DeserializationError on rails console - ruby-on-rails

I need for testing purpose to reproduce an ActiveJob::DeserializationError on rails console, I need to test the behaviour manually on testing environment.
class WrapperJob < ApplicationJob
discard_on ActiveJob::DeserializationError
def perform(event)
# doing something
end
end
The WrapperJob aims to perform something on the event, but during the enqueue time it might happen to remove event from database. I need a way please to reproduce ActiveJob::DeserializationError. right now I tried to pass a missing event, or a missing GlobalID but actually fail on the # doing something part instead of raising DeserializationError.
Thanks.

Related

How to write into rails log from a job?

I am facing an issue concerning logs. I built a rails app using ActiveJob with Sidekiq.
I want to write an entry into rails logs from a background job.
Usually, when I want to write an entry, I do something like this (in a controller for instance):
Rails.logger.fatal "BIG ISSUE!!!"
So I wanted to do the same but this time, in a background job:
class MyJob < ApplicationJob
queue_as :default
def perform(*args)
Rails.logger.fatal"FATAL TEST"
end
end
However nothing is written in the rails logs. Can you explain me why? And how to make this work?
Sidekiq logs will go to the /var/log/syslog to redirect check out this article https://github.com/mperham/sidekiq/wiki/Logging

Acting on job failure with ActiveJob and DelayedJob

My Rails application is using ActiveJob + DelayedJob to execute some background jobs.
I am trying to figure out what is the way to define what happens on failure (not on error) - meaning, if DelayedJob has marked the job as failed, after the allowed 3 attempts, I want to perform some operation.
This is what I know so far:
DelayedJob has the aptly named failure hook.
This hook is not supported in ActiveJob
ActiveJob has a rescue_from method
The rescue_from method is probably not the right solution, since I do not want to do something on each exception, but rather only after 3 attempts (read: only after DelayedJob has deemed the job as failed).
ActiveJob has an after_perform hook, which I cannot utilize since (as far as I can see) it is not called when perform fails.
Any help is appreciated.
You may already find the solution to this, but for people who still struggle on this issue, you can use ActiveJob rety_on method with a block to run custom logic when maximum attempts have reached but still failed.
class RemoteServiceJob < ApplicationJob
retry_on(CustomAppException) do |job, error|
ExceptionNotifier.caught(error)
end
def perform(*args)
# Might raise CustomAppException
end
end
You can find more info about Exception handling in ActiveJob in https://api.rubyonrails.org/v6.0.3.2/classes/ActiveJob/Exceptions/ClassMethods.html

ActiveJob could not find record in `test` environment

This issue only exists in test environment. Everything runs fine in development environment.
I am facing a strange issue after recently upgrading to Rails 5.0.0.1 from Rails 4.2.7.1. Everything was working fine before this upgrade.
In one of my models, I use ActiveJob to perform a task.
# webhook_invocation.rb
def schedule_invocation
WebhookRequestJob.perform_later(id)
end
def init
remember_webhook # No DB changes
init_errors_context # No DB changes
flow_step_invocation.implementation = self
flow_step_invocation.save!
return unless calculate_expressions # No DB changes
calculated! # An AASM event, with no callbacks
schedule_invocation
end
and in WebhookRequestJob#perform, I retrieve the object using the ID supplied
# webhook_request_job.rb
def perform(webhook_invocation_id)
invocation = WebhookInvocation.find_by(id: webhook_invocation_id)
invocation.run_request
end
The problem is that in the #perform, it cannot find the record (invocation becomes nil). I even tried putting p WebhookInvocation.all as the first line, but all it prints is an empty collection. On the other hand, if I try p WebhookInvocation.all in #schedule_invocation method, it properly prints out all the objects of WebhookInvocation.
There is no exception being raised, no lines of warnings either.
Edit 1:
I even tried passing the object directly to #perform_later i.e. WebhookRequestJob.perform_later(self), but the received object at #perform is nil.
Edit 2:
I noticed that there are some messages like Creating scope :fail. Overwriting existing method FlowStepInvocation.fail, caused by using AASM. I eliminated them by using create_scopes: false. But that still didn't solve the problem.
My guess from the info you supplied is that you have are calling the schedule_invocation method in a after_save or after_create callback. Since the callback is called, ActiveJob might start processing the job even before the object is actually persisted (before COMMIT is done). In this case your record will not show up in the database when job is processed and you will get an empty collection or nil.
To fix this change your callback to after_commit to make sure that the COMMIT action has happened before you queue the job.
It turns out that config.active_job.queue_adapter was set to :inline as default before Rails 5, but it is set to :async in Rails 5.
This made the specs to fail (don't know why). To resolve this, I put the following line in my config/environments/test.rb:
config.active_job.queue_adapter = :inline

How can I prevent ActiveRecord from writing to the DB?

I have a somewhat special use case, where I'd like to create a method that accepts a block, such that anything that happens inside that block is not written to the DB.
The obvious answer is to use transactions like so:
def no_db
ActiveRecord::Base.transaction do
yield
raise ActiveRecord::Rollback
end
end
But the trouble is that if my no_db method is used inside of another transaction block, then I'll ned up in the case of nested transactions. The drawback here is that nested transactions are only supported by MySQL, and I need support for PG, but more importantly SQLite (for tests). (I understand that PG is supported via savepoints, how reliable is that? performance hit?).
The other problem with this type of approach is that it seems really inefficient, writing things to a DB, and then rolling them back. It would be better if I could do something like this:
def no_db_2
# ActiveRecord::Base.turn_off_database
yield
# ActiveRecord::Base.turn_on_database
end
Is there such a method? Or a similar approach to what I'm looking for? I think it needs to be fairly low level..
(Rails version is 3.0.5, but I would be happy if there were an elegant solution for Rails 3.1)
This might be one way to do it:
class Book < ActiveRecord::Base
# the usual stuff
end
# Seems like a hack but you'll get the
# transaction behavior this way...
class ReadOnly < ActiveRecord::Base
establish_connection "#{Rails.env}_readonly"
end
I would think that this...
ReadOnly.transaction do
Book.delete_all
end
...should fail.
Finally, add another connection to config/database.yml
development:
username: fullaccess
development_readonly:
username: readonly
One downside is the lack of support for a read-only mode in the sqlite3-ruby driver. You'll notice that the mode parameter doesn't do anything yet according to the documentation. http://sqlite-ruby.rubyforge.org/classes/SQLite/Database.html#M000071

Delayed Jobs Hooks do not work?

I'm trying to make my delayed jobs hooks work, but they don't seem to be. Are they outdated? If they are not, can you show me an example of yours?
Here's mine:
class PhotoJob < Struct.new(:image_id)
def perform
Photo.find(self.image_id).regenerate_styles!
end
def error(job, exception)
if Photo.exists?(self.image_id)
Photo.find(self.image_id).regenerate_styles!
else
Delayed::Job.find(self).destroy
end
end
end
The reason I say they do not work is because if I upload a hundred images, half of them will fail with an error. If the error is raised, then the hook should be run, right?
Here's the catch, if I find the photo that's failing, and run Photo.find(n).regenerate_styles! , then the photo regenerates appropriately and works.
So I'm guessing that Delayed Jobs' hooks are not working.
They've been working ok for me:
class BaseProcessorJob
def perform
raise NotImplementedError
end
def error(job, exception)
Rails.logger.error "Job failed #{exception}"
end
end
I originally had a similar problem with hooks not getting called because of an outdated delayed_job gem (it was actually installed as a plugin, which caused my problems). Not sure if any of that helps you though.

Resources