Friday, August 12, 2016

Rails query execution causes database spikes

Leave a Comment

I'm having a problem with my Rails application where some random queries take around 5 seconds or longer to finish. Most of the time the queries are very simple (select * from x where id = ?) and the fields are even indexed too.

Here's some more information about the setup:

  • Puma 3.5.0 behind a reversed nginx proxy
    • 4 workers with minimum 4, max 8 threads each.
  • Ruby v2.2.3, Rails v4.2.4
  • PostgreSQL 9.4 database
    • Thread pool set to max 60 connections
  • Appsignal for monitoring
  • 8GB RAM, 4 CPU's, SSD.

I found this out when looking at the query performance in Appsignal. I noticed most queries finishing in a few ms and then every now and then, still in the same request, there are multiple queries that take 5+ seconds to finish. And the odd part is that it ALWAYS takes 5,.. seconds. Here's a picture of that in action: Appsignal performance

Things I've tried:

  • Increase the thread pool to make sure the puma worker threads have enough connection objects.
  • Set 'reaping_frequency' to 10s to make sure there are no dead connections being used.
  • Increase puma workers/threads

I'm noticing this in the application as there are some pages that take a long time to load (I have a function call that takes about 1 minute to finish) and somehow this is blocking new requests. This is strange to me as there are 4 workers each with 8 threads = 32 threads that can handle the other requests.

I ran an explain on the query in the picture above, this is the output:

Limit  (cost=0.28..8.30 rows=1 width=150)   ->  Index Scan using index_addresses_on_addressable_id_and_addressable_type on addresses  (cost=0.28..8.30 rows=1 width=150)         Index Cond: ((addressable_id = 1) AND ((addressable_type)::text = 'University'::text))         Filter: (deleted_at IS NULL) Total query runtime: 13 ms 

And this is the schema of the addresses table:

# Table name: addresses # #  id               :integer          not null, primary key #  street           :string #  zip_code         :string #  city             :string #  country          :string #  addressable_id   :integer #  addressable_type :string #  created_at       :datetime         not null #  updated_at       :datetime         not null #  street_number    :string #  latitude         :float #  longitude        :float #  mobile           :string #  phone            :string #  email            :string #  deleted_at       :datetime #  name             :string` 

Here's my Puma config file:

#!/usr/bin/env puma  directory '/home/deployer/apps/qeystate/current' rackup "/home/deployer/apps/qeystate/current/config.ru" environment 'staging'    pidfile "/home/deployer/apps/qeystate/shared/tmp/pids/puma.pid" state_path "/home/deployer/apps/qeystate/shared/tmp/pids/puma.state" stdout_redirect '/home/deployer/apps/qeystate/shared/log/puma_access.log', '/home/deployer/apps/qeystate/shared/log/puma_error.log', true threads 4,8 bind 'unix:///home/deployer/apps/qeystate/shared/tmp/sockets/puma.sock' workers 4 preload_app! prune_bundler  on_worker_boot do   ActiveSupport.on_load(:active_record) do     ActiveRecord::Base.establish_connection   end end  before_fork do   ActiveRecord::Base.connection_pool.disconnect! end 

0 Answers

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment