Slow find with including multiple associations - ruby-on-rails

In a Rails controller action I call
#grid = Grid.find(params[:id], :include => [:grid_properties, :grid_entities => [:entity => :properties]])
Unfortunately it takes really long. Benchmark.realtime (in development mode) tells me 1.8812830448150635, so about 2 seconds (Rails 3.0.7, Postgres 8.4).
When turning on the SQL logging I get the following for this line of code:
Grid Load (0.9ms) SELECT "grids".* FROM "grids" WHERE "grids"."id" = 2 LIMIT 1
GridProperty Load (2.5ms) SELECT "grid_properties".* FROM "grid_properties" WHERE ("grid_properties".grid_id = 2) ORDER BY row_order
GridEntity Load (1.3ms) SELECT "grid_entities".* FROM "grid_entities" WHERE ("grid_entities".grid_id = 2) ORDER BY row_order
Entity Load (3.0ms) SELECT "entities".* FROM "entities" WHERE ("entities"."id" IN (28,7,3,6,25,11,2,12))
Property Load (6.4ms) SELECT "properties".* FROM "properties" WHERE ("properties".entity_id IN (28,7,3,6,25,11,2,12))
Every database access seems to be in the low millisecond range. Why does it take so long in the end?

I guess that the time is spent on creating the object by Ruby. How many Properties are in that Grid? The time of 'SELECT FROM properties' looks relatively high.
You could further investigate the problem, if you have some functions where you could place checkpoints - logger.debug "CHECKPOINT: #{Time.now} #{caller(0).first}"
Do you have any 'on_load' callbacks, maybe?

Related

Rails 6 Multiple Database: Log database name/role

