Skip to content

Commit aae3c62

Browse files
committed
Log DB query request metrics
- number of queries per request - accumulated query time
1 parent 2e0ca41 commit aae3c62

File tree

10 files changed

+98
-13
lines changed

10 files changed

+98
-13
lines changed

lib/cloud_controller/db.rb

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
require 'cloud_controller/db_connection/options_factory'
44
require 'cloud_controller/db_connection/finalizer'
55
require 'sequel/extensions/query_length_logging'
6+
require 'sequel/extensions/request_query_metrics'
67

78
module VCAP::CloudController
89
class DB
@@ -30,7 +31,7 @@ def self.connect(opts, logger)
3031
db.logger = logger
3132
db.sql_log_level = opts[:log_level]
3233
db.extension :caller_logging
33-
db.caller_logging_ignore = /sequel_paginator|query_length_logging|sequel_pg|paginated_collection_renderer/
34+
db.caller_logging_ignore = /sequel_paginator|query_length_logging|sequel_pg|paginated_collection_renderer|request_query_metrics/
3435
db.caller_logging_formatter = lambda do |caller|
3536
"(#{caller.sub(%r{^.*/cloud_controller_ng/}, '')})"
3637
end
@@ -43,6 +44,7 @@ def self.connect(opts, logger)
4344
add_connection_validator_extension(db, opts)
4445
db.extension(:requires_unique_column_names_in_subquery)
4546
add_connection_metrics_extension(db)
47+
db.extension(:request_query_metrics)
4648
db
4749
end
4850

lib/cloud_controller/logs/request_logs.rb

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,15 @@ def start_request(request_id, env)
2626
@incomplete_requests.store(request_id, env)
2727
end
2828

29-
def complete_request(request_id, status, env, time_taken)
29+
def complete_request(request_id, status, env, time_taken_ms, total_db_query_time_us, query_count)
3030
return if @incomplete_requests.delete(request_id).nil?
3131

3232
request = ActionDispatch::Request.new(env)
3333
@logger.info("Completed #{status} vcap-request-id: #{request_id}",
3434
{ status_code: status,
35-
time_taken_in_ms: time_taken,
35+
time_taken_in_ms: time_taken_ms,
36+
total_db_query_time_in_ms: (total_db_query_time_us.to_f / 1_000),
37+
db_query_count: query_count,
3638
request_method: request.request_method,
3739
request_fullpath: request.filtered_path })
3840
end
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# frozen_string_literal: true
2+
3+
module Sequel::RequestQueryMetrics
4+
def log_connection_yield(sql, conn, args=nil)
5+
start_timer = timer
6+
7+
result = super
8+
9+
VCAP::Request.increment_total_db_query_time_us(timer - start_timer)
10+
VCAP::Request.increment_db_query_count
11+
12+
result
13+
end
14+
15+
private
16+
17+
def timer
18+
Process.clock_gettime(Process::CLOCK_MONOTONIC, :microsecond)
19+
end
20+
end
21+
22+
Sequel::Database.register_extension(:request_query_metrics, Sequel::RequestQueryMetrics)

lib/vcap/request.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,29 @@ def b3_span_id=(span_id)
5555
def b3_span_id
5656
Thread.current[:b3_span_id]
5757
end
58+
59+
def total_db_query_time_us
60+
Thread.current[:total_db_query_time_us] ||= 0
61+
end
62+
63+
def increment_total_db_query_time_us(duration)
64+
Thread.current[:total_db_query_time_us] ||= 0
65+
Thread.current[:total_db_query_time_us] += duration
66+
end
67+
68+
def db_query_count
69+
Thread.current[:db_query_count] ||= 0
70+
end
71+
72+
def increment_db_query_count
73+
Thread.current[:db_query_count] ||= 0
74+
Thread.current[:db_query_count] += 1
75+
end
76+
77+
def reset_db_query_metrics
78+
Thread.current[:total_db_query_time_us] = 0
79+
Thread.current[:db_query_count] = 0
80+
end
5881
end
5982
end
6083
end

middleware/request_logs.rb

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,22 @@ def initialize(app, request_logs)
99
def call(env)
1010
request_id = env['cf.request_id']
1111
@request_logs.start_request(request_id, env)
12-
start_time = Time.now
12+
start_timer = timer
1313

