Commit 63986a76 authored by Kamil Trzciński's avatar Kamil Trzciński

Merge branch '222684-fix-db-call-logging' into 'master'

Fix db call logging numbers

Closes #222684

See merge request gitlab-org/gitlab!34939
parents 9d3ebcaf f7a590dd
...@@ -15,6 +15,7 @@ unless Gitlab::Runtime.sidekiq? ...@@ -15,6 +15,7 @@ unless Gitlab::Runtime.sidekiq?
data[:db_duration_s] = Gitlab::Utils.ms_to_round_sec(data.delete(:db)) if data[:db] data[:db_duration_s] = Gitlab::Utils.ms_to_round_sec(data.delete(:db)) if data[:db]
data[:view_duration_s] = Gitlab::Utils.ms_to_round_sec(data.delete(:view)) if data[:view] data[:view_duration_s] = Gitlab::Utils.ms_to_round_sec(data.delete(:view)) if data[:view]
data[:duration_s] = Gitlab::Utils.ms_to_round_sec(data.delete(:duration)) if data[:duration] data[:duration_s] = Gitlab::Utils.ms_to_round_sec(data.delete(:duration)) if data[:duration]
data.merge!(::Gitlab::Metrics::Subscribers::ActiveRecord.db_counter_payload)
data data
end end
......
...@@ -20,8 +20,6 @@ module Gitlab ...@@ -20,8 +20,6 @@ module Gitlab
username: event.payload[:username], username: event.payload[:username],
ua: event.payload[:ua] ua: event.payload[:ua]
} }
add_db_counters!(payload)
payload.merge!(event.payload[:metadata]) if event.payload[:metadata] payload.merge!(event.payload[:metadata]) if event.payload[:metadata]
::Gitlab::InstrumentationHelper.add_instrumentation_data(payload) ::Gitlab::InstrumentationHelper.add_instrumentation_data(payload)
...@@ -46,16 +44,6 @@ module Gitlab ...@@ -46,16 +44,6 @@ module Gitlab
payload payload
end end
def self.add_db_counters!(payload)
current_transaction = Gitlab::Metrics::Transaction.current
if current_transaction
payload[:db_count] = current_transaction.get(:db_count, :counter).to_i
payload[:db_write_count] = current_transaction.get(:db_write_count, :counter).to_i
payload[:db_cached_count] = current_transaction.get(:db_cached_count, :counter).to_i
end
end
private_class_method :add_db_counters!
end end
end end
end end
...@@ -26,9 +26,7 @@ module Gitlab ...@@ -26,9 +26,7 @@ module Gitlab
private private
def add_info_to_payload(payload, trans) def add_info_to_payload(payload, trans)
payload[:db_count] = trans.get(:db_count, :counter).to_i payload.merge!(::Gitlab::Metrics::Subscribers::ActiveRecord.db_counter_payload)
payload[:db_write_count] = trans.get(:db_write_count, :counter).to_i
payload[:db_cached_count] = trans.get(:db_cached_count, :counter).to_i
end end
end end
end end
......
...@@ -23,6 +23,14 @@ module Gitlab ...@@ -23,6 +23,14 @@ module Gitlab
increment_db_counters(payload) increment_db_counters(payload)
end end
def self.db_counter_payload
return {} unless Gitlab::SafeRequestStore.active?
DB_COUNTERS.map do |counter|
[counter, Gitlab::SafeRequestStore[counter].to_i]
end.to_h
end
private private
define_histogram :gitlab_sql_duration_seconds do define_histogram :gitlab_sql_duration_seconds do
...@@ -36,13 +44,21 @@ module Gitlab ...@@ -36,13 +44,21 @@ module Gitlab
end end
def increment_db_counters(payload) def increment_db_counters(payload)
current_transaction.increment(:db_count, 1) increment(:db_count)
if payload.fetch(:cached, payload[:name] == 'CACHE') if payload.fetch(:cached, payload[:name] == 'CACHE')
current_transaction.increment(:db_cached_count, 1) increment(:db_cached_count)
end end
current_transaction.increment(:db_write_count, 1) unless select_sql_command?(payload) increment(:db_write_count) unless select_sql_command?(payload)
end
def increment(counter)
current_transaction.increment(counter, 1)
if Gitlab::SafeRequestStore.active?
Gitlab::SafeRequestStore[counter] = Gitlab::SafeRequestStore[counter].to_i + 1
end
end end
def current_transaction def current_transaction
......
...@@ -92,12 +92,6 @@ module Gitlab ...@@ -92,12 +92,6 @@ module Gitlab
self.class.transaction_metric(name, :gauge).set(labels, value) if use_prometheus self.class.transaction_metric(name, :gauge).set(labels, value) if use_prometheus
end end
def get(name, type, tags = {})
metric = self.class.transaction_metric(name, type)
metric.get(filter_tags(tags).merge(labels))
end
def labels def labels
BASE_LABELS BASE_LABELS
end end
......
...@@ -152,5 +152,35 @@ RSpec.describe 'lograge', type: :request do ...@@ -152,5 +152,35 @@ RSpec.describe 'lograge', type: :request do
expect(log_data['etag_route']).to eq(etag_route) expect(log_data['etag_route']).to eq(etag_route)
end end
end end
context 'with transaction' do
let(:transaction) { Gitlab::Metrics::WebTransaction.new({}) }
before do
allow(Gitlab::Metrics::Transaction).to receive(:current).and_return(transaction)
end
context 'when RequestStore is enabled', :request_store do
context 'with db payload' do
it 'includes db counters', :request_store do
ActiveRecord::Base.connection.execute('SELECT pg_sleep(0.1);')
subscriber.process_action(event)
expect(log_data).to include("db_count" => 1, "db_write_count" => 0, "db_cached_count" => 0)
end
end
end
context 'when RequestStore is disabled' do
context 'with db payload' do
it 'does not include db counters' do
ActiveRecord::Base.connection.execute('SELECT pg_sleep(0.1);')
subscriber.process_action(event)
expect(log_data).not_to include("db_count" => 1, "db_write_count" => 0, "db_cached_count" => 0)
end
end
end
end
end end
end end
...@@ -44,18 +44,6 @@ RSpec.describe Gitlab::Lograge::CustomOptions do ...@@ -44,18 +44,6 @@ RSpec.describe Gitlab::Lograge::CustomOptions do
end end
end end
context 'with transaction' do
let(:transaction) { Gitlab::Metrics::WebTransaction.new({}) }
before do
allow(Gitlab::Metrics::Transaction).to receive(:current).and_return(transaction)
end
it 'adds db counters' do
expect(subject).to include(:db_count, :db_write_count, :db_cached_count)
end
end
it 'adds the user id' do it 'adds the user id' do
expect(subject[:user_id]).to eq('test') expect(subject[:user_id]).to eq('test')
end end
......
...@@ -18,8 +18,20 @@ RSpec.describe Gitlab::Metrics::SidekiqMiddleware do ...@@ -18,8 +18,20 @@ RSpec.describe Gitlab::Metrics::SidekiqMiddleware do
middleware.call(worker, message, :test) do middleware.call(worker, message, :test) do
ActiveRecord::Base.connection.execute('SELECT pg_sleep(0.1);') ActiveRecord::Base.connection.execute('SELECT pg_sleep(0.1);')
end end
end
it 'prevents database counters from leaking to the next transaction' do
worker = double(:worker, class: double(:class, name: 'TestWorker'))
2.times do
Gitlab::WithRequestStore.with_request_store do
middleware.call(worker, message, :test) do
ActiveRecord::Base.connection.execute('SELECT pg_sleep(0.1);')
end
end
end
expect(message).to include(:db_count, :db_write_count, :db_cached_count) expect(message).to include(db_count: 1, db_write_count: 0, db_cached_count: 0)
end end
it 'tracks the transaction (for messages without `enqueued_at`)', :aggregate_failures do it 'tracks the transaction (for messages without `enqueued_at`)', :aggregate_failures do
...@@ -35,19 +47,20 @@ RSpec.describe Gitlab::Metrics::SidekiqMiddleware do ...@@ -35,19 +47,20 @@ RSpec.describe Gitlab::Metrics::SidekiqMiddleware do
middleware.call(worker, {}, :test) { nil } middleware.call(worker, {}, :test) { nil }
end end
it 'tracks any raised exceptions', :aggregate_failures do it 'tracks any raised exceptions', :aggregate_failures, :request_store do
worker = double(:worker, class: double(:class, name: 'TestWorker')) worker = double(:worker, class: double(:class, name: 'TestWorker'))
expect_any_instance_of(Gitlab::Metrics::Transaction)
.to receive(:run).and_raise(RuntimeError)
expect_any_instance_of(Gitlab::Metrics::Transaction) expect_any_instance_of(Gitlab::Metrics::Transaction)
.to receive(:add_event).with(:sidekiq_exception) .to receive(:add_event).with(:sidekiq_exception)
expect { middleware.call(worker, message, :test) } expect do
.to raise_error(RuntimeError) middleware.call(worker, message, :test) do
ActiveRecord::Base.connection.execute('SELECT pg_sleep(0.1);')
raise RuntimeError
end
end.to raise_error(RuntimeError)
expect(message).to include(:db_count, :db_write_count, :db_cached_count) expect(message).to include(db_count: 1, db_write_count: 0, db_cached_count: 0)
end end
end end
end end
...@@ -28,109 +28,123 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do ...@@ -28,109 +28,123 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do
end end
describe 'with a current transaction' do describe 'with a current transaction' do
shared_examples 'read only query' do shared_examples 'track executed query' do
it 'increments only db count value' do before do
allow(subscriber).to receive(:current_transaction) allow(subscriber).to receive(:current_transaction)
.at_least(:once) .at_least(:once)
.and_return(transaction) .and_return(transaction)
end
expect(transaction).to receive(:increment) it 'increments only db count value' do
.with(:db_count, 1) described_class::DB_COUNTERS.each do |counter|
if expected_counters[counter] > 0
expect(transaction).not_to receive(:increment) expect(transaction).to receive(:increment).with(counter, 1)
.with(:db_cached_count, 1) else
expect(transaction).not_to receive(:increment).with(counter, 1)
expect(transaction).not_to receive(:increment)
.with(:db_write_count, 1)
subscriber.sql(event)
end end
end end
shared_examples 'write query' do subscriber.sql(event)
it 'increments db_write_count and db_count value' do end
expect(subscriber).to receive(:current_transaction)
.at_least(:once)
.and_return(transaction)
expect(transaction).to receive(:increment)
.with(:db_count, 1)
expect(transaction).not_to receive(:increment) context 'when RequestStore is enabled' do
.with(:db_cached_count, 1) it 'caches db count value', :request_store, :aggregate_failures do
subscriber.sql(event)
expect(transaction).to receive(:increment) described_class::DB_COUNTERS.each do |counter|
.with(:db_write_count, 1) expect(Gitlab::SafeRequestStore[counter].to_i).to eq expected_counters[counter]
end
end
it 'prevents db counters from leaking to the next transaction' do
2.times do
Gitlab::WithRequestStore.with_request_store do
subscriber.sql(event) subscriber.sql(event)
described_class::DB_COUNTERS.each do |counter|
expect(Gitlab::SafeRequestStore[counter].to_i).to eq expected_counters[counter]
end
end
end
end
end end
end end
shared_examples 'cached query' do it 'observes sql_duration metric' do
it 'increments db_cached_count and db_count value' do
expect(subscriber).to receive(:current_transaction) expect(subscriber).to receive(:current_transaction)
.at_least(:once) .at_least(:once)
.and_return(transaction) .and_return(transaction)
expect(described_class.send(:gitlab_sql_duration_seconds)).to receive(:observe).with({}, 0.002)
subscriber.sql(event)
end
expect(transaction).to receive(:increment) context 'with read query' do
.with(:db_count, 1) let(:expected_counters) do
{
db_count: 1,
db_write_count: 0,
db_cached_count: 0
}
end
expect(transaction).to receive(:increment) it_behaves_like 'track executed query'
.with(:db_cached_count, 1)
expect(transaction).not_to receive(:increment) context 'with only select' do
.with(:db_write_count, 1) let(:payload) { { sql: 'WITH active_milestones AS (SELECT COUNT(*), state FROM milestones GROUP BY state) SELECT * FROM active_milestones' } }
subscriber.sql(event) it_behaves_like 'track executed query'
end end
end end
it 'observes sql_duration metric' do context 'write query' do
expect(subscriber).to receive(:current_transaction) let(:expected_counters) do
.at_least(:once) {
.and_return(transaction) db_count: 1,
expect(described_class.send(:gitlab_sql_duration_seconds)).to receive(:observe).with({}, 0.002) db_write_count: 1,
subscriber.sql(event) db_cached_count: 0
}
end end
it_behaves_like 'read only query'
context 'with select for update sql event' do context 'with select for update sql event' do
let(:payload) { { sql: 'SELECT * FROM users WHERE id = 10 FOR UPDATE' } } let(:payload) { { sql: 'SELECT * FROM users WHERE id = 10 FOR UPDATE' } }
it_behaves_like 'write query' it_behaves_like 'track executed query'
end end
context 'with common table expression' do context 'with common table expression' do
context 'with insert' do context 'with insert' do
let(:payload) { { sql: 'WITH archived_rows AS (SELECT * FROM users WHERE archived = true) INSERT INTO products_log SELECT * FROM archived_rows' } } let(:payload) { { sql: 'WITH archived_rows AS (SELECT * FROM users WHERE archived = true) INSERT INTO products_log SELECT * FROM archived_rows' } }
it_behaves_like 'write query' it_behaves_like 'track executed query'
end
context 'with only select' do
let(:payload) { { sql: 'WITH active_milestones AS (SELECT COUNT(*), state FROM milestones GROUP BY state) SELECT * FROM active_milestones' } }
it_behaves_like 'read only query'
end end
end end
context 'with delete sql event' do context 'with delete sql event' do
let(:payload) { { sql: 'DELETE FROM users where id = 10' } } let(:payload) { { sql: 'DELETE FROM users where id = 10' } }
it_behaves_like 'write query' it_behaves_like 'track executed query'
end end
context 'with insert sql event' do context 'with insert sql event' do
let(:payload) { { sql: 'INSERT INTO project_ci_cd_settings (project_id) SELECT id FROM projects' } } let(:payload) { { sql: 'INSERT INTO project_ci_cd_settings (project_id) SELECT id FROM projects' } }
it_behaves_like 'write query' it_behaves_like 'track executed query'
end end
context 'with update sql event' do context 'with update sql event' do
let(:payload) { { sql: 'UPDATE users SET admin = true WHERE id = 10' } } let(:payload) { { sql: 'UPDATE users SET admin = true WHERE id = 10' } }
it_behaves_like 'write query' it_behaves_like 'track executed query'
end
end
context 'with cached query' do
let(:expected_counters) do
{
db_count: 1,
db_write_count: 0,
db_cached_count: 1
}
end end
context 'with cached payload ' do context 'with cached payload ' do
...@@ -141,7 +155,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do ...@@ -141,7 +155,7 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do
} }
end end
it_behaves_like 'cached query' it_behaves_like 'track executed query'
end end
context 'with cached payload name' do context 'with cached payload name' do
...@@ -152,7 +166,8 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do ...@@ -152,7 +166,8 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do
} }
end end
it_behaves_like 'cached query' it_behaves_like 'track executed query'
end
end end
context 'events are internal to Rails or irrelevant' do context 'events are internal to Rails or irrelevant' do
...@@ -215,4 +230,54 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do ...@@ -215,4 +230,54 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do
end end
end end
end end
describe 'self.db_counter_payload' do
before do
allow(subscriber).to receive(:current_transaction)
.at_least(:once)
.and_return(transaction)
end
context 'when RequestStore is enabled', :request_store do
context 'when query is executed' do
let(:expected_payload) do
{
db_count: 1,
db_cached_count: 0,
db_write_count: 0
}
end
it 'returns correct payload' do
subscriber.sql(event)
expect(described_class.db_counter_payload).to eq(expected_payload)
end
end
context 'when query is not executed' do
let(:expected_payload) do
{
db_count: 0,
db_cached_count: 0,
db_write_count: 0
}
end
it 'returns correct payload' do
expect(described_class.db_counter_payload).to eq(expected_payload)
end
end
end
context 'when RequestStore is disabled' do
let(:expected_payload) { {} }
it 'returns empty payload' do
subscriber.sql(event)
expect(described_class.db_counter_payload).to eq(expected_payload)
end
end
end
end end
...@@ -114,15 +114,4 @@ RSpec.describe Gitlab::Metrics::Transaction do ...@@ -114,15 +114,4 @@ RSpec.describe Gitlab::Metrics::Transaction do
transaction.set(:meow, 1) transaction.set(:meow, 1)
end end
end end
describe '#get' do
let(:prometheus_metric) { instance_double(Prometheus::Client::Counter, get: nil) }
it 'gets a metric' do
expect(described_class).to receive(:fetch_metric).with(:counter, :gitlab_transaction_meow_total).and_return(prometheus_metric)
expect(prometheus_metric).to receive(:get)
transaction.get(:meow, :counter)
end
end
end end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment