in spec/support/database/auto_explain.rb [16:134]
def record
Gitlab::Database::EachDatabase.each_connection do |connection, connection_name|
next unless record_auto_explain?(connection)
connection.execute(<<~SQL.squish)
CREATE EXTENSION IF NOT EXISTS file_fdw;
CREATE SERVER IF NOT EXISTS pglog FOREIGN DATA WRAPPER file_fdw;
SQL
csvlog_columns = [
'log_time timestamp(3) with time zone',
'user_name text',
'database_name text',
'process_id integer',
'connection_from text',
'session_id text',
'session_line_num bigint',
'command_tag text',
'session_start_time timestamp with time zone',
'virtual_transaction_id text',
'transaction_id bigint',
'error_severity text',
'sql_state_code text',
'message text',
'detail text',
'hint text',
'internal_query text',
'internal_query_pos integer',
'context text',
'query text',
'query_pos integer',
'location text',
'application_name text',
'backend_type text',
'leader_pid integer',
'query_id bigint'
]
connection.transaction do
connection.execute(<<~SQL.squish)
CREATE FOREIGN TABLE IF NOT EXISTS pglog (
SERVER pglog
OPTIONS ( filename 'log/pglog.csv', format 'csv' );
SQL
log_file = Rails.root.join(
File.dirname(ENV.fetch('RSPEC_AUTO_EXPLAIN_LOG_PATH', 'auto_explain/auto_explain.ndjson.gz')),
"#{ENV.fetch('CI_JOB_NAME_SLUG', 'rspec')}.#{Process.pid}.#{connection_name}.ndjson.gz"
)
FileUtils.mkdir_p(File.dirname(log_file))
fingerprints = Set.new
recording_start = Time.now
Zlib::GzipWriter.open(log_file) do |gz|
pg = connection.raw_connection
pg.exec('SET statement_timeout TO 0;')
pg.send_query(<<~SQL.squish)
WITH base_data AS (
SELECT
substring(message from '\{.*$')::jsonb AS message,
query_id
FROM pglog
WHERE message LIKE '%{%'
)
(
SELECT DISTINCT ON (query_id)
m.query_id,
m.message->>'Query Text' as query,
m.message->'Plan' as plan
FROM base_data m
WHERE m.query_id != 0
)
UNION ALL
(
SELECT
m.query_id,
m.message->>'Query Text' as query,
m.message->'Plan' as plan
FROM base_data m
WHERE m.query_id = 0
)
ORDER BY query_id;
SQL
pg.set_single_row_mode
pg.get_result.stream_each do |row|
query = row['query']
fingerprint = PgQuery.fingerprint(query)
next unless fingerprints.add?(fingerprint)
plan = Gitlab::Json.parse(row['plan'])
output = {
query_id: row['query_id'],
query: query,
plan: plan,
fingerprint: fingerprint,
normalized: PgQuery.normalize(query)
}
gz.puts Gitlab::Json.generate(output)
end
puts "auto_explain log contains #{fingerprints.size} entries for #{connection_name}, writing to #{log_file}"
puts "took #{Time.now - recording_start}"
end
raise ActiveRecord::Rollback
end
end
end