Commit dac8e99e authored by Stan Hu's avatar Stan Hu

Add Redis call details in Peek performance bar

Since Redis timings appear to be increasing in production, this change
makes it easier to see what exactly which queries are being called and
where.

This is done by prepending modules in peek-redis to store the call
details.

This commit redact values for all SET commands (e.g. HMSET, GETSET,
etc.).
parent c1d250c2
...@@ -4,14 +4,12 @@ import { glEmojiTag } from '~/emoji'; ...@@ -4,14 +4,12 @@ import { glEmojiTag } from '~/emoji';
import detailedMetric from './detailed_metric.vue'; import detailedMetric from './detailed_metric.vue';
import requestSelector from './request_selector.vue'; import requestSelector from './request_selector.vue';
import simpleMetric from './simple_metric.vue';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
export default { export default {
components: { components: {
detailedMetric, detailedMetric,
requestSelector, requestSelector,
simpleMetric,
}, },
props: { props: {
store: { store: {
...@@ -43,8 +41,13 @@ export default { ...@@ -43,8 +41,13 @@ export default {
details: 'details', details: 'details',
keys: ['feature', 'request'], keys: ['feature', 'request'],
}, },
{
metric: 'redis',
header: 'Redis calls',
details: 'details',
keys: ['cmd'],
},
], ],
simpleMetrics: ['redis'],
data() { data() {
return { currentRequestId: '' }; return { currentRequestId: '' };
}, },
...@@ -124,12 +127,6 @@ export default { ...@@ -124,12 +127,6 @@ export default {
</button> </button>
<a v-else :href="profileUrl">{{ s__('PerformanceBar|profile') }}</a> <a v-else :href="profileUrl">{{ s__('PerformanceBar|profile') }}</a>
</div> </div>
<simple-metric
v-for="metric in $options.simpleMetrics"
:key="metric"
:current-request="currentRequest"
:metric="metric"
/>
<div id="peek-view-gc" class="view"> <div id="peek-view-gc" class="view">
<span v-if="currentRequest.details" class="bold"> <span v-if="currentRequest.details" class="bold">
<span title="Invoke Time">{{ currentRequest.details.gc.gc_time }}</span <span title="Invoke Time">{{ currentRequest.details.gc.gc_time }}</span
......
<script>
export default {
props: {
currentRequest: {
type: Object,
required: true,
},
metric: {
type: String,
required: true,
},
},
computed: {
duration() {
return (
this.currentRequest.details[this.metric] &&
this.currentRequest.details[this.metric].duration
);
},
calls() {
return (
this.currentRequest.details[this.metric] && this.currentRequest.details[this.metric].calls
);
},
},
};
</script>
<template>
<div :id="`peek-view-${metric}`" class="view">
<span v-if="currentRequest.details" class="bold"> {{ duration }} / {{ calls }} </span>
{{ metric }}
</div>
</template>
---
title: Add Redis call details in Peek performance bar
merge_request: 30191
author:
type: changed
# frozen_string_literal: true
require 'redis'
require 'peek-redis'
module Gitlab
module Peek
module RedisInstrumented
def call(*args, &block)
start = Time.now
super(*args, &block)
ensure
duration = (Time.now - start)
add_call_details(duration, args)
end
private
def add_call_details(duration, args)
# redis-rb passes an array (e.g. [:get, key])
return unless args.length == 1
detail_store << {
cmd: args.first,
duration: duration,
backtrace: Gitlab::Profiler.clean_backtrace(caller)
}
end
def detail_store
::Gitlab::SafeRequestStore['redis_call_details'] ||= []
end
end
end
end
module Peek
module Views
module RedisDetailed
def results
super.merge(details: details)
end
def details
detail_store
.sort { |a, b| b[:duration] <=> a[:duration] }
.map(&method(:format_call_details))
end
def detail_store
::Gitlab::SafeRequestStore['redis_call_details'] ||= []
end
def format_call_details(call)
call.merge(cmd: format_command(call[:cmd]),
duration: (call[:duration] * 1000).round(3))
end
def format_command(cmd)
# Scrub out the value of the SET calls to avoid binary
# data or large data from spilling into the view
if cmd.length >= 2 && cmd.first =~ /set/i
cmd[-1] = "<redacted>"
end
cmd.join(' ')
end
end
end
end
class Redis::Client
prepend Gitlab::Peek::RedisInstrumented
end
module Peek
module Views
class Redis < View
prepend Peek::Views::RedisDetailed
end
end
end
import Vue from 'vue';
import simpleMetric from '~/performance_bar/components/simple_metric.vue';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
describe('simpleMetric', () => {
let vm;
afterEach(() => {
vm.$destroy();
});
describe('when the current request has no details', () => {
beforeEach(() => {
vm = mountComponent(Vue.extend(simpleMetric), {
currentRequest: {},
metric: 'gitaly',
});
});
it('does not display details', () => {
expect(vm.$el.innerText).not.toContain('/');
});
it('displays the metric name', () => {
expect(vm.$el.innerText).toContain('gitaly');
});
});
describe('when the current request has details', () => {
beforeEach(() => {
vm = mountComponent(Vue.extend(simpleMetric), {
currentRequest: {
details: { gitaly: { duration: '123ms', calls: '456' } },
},
metric: 'gitaly',
});
});
it('diplays details', () => {
expect(vm.$el.innerText.replace(/\s+/g, ' ')).toContain('123ms / 456');
});
it('displays the metric name', () => {
expect(vm.$el.innerText).toContain('gitaly');
});
});
});
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