1414
status, headers, body = @app.call(env)
15-
# convert to milliseconds
16-
time_taken = (Time.now - start_time) * 1000
17-
time_taken = time_taken.to_i
18-
@request_logs.complete_request(request_id, status, env, time_taken)
15+
time_taken_ms = timer - start_timer
16+
total_db_query_time_us = ::VCAP::Request.total_db_query_time_us
17+
query_count = ::VCAP::Request.db_query_count
18+
@request_logs.complete_request(request_id, status, env, time_taken_ms, total_db_query_time_us, query_count)
1919

2020
[status, headers, body]
2121
end
22+
23+
private
24+
25+
def timer
26+
Process.clock_gettime(Process::CLOCK_MONOTONIC, :millisecond)
27+
end
2228
end
2329
end
2430
end

middleware/request_metrics.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ def initialize(app, request_metrics)
77
end
88

99
def call(env)
10+
::VCAP::Request.reset_db_query_metrics
1011
@request_metrics.start_request
1112

1213
status, headers, body = @app.call(env)

spec/unit/lib/cloud_controller/logs/request_logs_spec.rb

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ module VCAP::CloudController::Logs
99
let(:fake_fullpath) { 'fullpath' }
1010
let(:fake_request) { double('request', request_method: 'request_method', ip: fake_ip, filtered_path: 'filtered_path', fullpath: fake_fullpath) }
1111
let(:request_id) { 'ID' }
12-
let(:time_taken) { 30 }
12+
let(:time_taken_ms) { 30 }
13+
let(:total_db_query_time_us) { 123_456 }
14+
let(:query_count) { 5 }
1315
let(:env) { { 'cf.user_guid' => 'user-guid' } }
1416
let(:status) { 200 }
1517

@@ -46,10 +48,12 @@ module VCAP::CloudController::Logs
4648
end
4749

4850
it 'logs the completion of the request' do
49-
request_logs.complete_request(request_id, status, env, time_taken)
51+
request_logs.complete_request(request_id, status, env, time_taken_ms, total_db_query_time_us, query_count)
5052
expect(logger).to have_received(:info).with(
5153
/\ACompleted 200 vcap-request-id: ID/,
5254
time_taken_in_ms: 30,
55+
total_db_query_time_in_ms: 123.456,
56+
db_query_count: 5,
5357
request_method: 'request_method',
5458
request_fullpath: 'filtered_path',
5559
status_code: 200
@@ -59,7 +63,7 @@ module VCAP::CloudController::Logs
5963

6064
context 'without a matching start request' do
6165
it 'does not log the completion of the request' do
62-
request_logs.complete_request(request_id, status, env, time_taken)
66+
request_logs.complete_request(request_id, status, env, time_taken_ms, total_db_query_time_us, query_count)
6367
expect(logger).not_to have_received(:info)
6468
end
6569
end
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
require 'spec_helper'
2+
3+
RSpec.describe Sequel::RequestQueryMetrics do
4+
let(:db) { DbConfig.new.connection }
5+
6+
before do
7+
db.extension(:request_query_metrics)
8+
allow(VCAP::Request).to receive(:increment_total_db_query_time_us).and_wrap_original do |original_method|
9+
original_method.call(1_234)
10+
end
11+
end
12+
13+
it 'increments the thread local total query time on each query' do
14+
expect do
15+
2.times { db.fetch('SELECT 1').all }
16+
end.to change(VCAP::Request, :total_db_query_time_us).by(2_468)
17+
end
18+
19+
it 'increments the thread local query count on each query' do
20+
expect do
21+
db.fetch('SELECT 1').all
22+
end.to change(VCAP::Request, :db_query_count).by(1)
23+
end
24+
end

spec/unit/middleware/request_logs_spec.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ module Middleware
2121

2222
it 'calls complete request on request logs after the request' do
2323
middleware.call(env)
24-
expect(request_logs).to have_received(:complete_request).with('ID', 200, { 'cf.request_id' => 'ID' }, be_a(Numeric))
24+
expect(request_logs).to have_received(:complete_request).with('ID', 200, { 'cf.request_id' => 'ID' }, be_a(Numeric), be_a(Numeric), be_a(Numeric))
2525
end
2626
end
2727
end

spec/unit/middleware/request_metrics_spec.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ module Middleware
99
let(:request_metrics) { instance_double(VCAP::CloudController::Metrics::RequestMetrics, start_request: nil, complete_request: nil) }
1010

1111
describe 'handling the request' do
12-
it 'calls start request on request metrics before the request' do
12+
it 'resets the db query count and calls start request on request metrics before the request' do
13+
expect(VCAP::Request).to receive(:reset_db_query_metrics)
1314
middleware.call({})
1415
expect(request_metrics).to have_received(:start_request)
1516
end

0 commit comments

Comments
 (0)