I have a strange issue with rails console. In fact, I am modifying the code of class function (static class).
class SomethingWorker
#queue = :resque_queue
def self.perform(method, *args)
send(method, *args)
end
def self.async(method, *args)
Resque.enqueue(SomethingWorker, method, *args)
end
def self.process_data
puts "hello"
end
end
Sometimes, when I change the function code. It contain to execute stale (old) code. Even restarting rails console doesn't solve the issue.
I verified that the files were saved and killed all the ruby processes.
PS. the self.process_data isn't queued and I am just calling it from rails console:
SomethingWorker.process_data.
I discard any issue with resque (not running) and redis (down)
spring stop
Solved the issue and rails console is running the latest code.
Related
I have an API which uses a Service, in which I have used Ruby thread to reduce the response time of the API. I have tried to share the context using the following example. It was working fine with Rails 4, ruby 2.2.1
Now, we have upgraded rails to 5.2.3 and ruby 2.6.5. After which service has stopped working. I can call the service from Console, it works fine. But with API call, service becomes unresponsive once it reaches CurrencyConverter.new. Any Idea what can be the issue?
class ParallelTest
def initialize
puts "Initialized"
end
def perform
# Our sample set of currencies
currencies = ['ARS','AUD','CAD','CNY','DEM','EUR','GBP','HKD','ILS','INR','USD','XAG','XAU']
# Create an array to keep track of threads
threads = []
currencies.each do |currency|
# Keep track of the child processes as you spawn them
threads << Thread.new do
puts currency
CurrencyConverter.new(currency).print
end
end
# Join on the child processes to allow them to finish
threads.each do |thread|
thread.join
end
{ success: true }
end
end
class CurrencyConverter
def initialize(params)
#curr = params
end
def print
puts #curr
end
end
If I remove the CurrencyConverter.new(currency), then everything works fine. CurrencyConverter is a service object that I have.
Found the Issue
Thanks to #anothermh for this link
https://guides.rubyonrails.org/threading_and_code_execution.html#wrapping-application-code
https://guides.rubyonrails.org/threading_and_code_execution.html#load-interlock
As per the blog, When one thread is performing an autoload by evaluating the class definition from the appropriate file, it is important no other thread encounters a reference to the partially-defined constant.
Only one thread may load or unload at a time, and to do either, it must wait until no other threads are running application code. If a thread is waiting to perform a load, it doesn't prevent other threads from loading (in fact, they'll cooperate, and each perform their queued load in turn, before all resuming running together).
This can be resolved by permitting concurrent loads.
https://guides.rubyonrails.org/threading_and_code_execution.html#permit-concurrent-loads
Rails.application.executor.wrap do
urls.each do |currency|
threads << Thread.new do
CurrencyConverter.new(currency)
puts currency
end
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
threads.map(&:join)
end
end
end
Thank you everybody for your time, I appreciate.
Don't re-invent the wheel and use Sidekiq instead. 😉
From the project's page:
Simple, efficient background processing for Ruby.
Sidekiq uses threads to handle many jobs at the same time in the same process. It does not require Rails but will integrate tightly with Rails to make background processing dead simple.
With 400+ contributors, and 10k+ starts on Github, they have build a solid parallel job execution process that is production ready, and easy to setup.
Have a look at their Getting Started to see it by yourself.
Recently I had a problem with a Rails application with the following code:
class MyBaseController < ApplicationController
def error(a, b)
# ...
end
end
class MyController < MyBaseController
def my_action
begin
another_method
rescue e
# The next line had a typo, it should
# have been "e" instead of "error" and
# hence it caused and unhandled exception
logger.error("the error was #{error}")
# ...
end
# ...
end
end
As you can see the logging statement will fail as it will try to call the error method from MyBaseController instead of getting the value of e.
This was an honest mistake but from what I see it could have been prevented: when I opened my application in IntelliJ with the Ruby plugin it marked the error with a red squiggle. Is not the first time I've seen one of these errors
My question is: Is there any Gem or tool (besides Intellij) to detect this kind of problems so I can add to my Rakefile and run it in my toolchain before my application gets deployed?
You could add debugger anywhere before or after any code you would like to trace.
Once you do the action from the browser, your running server in shell will pause the work and give you the ability to trace your code and test it.
I would recommend you to read more about debugging Rails applications.
Click here to read more about rails debugging
I have a setup command that I want executed every time I start the rails console -
MyClass.some_method()
I get tired of retyping it each time I fire up rails c - is there a way to have it automatically get run every time a new console is started?
Thanks!
We do this in order to ask for the tenant every time the console starts. It took a bit of investigation, but we got it working fairly elegantly. Note that this works with Rails 5.2 but it has worked mostly the same way since Rails 4.
Another thing to note is that this is written specifically because we wanted to be able to run the method once on start and then be able to run it again while using the console, say if we wanted to switch the tenant during a session.
The first step is to create a set of modules and classes in a lib file. Here is an example extracted from ours:
# lib/console_extension.rb
module ConsoleExtension
# This module provides methods that are only available in the console
module ConsoleHelpers
def do_someting
puts "doing something"
end
end
# This is a simple class that allows us access to the ConsoleHelpers before
# we get into the console
class ConsoleRunner
include ConsoleExtension::ConsoleHelpers
end
# This is specifically to patch into the startup behavior for the console.
#
# In the console_command.rb file, it does this right before start:
#
# if defined?(console::ExtendCommandBundle)
# console::ExtendCommandBundle.include(Rails::ConsoleMethods)
# end
#
# This is a little tricky. We're defining an included method on this module
# so that the Rails::ConsoleMethods module gets a self.included method.
#
# This causes the Rails::ConsoleMethods to run this code when it's included
# in the console::ExtendCommandBundle at the last step before the console
# starts, instead of during the earlier load_console stage.
module ConsoleMethods
def included(_klass)
ConsoleExtension::ConsoleRunner.new.do_someting
end
end
end
The next step is to add the following into your application.rb file:
module MyApp
class Application < Rails::Application
...
console do
require 'console_extension' # lib/console_extension.rb
Rails::ConsoleMethods.send :include, ConsoleExtension::ConsoleHelpers
Rails::ConsoleMethods.send :extend, ConsoleExtension::ConsoleMethods
end
end
end
Now, every time you run rails console, it will do something:
If you're just looking to run something once every time the console starts, this is more complicated than it needs to be. Instead, you can just use the console() method in MyApp::Application and it will run whatever code you want as part of the load_console step.
module MyApp
class Application < Rails::Application
...
console do
puts "do something"
end
end
end
One issue we had with this was that it runs the code before it prints out the environment, so if you're doing any printing or interaction it feels a bit weird:
You may not be as picky as we are though. Do whatever makes you and your team the happiest.
I dont know if its a good practice, but you can check if server is running on Console, like Aditya awnsered
if defined?(Rails::Console)
MyClass.some_method()
end
Note that this won't work during Rails initialization when running Spring like Swartz said.
I would try creating a Rake task for it and invoke it with after_initialize:
config.after_initialize do
IndividualProject::Application.load_tasks
Rake::Task[ 'foo:bar' ].invoke
end
I am running a delayed job worker. When ever I invoke the foo method, worker prints hello.
class User
def foo
puts "Hello"
end
handle_asynchronously :foo
end
If I make some changes to the foo method, I have to restart the worker for the changes to reflect. In the development mode this can become quite tiresome.
I am trying to find a way to reload the payload class(in this case User class) for every request. I tried monkey patching the DelayedJob library to invoke require_dependency before the payload method invocation.
module Delayed::Backend::Base
def payload_object_with_reload
if Rails.env.development? and #payload_object_with_reload.nil?
require_dependency(File.join(Rails.root, "app", "models", "user.rb"))
end
#payload_object_with_reload ||= payload_object_without_reload
end
alias_method_chain :payload_object, :reload
end
This approach doesn't work as the classes registered using require_dependency needs to be reloaded before the invocation and I haven't figured out how to do it. I spent some time reading the dispatcher code to figure out how Rails reloads the classes for every request. I wasn't able to locate the reload code.
Has anybody tried this before? How would you advise me to proceed? Or do you have any pointers for locating the Rails class reload code?
I managed to find a solution. I used ActiveSupport::Dependencies.clear method to clear the loaded classes.
Add a file called config/initializers/delayed_job.rb
Delayed::Worker.backend = :active_record
if Rails.env.development?
module Delayed::Backend::Base
def payload_object_with_reload
if #payload_object_with_reload.nil?
ActiveSupport::Dependencies.clear
end
#payload_object_with_reload ||= payload_object_without_reload
end
alias_method_chain :payload_object, :reload
end
end
As of version 4.0.6, DelayedJob reloads automatically if Rails.application.config.cache_classes is set to false:
In development mode, if you are using Rails 3.1+, your application code will automatically reload every 100 jobs or when the queue finishes. You no longer need to restart Delayed Job every time you update your code in development.
This looks like it solves your problem without the alias_method hackery:
https://github.com/Viximo/delayed_job-rails_reloader
I have been happily using the DelayedJob idiom:
foo.send_later(:bar)
This calls the method bar on the object foo in the DelayedJob process.
And I've been using DaemonSpawn to kick off the DelayedJob process on my server.
But... if foo throws an exception Hoptoad doesn't catch it.
Is this a bug in any of these packages... or do I need to change some configuration... or do I need to insert some exception handling in DS or DJ that will call the Hoptoad notifier?
In response to the first comment below.
class DelayedJobWorker < DaemonSpawn::Base
def start(args)
ENV['RAILS_ENV'] ||= args.first || 'development'
Dir.chdir RAILS_ROOT
require File.join('config', 'environment')
Delayed::Worker.new.start
end
Try monkeypatching Delayed::Worker#handle_failed_job :
# lib/delayed_job_airbrake.rb
module Delayed
class Worker
protected
def handle_failed_job_with_airbrake(job, error)
say "Delayed job failed -- logging to Airbrake"
HoptoadNotifier.notify(error)
handle_failed_job_without_airbrake(job, error)
end
alias_method_chain :handle_failed_job, :airbrake
end
end
This worked for me.
(in a Rails 3.0.10 app using delayed_job 2.1.4 and hoptoad_notifier 2.4.11)
Check out the source for Delayed::Job... there's a snippet like:
# This is a good hook if you need to report job processing errors in additional or different ways
def log_exception(error)
logger.error "* [JOB] #{name} failed with #{error.class.name}: #{error.message} - #{attempts} failed attempts"
logger.error(error)
end
I haven't tried it, but I think you could do something like:
class Delayed::Job
def log_exception_with_hoptoad(error)
log_exception_without_hoptoad(error)
HoptoadNotifier.notify(error)
end
alias_method_chain :log_exception, :hoptoad
end
Hoptoad uses the Rails rescue_action_in_public hook method to intercept exceptions and log them. This method is only executed when the request is dispatched by a Rails controller.
For this reason, Hoptoad is completely unaware of any exception generated, for example, by rake tasks or the rails script/runner.
If you want to have Hoptoad tracking your exception, you should manually integrate it.
It should be quite straightforward. The following code fragment demonstrates how Hoptoad is invoked
def rescue_action_in_public_with_hoptoad exception
notify_hoptoad(exception) unless ignore?(exception) || ignore_user_agent?
rescue_action_in_public_without_hoptoad(exception)
end
Just include Hoptoad library in your environment and call notify_hoptoad(exception) should work. Make sure your environment provides the same API of a Rails controller or Hoptoad might complain.
Just throwing it out there - your daemon should require the rails environment that you're working on. It should look something along the lines of:
RAILS_ENV = ARGV.first || ENV['RAILS_ENV'] || 'production'
require File.join('config', 'environment')
This way you can specify environment in which daemon is called.
Since it runs delayed job chances are daemon already does that (it needs activerecord), but maybe you're only requiring minimal activerecord to make delayed_job happy without rails.