Commit f9b157fe authored by Bob Van Landuyt's avatar Bob Van Landuyt

Merge branch 'qmnguyen0711/expand-a-histogram-bucket' into 'master'

Adjust gitlab_database_transaction_seconds bucket

See merge request gitlab-org/gitlab!56952
parents f933d954 1c439f93
---
title: Adjust gitlab_database_transaction_seconds histogram bucket
merge_request: 56952
author:
type: changed
...@@ -54,7 +54,9 @@ module EE ...@@ -54,7 +54,9 @@ module EE
end end
def observe_db_role_duration(db_role, event) def observe_db_role_duration(db_role, event)
observe("gitlab_sql_#{db_role}_duration_seconds".to_sym, event) observe("gitlab_sql_#{db_role}_duration_seconds".to_sym, event) do
buckets ::Gitlab::Metrics::Subscribers::ActiveRecord::SQL_DURATION_BUCKET
end
duration = event.duration / 1000.0 duration = event.duration / 1000.0
duration_key = "db_#{db_role}_duration_s".to_sym duration_key = "db_#{db_role}_duration_s".to_sym
......
...@@ -11,13 +11,16 @@ module Gitlab ...@@ -11,13 +11,16 @@ module Gitlab
DB_COUNTERS = %i{db_count db_write_count db_cached_count}.freeze DB_COUNTERS = %i{db_count db_write_count db_cached_count}.freeze
SQL_COMMANDS_WITH_COMMENTS_REGEX = /\A(\/\*.*\*\/\s)?((?!(.*[^\w'"](DELETE|UPDATE|INSERT INTO)[^\w'"])))(WITH.*)?(SELECT)((?!(FOR UPDATE|FOR SHARE)).)*$/i.freeze SQL_COMMANDS_WITH_COMMENTS_REGEX = /\A(\/\*.*\*\/\s)?((?!(.*[^\w'"](DELETE|UPDATE|INSERT INTO)[^\w'"])))(WITH.*)?(SELECT)((?!(FOR UPDATE|FOR SHARE)).)*$/i.freeze
DURATION_BUCKET = [0.05, 0.1, 0.25].freeze SQL_DURATION_BUCKET = [0.05, 0.1, 0.25].freeze
TRANSACTION_DURATION_BUCKET = [0.1, 0.25, 1].freeze
# This event is published from ActiveRecordBaseTransactionMetrics and # This event is published from ActiveRecordBaseTransactionMetrics and
# used to record a database transaction duration when calling # used to record a database transaction duration when calling
# ActiveRecord::Base.transaction {} block. # ActiveRecord::Base.transaction {} block.
def transaction(event) def transaction(event)
observe(:gitlab_database_transaction_seconds, event) observe(:gitlab_database_transaction_seconds, event) do
buckets TRANSACTION_DURATION_BUCKET
end
end end
def sql(event) def sql(event)
...@@ -33,7 +36,9 @@ module Gitlab ...@@ -33,7 +36,9 @@ module Gitlab
increment(:db_cached_count) if cached_query?(payload) increment(:db_cached_count) if cached_query?(payload)
increment(:db_write_count) unless select_sql_command?(payload) increment(:db_write_count) unless select_sql_command?(payload)
observe(:gitlab_sql_duration_seconds, event) observe(:gitlab_sql_duration_seconds, event) do
buckets SQL_DURATION_BUCKET
end
end end
def self.db_counter_payload def self.db_counter_payload
...@@ -66,10 +71,8 @@ module Gitlab ...@@ -66,10 +71,8 @@ module Gitlab
Gitlab::SafeRequestStore[counter] = Gitlab::SafeRequestStore[counter].to_i + 1 Gitlab::SafeRequestStore[counter] = Gitlab::SafeRequestStore[counter].to_i + 1
end end
def observe(histogram, event) def observe(histogram, event, &block)
current_transaction&.observe(histogram, event.duration / 1000.0) do current_transaction&.observe(histogram, event.duration / 1000.0, &block)
buckets DURATION_BUCKET
end
end end
def current_transaction def current_transaction
......
...@@ -8,65 +8,145 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do ...@@ -8,65 +8,145 @@ RSpec.describe Gitlab::Metrics::Subscribers::ActiveRecord do
let(:env) { {} } let(:env) { {} }
let(:subscriber) { described_class.new } let(:subscriber) { described_class.new }
let(:connection) { double(:connection) } let(:connection) { double(:connection) }
let(:payload) { { sql: 'SELECT * FROM users WHERE id = 10', connection: connection } }
let(:event) do
double(
:event,
name: 'sql.active_record',
duration: 2,
payload: payload
)
end
# Emulate Marginalia pre-pending comments describe '#transaction' do
def sql(query, comments: true) let(:web_transaction) { double('Gitlab::Metrics::WebTransaction') }
if comments && !%w[BEGIN COMMIT].include?(query) let(:background_transaction) { double('Gitlab::Metrics::WebTransaction') }
"/*application:web,controller:badges,action:pipeline,correlation_id:01EYN39K9VMJC56Z7808N7RSRH*/ #{query}"
else let(:event) do
query double(
:event,
name: 'transaction.active_record',
duration: 230,
payload: { connection: connection }
)
end end
end
shared_examples 'track generic sql events' do before do
where(:name, :sql_query, :record_query, :record_write_query, :record_cached_query) do allow(background_transaction).to receive(:observe)
'SQL' | 'SELECT * FROM users WHERE id = 10' | true | false | false allow(web_transaction).to receive(:observe)
'SQL' | 'WITH active_milestones AS (SELECT COUNT(*), state FROM milestones GROUP BY state) SELECT * FROM active_milestones' | true | false | false
'SQL' | 'SELECT * FROM users WHERE id = 10 FOR UPDATE' | true | true | false
'SQL' | 'WITH archived_rows AS (SELECT * FROM users WHERE archived = true) INSERT INTO products_log SELECT * FROM archived_rows' | true | true | false
'SQL' | 'DELETE FROM users where id = 10' | true | true | false
'SQL' | 'INSERT INTO project_ci_cd_settings (project_id) SELECT id FROM projects' | true | true | false
'SQL' | 'UPDATE users SET admin = true WHERE id = 10' | true | true | false
'CACHE' | 'SELECT * FROM users WHERE id = 10' | true | false | true
'SCHEMA' | "SELECT attr.attname FROM pg_attribute attr INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = any(cons.conkey) WHERE cons.contype = 'p' AND cons.conrelid = '\"projects\"'::regclass" | false | false | false
nil | 'BEGIN' | false | false | false
nil | 'COMMIT' | false | false | false
end end
with_them do context 'when both web and background transaction are available' do
let(:payload) { { name: name, sql: sql(sql_query, comments: comments), connection: connection } } before do
allow(::Gitlab::Metrics::WebTransaction).to receive(:current)
.and_return(web_transaction)
allow(::Gitlab::Metrics::BackgroundTransaction).to receive(:current)
.and_return(background_transaction)
end
it 'captures the metrics for web only' do
expect(web_transaction).to receive(:observe).with(:gitlab_database_transaction_seconds, 0.23)
it 'marks the current thread as using the database' do expect(background_transaction).not_to receive(:observe)
# since it would already have been toggled by other specs expect(background_transaction).not_to receive(:increment)
Thread.current[:uses_db_connection] = nil
expect { subscriber.sql(event) }.to change { Thread.current[:uses_db_connection] }.from(nil).to(true) subscriber.transaction(event)
end end
end
context 'when web transaction is available' do
let(:web_transaction) { double('Gitlab::Metrics::WebTransaction') }
before do
allow(::Gitlab::Metrics::WebTransaction).to receive(:current)
.and_return(web_transaction)
allow(::Gitlab::Metrics::BackgroundTransaction).to receive(:current)
.and_return(nil)
end
it 'captures the metrics for web only' do
expect(web_transaction).to receive(:observe).with(:gitlab_database_transaction_seconds, 0.23)
it_behaves_like 'record ActiveRecord metrics' expect(background_transaction).not_to receive(:observe)
it_behaves_like 'store ActiveRecord info in RequestStore' expect(background_transaction).not_to receive(:increment)
subscriber.transaction(event)
end
end end
end
context 'without Marginalia comments' do context 'when background transaction is available' do
let(:comments) { false } let(:background_transaction) { double('Gitlab::Metrics::BackgroundTransaction') }
before do
allow(::Gitlab::Metrics::WebTransaction).to receive(:current)
.and_return(nil)
allow(::Gitlab::Metrics::BackgroundTransaction).to receive(:current)
.and_return(background_transaction)
end
it_behaves_like 'track generic sql events' it 'captures the metrics for web only' do
expect(background_transaction).to receive(:observe).with(:gitlab_database_transaction_seconds, 0.23)
expect(web_transaction).not_to receive(:observe)
expect(web_transaction).not_to receive(:increment)
subscriber.transaction(event)
end
end
end end
context 'with Marginalia comments' do describe '#sql' do
let(:comments) { true } let(:payload) { { sql: 'SELECT * FROM users WHERE id = 10', connection: connection } }
it_behaves_like 'track generic sql events' let(:event) do
double(
:event,
name: 'sql.active_record',
duration: 2,
payload: payload
)
end
# Emulate Marginalia pre-pending comments
def sql(query, comments: true)
if comments && !%w[BEGIN COMMIT].include?(query)
"/*application:web,controller:badges,action:pipeline,correlation_id:01EYN39K9VMJC56Z7808N7RSRH*/ #{query}"
else
query
end
end
shared_examples 'track generic sql events' do
where(:name, :sql_query, :record_query, :record_write_query, :record_cached_query) do
'SQL' | 'SELECT * FROM users WHERE id = 10' | true | false | false
'SQL' | 'WITH active_milestones AS (SELECT COUNT(*), state FROM milestones GROUP BY state) SELECT * FROM active_milestones' | true | false | false
'SQL' | 'SELECT * FROM users WHERE id = 10 FOR UPDATE' | true | true | false
'SQL' | 'WITH archived_rows AS (SELECT * FROM users WHERE archived = true) INSERT INTO products_log SELECT * FROM archived_rows' | true | true | false
'SQL' | 'DELETE FROM users where id = 10' | true | true | false
'SQL' | 'INSERT INTO project_ci_cd_settings (project_id) SELECT id FROM projects' | true | true | false
'SQL' | 'UPDATE users SET admin = true WHERE id = 10' | true | true | false
'CACHE' | 'SELECT * FROM users WHERE id = 10' | true | false | true
'SCHEMA' | "SELECT attr.attname FROM pg_attribute attr INNER JOIN pg_constraint cons ON attr.attrelid = cons.conrelid AND attr.attnum = any(cons.conkey) WHERE cons.contype = 'p' AND cons.conrelid = '\"projects\"'::regclass" | false | false | false
nil | 'BEGIN' | false | false | false
nil | 'COMMIT' | false | false | false
end
with_them do
let(:payload) { { name: name, sql: sql(sql_query, comments: comments), connection: connection } }
it 'marks the current thread as using the database' do
# since it would already have been toggled by other specs
Thread.current[:uses_db_connection] = nil
expect { subscriber.sql(event) }.to change { Thread.current[:uses_db_connection] }.from(nil).to(true)
end
it_behaves_like 'record ActiveRecord metrics'
it_behaves_like 'store ActiveRecord info in RequestStore'
end
end
context 'without Marginalia comments' do
let(:comments) { false }
it_behaves_like 'track generic sql events'
end
context 'with Marginalia comments' do
let(:comments) { true }
it_behaves_like 'track generic sql events'
end
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