I have just configured database replication in a Rails 6.0 application. It works smoothly, but I am trying to confirm that the right database/role is used for the queries I am expecting it to (long story short: manually calling ActiveRecord::Base.connected_to for business logic reasons).
I Googled and Stack-Overflow-ed that question, but can't seem to find an answer.
Is it possible to have the SQL queries debug entries display the database name and role used?
2021-08-06T13:26:02.186960+00:00 app[web.1]: D, [2021-08-06T13:26:02.186858 #46] DEBUG -- : [c132c761-c02c-4690-a8b0-f8c29d999xxx] Shop Load (1.6ms) SELECT "shops".* FROM "shops" WHERE "shops"."slug" = 'some slug' LIMIT 1
If not, what other ways are at my disposal to assert that my replica/follower is being hit instead of the leader/master database?
If it is of any help to answer my question: I am running this on Heroku.
Thank you in advance!

many cache calls in development log - how to prevent them

I have a Rails 4.2 app that has literally hundreds of calls like this:
CACHE (0.0ms) SELECT "albums".* FROM "albums" WHERE (albumable_type='ItemProfile' and albumable_id=33333) ORDER BY "albums"."id" ASC LIMIT 1
CACHE (0.0ms) SELECT COUNT(*) FROM "assets" WHERE "assets"."album_id" = $1 AND "assets"."is_enabled" = $2 [["album_id", 19182], ["is_enabled", true]]
CACHE (0.0ms) SELECT "albums".* FROM "albums" WHERE (albumable_type='ItemProfile' and albumable_id=33333) ORDER BY "albums"."id" ASC LIMIT 1
CACHE (0.0ms) SELECT "assets".* FROM "assets" WHERE "assets"."album_id" = $1 AND "assets"."is_enabled" = $2 ORDER BY "assets"."position" ASC LIMIT 1 [["album_id", 19182], ["is_enabled", true]]
Two questions:
Are these calls actually being made (my understanding is no due to "CACHE")?
Is there a way to tell Rails that it already has this info and doesn't need to check for it? Like hundreds of times seems liek a bug on my end
edit #1
so the problem is that there is stuff like the following:
def instore_image
album.enabled_assets.first
end
and
def image_600w
Rails.logger.info("#9797 here is instore_image_url")
instore_image.asset.url(:fixed_width600)
Rails.logger.info("#9898 here is instore_image_url")
end
The image_600w is calling a round of sql calls (whether cached or not). It seems like to prevent this from happening I need to set an instance variable on the object.
You should check out the section on Caching with Rails in Rails Guides:
1.5 SQL Caching
Query caching is a Rails feature that caches the result set returned
by each query so that if Rails encounters the same query again for
that request, it will use the cached result set as opposed to running
the query against the database again.
So these queries are actually being made by your application, however ActiveRecord intercepts these queries and prevents SQL query by directly delivering from Query cache.
Is there a way to tell Rails that it already has this info and doesn't need to check for it? Like hundreds of times seems liek a bug on my end.
Yes, Rails is already doing the best it can by reusing cached query results. You can use bullet to identify N+1 queries and other similar inefficient database usage patterns in your application.

Attribute not updated in production

Rails version: 4.2.3
I am into a strange situation here, I think it is a silly thing, but I dont know why this happens.
I receive a post notification on a URL, something like /accounts/payment. After process the json I received, I need do a update on a attribute of model Account. I am doing something like this:
account.update_columns(status: "delayed")
or update_attributes
everything works fine locally. All tests pass. But for a unknown reason when this code run in production, the attribute is not updated.
here some logs from production env.
SQL (1.7ms) UPDATE "accounts" SET "status" = 'delayed' WHERE "accounts"."type" IN ('StandardAccount') AND "accounts"."id" = $1 [["id", 94]]
Account.find(94).status
Account Load (0.7ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."id" = $1 LIMIT 1 [["id", 94]]
=> "actived"
Thanks in advance
UPDATE 1
My modal schema is:
StandardAccount < Account
UPDATE 2
Not a Rails problem. I found a Trigger on DB involved with accounts table and cause the problem.

rails ActiveRecord transactional Integrity / Data not committing mid scenario spec?

I know i'm mis-understanding how ActiveRecord maintains integrity/works: hoping someone can clarify for me explaining why the following is not working?
We have an abnormal situation: our rails app calls a compiled binary that accesses (and creates a new table rows, it does not update existing) in a shared database. We therefore run config.use_transactional_fixtures = false in rails_helper (other wise we get savepoint errors).
The data needs to commit within the scenario so this legacy app can access the data in the database during the test.
During a test we are setting up data via eval(rubyexpression) (see below for full code)
"provider = Provider.create({:provider_reseller_phone_number => '0200000000', :provider_registered_business_name => 'ProviderReseller', :provider_name => 'providerwithzero'})"
NOTE:
i know we should be using factorygirl for this, thats a different
long story
there is no additional provider model code e.g. callbacks,
hooks are anything
using debugger to pause the test (line 22), the data is not saved to the database, but it is there once the rspec completes.
We cannot figure out why!? surely data is committed after each transaction e.g. eval?
appreciate any guidance / learnings?
we've tried
using new + "save" on the provider variable but it isn't populated by the eval.
config.use_transactional_fixtures = true but this breaks our other need e.g. external process accessing DB
searching for ways to flush (But only seems to apply to "transactions')
tried searching for ways of committing "save_points" (no luck)
provider.create!
based on ()Rails 3: ActiveRecord observer: after_commit callback doesn't fire during tests, but after_save does fire run_callbacks(:commit)
spec_test_eval.rb
require 'rails_helper'
describe 'trying to test using rails populated data for external process' do
it 'populates provider and tests external process' do
initial_data = "provider = Provider.create({:provider_reseller_phone_number => '0200000000', :provider_registered_business_name => 'ProviderReseller', :provider_name => 'providerwithzero'})"
eval(initial_data)
debugger
expect Provider.all.count.eql?(1)
# using mysql to check providers table its empty
exec_path_str = "#{EXTERNALPROCESS} 1 1"
stdop_content = `#{exec_path_str}`
end
end
test.log output
ActiveRecord::SchemaMigration Load (0.2ms) SELECT `schema_migrations`.* FROM `schema_migrations`
(0.1ms) BEGIN
(0.1ms) SAVEPOINT active_record_1
SQL (0.2ms) INSERT INTO `providers` (`created_at`, `provider_name`, `provider_registered_business_name`, `provider_reseller_phone_number`, `updated_at`) VALUES ('2014-12-27 03:33:21', 'providerwithzero', 'ProviderReseller', '0200000000', '2014-12-27 03:33:21')
(0.1ms) RELEASE SAVEPOINT active_record_1
(0.2ms) SELECT COUNT(*) FROM `providers`
(0.4ms) ROLLBACK
So it seems that its the DatabaseCleaner gem thats causing this behaviour.
Having understood truncation, transaction etc. differences (database cleaner strategies we had transaction enabled which forces the surrounding transaction and rollback. But using DatabaseCleaner.strategy = :truncation) allows each ActiveRecord action to commit to the database. At a speed cost.
Given this does slow up the tests and we only need on some special tests, now searching solutions for different strategies based on flags/attributes.

ActiveRecord requerying depending on order of operations

New to Ruby and Rails so forgive me if my terminology is off a bit.
I am working on optimizing some inherited code and watching the logs I am seeing queries repeat themselves due to lines like this in a .rabl file:
node(:program) { |user| (!user.programs.first.nil?) ? user.programs.first.name : '' }
user and program are both active record objects
Moving to the rails console, I can replicate the problem, but I can also get the expected behavior, which is only one query:
>u = User.find(1234)
User Load (0.3ms) SELECT `users`.* FROM `users` WHERE [...]
> (!u.programs.first.nil?) ? u.programs.first.name : ''
Program Load (0.2ms) SELECT `programs`.* FROM `programs` [...]
Program Load (0.3ms) SELECT `programs`.* FROM `programs` [...]
=> "Output"
Note that repeating the ternary statement in the console will always give me 2 queries.
I can get the expected behavior like so:
> newu = User.find(12345)
User Load (3.8ms) SELECT `users`.* FROM `users` WHERE [...]
> newu.programs
Program Load (0.6ms) SELECT `programs`.* FROM `programs` [...]
> (!newu.programs.first.nil?) ? newu.programs.first.name : ''
=> "Output"
Repeating the ternary statement now won't requery at all.
So the question is: why does calling newu.programs change the behavior? Shouldn't calling u.programs.first.nil? also act to load all the program records in the same way?
With an association, first is not sugar for [0].
If the association is loaded, then it just returns the first element of the array. If it is not loaded, it makes a database query to load just that one element. It can't stick that in the association cache (at least not without being smarter), so the next query to first does the query again (this will use the query cache if turned on)
What Rails is assuming is that if the association is big, and you are only using one element of it then it would be silly to load the whole thing. This can be a little annoying when this isn't the case and you are just using the one item, but you're using it repeatedly.
To avoid this you can either assign the item to a local variable so that you do genuinely only call first once, or do
newu.programs[0]
which will load the whole association (once only) and return the first element.
Rails does the same thing with include?. Instead of loading the whole collection, it will run a query that tests whether a specific item is in the collection (unless the collection is loaded)

Resources