I'm wondering if rails caches anything in the following case:
Rails.cache.fetch("some_key", expires_in: 1.day) do
service.call # raises exception
[]
end
I'm concerned because if the request inside the Rails.cache.fetch block fails, I want to retry on the next request. Not make the user wait 24HRS to retry.
No. Rails doesn't cache anything if an exception is raised.
Rails Guides says that the return value of the block will be written to the cache.
When a block raises an exception, it doesn't return anything, therefore nothing is cached.
Related
Some background
PG::InFailedSqlTransaction appears when some PG exception is rescued and prevents the transaction from rollback. Simple example:
ActiveRecord::Base.transaction do
ActiveRecord::Base.connection.execute('SELECT nothing FROM nowhere') rescue nil
binding.pry # <- let's assume we're here
User.count # raises PG::InFailedSqlTransaction
end
The first line inside the transaction silently breaks the transaction and proceeds, any SQL statements after the first line will raise PG::InFailedSqlTransaction: ERROR: current transaction is aborted, commands ignored until end of transaction block.
The question
Assuming we're on the binding.pry breakpoint above, are there any ways to debug it and get the initial exception or any other data?
Not quite sure how it is implemented under the hood, but it seems quite possible for PG to cache some metadata about errors.
EDIT: the code above is just an example to demonstrate the general issue, the question is how to get the error in the situation when we cannot easily find the place that rescues the exception
Per #engineersmnky comment, it is possible to get the last error message from the PG::Connection instance with #error_message method, the instance can be obtained from the ActiveRecord connection: ActiveRecord::Base.connection.raw_connection.error_message.
If we run the above command from the pry breakpoint in the example from the question, it will return the following message:
ERROR: relation "nowhere" does not exist
LINE 1: SELECT nothing FROM nowhere
^
Let's say you have the following example code block:
def next_page(next_token)
client.list_order_items_by_next_token(next_token)
rescue => error
binding.pry
end
Without diving into the issue that this rescue is capturing all errors and how that is bad (this block has been modified) is there a way to determine the method list_order_items_by_next_token caused the issue? I know the stack trace is available but that does not feel right.
Just use
error.backtrace
Don't worry about performance.
By the time you rescue the exception the cost of creating the backtrace has already occurred. The backtrace is created when the exception is raised. And it is expensive! Ruby has to create O(N) new strings whenever an exception is raised, where N is the depth of the stacktrace. Which can be 1000+ in a common Rails application. It gets even worse in a loop. And all these strings are usually never used and just clog up garbage collection. In our production system those backtrace strings are one of the major causes of performance degradation. But as I said, that cost occurs when the exception is raised, accessing the backtrace later is free of cost.
I have created a method that is called for each web element accessed by the scripts, as to avoid the "StaleElementReferenceError" thrown by selenium. This is how the code looks:
def reach_element(page,element)
begin
element.wait_until_present
rescue Selenium::WebDriver::Error::StaleElementReferenceError
puts '* retrying to reach element'
page.refresh
retry
end
end
It appears that the StaleElementReferenceError is ignored and the tests keep failing with this error.
Am I doing something wrong?
CORRECTIONS:
This error shouldn't appear at all for it to be rescued by ruby.
The main cause was the old version of watir-webdriver gem. If you still encounter this error, a simple gem update should do the trick.
We mostly got rid of stale element issues for when you take an action on an element in watir-webdriver last year. This is the code: https://github.com/watir/watir-webdriver/blob/master/lib/watir-webdriver/elements/element.rb#L597
When an action is taken on the element, but it is stale, it will re-look it up with the selector provided. It will fail for not existing if it isn't there.
Are you seeing your element go stale between when you locate it, but before it becomes visible? That's an interesting use case that I have plans to fix. If that is your issue, refreshing the page will force the element to go stale, so that will just repeat your issue. Remove the refresh, and it should keep relocating the stale element until it is present.
If that isn't the issue or that doesn't work, provide a stack trace of what you are seeing.
In the production environment configuration I can't seem to find a configuration parameter to tell the server that when an ActiveRecord Class.find() throws an exception, that it should really be an exception. In development mode, it throws an ActiveRecord::RecordNotFound exception, but in production it rescues it and renders a 404. I seem to remember this being something you could turn on/off? I would rather not monkey patch the 404 which is the only solution I have seen so far.
The .find(id) method should be used whenever you want an exception to be thrown if the id cannot be found as described in the documentation. If you don't want an exception to be thrown, you should use .find_by_id(id).
https://github.com/rails/rails/blob/4-0-stable/activerecord/lib/active_record/railtie.rb#L26
adds a mapping
'ActiveRecord::RecordNotFound' => :not_found
asking action_dispatch to rescue 'ActiveRecord::RecordNotFound' with :not_found response.
Doing -
YourAppName::Application.config.action_dispatch.rescue_responses.delete('ActiveRecord::RecordNotFound')
in your initializer might get what you're expecting.
I want to verify that an exception is being thrown in some function tests by calling get :methodname, :params. I had expected just making this call and then verifying the result is a 500 would be sufficient, but rails is failing the test as soon as the exception is reached in the get. While I figure I could go ahead and wrap the get in a rescue and verify the exception is thrown myself, and would hope there's a better method for doing this.
What's the best way to verify an exception is thrown when using a get in your tests?
You can do:
def test_sommat
assert_raises SomeException do
get :methodname, params
end
end