Commit 7d0a9c11 authored by Kushal Pandya's avatar Kushal Pandya

Merge branch 'qmnguyen0711/rollout-performance-bar-sort-order' into 'master'

Enable chronological sort order for other items in the performance bar

See merge request gitlab-org/gitlab!58572
parents 7d880b7d b5357b2b
...@@ -54,11 +54,17 @@ export default { ...@@ -54,11 +54,17 @@ export default {
return this.currentRequest.details[this.metric]; return this.currentRequest.details[this.metric];
}, },
metricDetailsSummary() { metricDetailsSummary() {
return { const summary = {};
[s__('Total')]: this.metricDetails.calls,
[s__('PerformanceBar|Total duration')]: this.metricDetails.duration, if (!this.metricDetails.summaryOptions?.hideTotal) {
...(this.metricDetails.summary || {}), summary[s__('Total')] = this.metricDetails.calls;
}; }
if (!this.metricDetails.summaryOptions?.hideDuration) {
summary[s__('PerformanceBar|Total duration')] = this.metricDetails.duration;
}
return { ...summary, ...(this.metricDetails.summary || {}) };
}, },
metricDetailsLabel() { metricDetailsLabel() {
if (this.metricDetails.duration && this.metricDetails.calls) { if (this.metricDetails.duration && this.metricDetails.calls) {
......
/* eslint-disable @gitlab/require-i18n-strings */
import Vue from 'vue'; import Vue from 'vue';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { numberToHumanSize } from '~/lib/utils/number_utils';
import { s__ } from '~/locale';
import Translate from '~/vue_shared/translate'; import Translate from '~/vue_shared/translate';
import initPerformanceBarLog from './performance_bar_log'; import initPerformanceBarLog from './performance_bar_log';
...@@ -75,40 +76,53 @@ const initPerformanceBar = (el) => { ...@@ -75,40 +76,53 @@ const initPerformanceBar = (el) => {
const resourceEntries = performance.getEntriesByType('resource'); const resourceEntries = performance.getEntriesByType('resource');
let durationString = ''; let durationString = '';
let summary = {};
if (navigationEntries.length > 0) { if (navigationEntries.length > 0) {
durationString = `${Math.round(navigationEntries[0].responseEnd)} | `; const backend = Math.round(navigationEntries[0].responseEnd);
durationString += `${Math.round(paintEntries[1].startTime)} | `; const firstContentfulPaint = Math.round(paintEntries[1].startTime);
durationString += ` ${Math.round(navigationEntries[0].domContentLoadedEventEnd)}`; const domContentLoaded = Math.round(navigationEntries[0].domContentLoadedEventEnd);
summary = {
[s__('PerformanceBar|Backend')]: backend,
[s__('PerformanceBar|First Contentful Paint')]: firstContentfulPaint,
[s__('PerformanceBar|DOM Content Loaded')]: domContentLoaded,
};
durationString = `${backend} | ${firstContentfulPaint} | ${domContentLoaded}`;
} }
let newEntries = resourceEntries.map(this.transformResourceEntry); let newEntries = resourceEntries.map(this.transformResourceEntry);
this.updateFrontendPerformanceMetrics(durationString, newEntries); this.updateFrontendPerformanceMetrics(durationString, summary, newEntries);
if ('PerformanceObserver' in window) { if ('PerformanceObserver' in window) {
// We start observing for more incoming timings // We start observing for more incoming timings
const observer = new PerformanceObserver((list) => { const observer = new PerformanceObserver((list) => {
newEntries = newEntries.concat(list.getEntries().map(this.transformResourceEntry)); newEntries = newEntries.concat(list.getEntries().map(this.transformResourceEntry));
this.updateFrontendPerformanceMetrics(durationString, newEntries); this.updateFrontendPerformanceMetrics(durationString, summary, newEntries);
}); });
observer.observe({ entryTypes: ['resource'] }); observer.observe({ entryTypes: ['resource'] });
} }
} }
}, },
updateFrontendPerformanceMetrics(durationString, requestEntries) { updateFrontendPerformanceMetrics(durationString, summary, requestEntries) {
this.store.setRequestDetailsData(this.requestId, 'total', { this.store.setRequestDetailsData(this.requestId, 'total', {
duration: durationString, duration: durationString,
calls: requestEntries.length, calls: requestEntries.length,
details: requestEntries, details: requestEntries,
summaryOptions: {
hideDuration: true,
},
summary,
}); });
}, },
transformResourceEntry(entry) { transformResourceEntry(entry) {
const nf = new Intl.NumberFormat();
return { return {
start: entry.startTime,
name: entry.name.replace(document.location.origin, ''), name: entry.name.replace(document.location.origin, ''),
duration: Math.round(entry.duration), duration: Math.round(entry.duration),
size: entry.transferSize ? `${nf.format(entry.transferSize)} bytes` : 'cached', size: entry.transferSize ? numberToHumanSize(entry.transferSize) : 'cached',
}; };
}, },
}, },
......
---
title: Enable chronological sort order for other items in the performance bar
merge_request: 58572
author:
type: changed
...@@ -50,11 +50,11 @@ module Gitlab ...@@ -50,11 +50,11 @@ module Gitlab
end end
def recording_request def recording_request
start = Gitlab::Metrics::System.monotonic_time @start = Gitlab::Metrics::System.monotonic_time
yield yield
ensure ensure
@duration += Gitlab::Metrics::System.monotonic_time - start @duration += Gitlab::Metrics::System.monotonic_time - @start
end end
def store_timings def store_timings
...@@ -64,8 +64,14 @@ module Gitlab ...@@ -64,8 +64,14 @@ module Gitlab
request_hash = @request.is_a?(Google::Protobuf::MessageExts) ? @request.to_h : {} request_hash = @request.is_a?(Google::Protobuf::MessageExts) ? @request.to_h : {}
GitalyClient.add_call_details(feature: "#{@service}##{@rpc}", duration: @duration, request: request_hash, rpc: @rpc, GitalyClient.add_call_details(
backtrace: Gitlab::BacktraceCleaner.clean_backtrace(caller)) start: @start,
feature: "#{@service}##{@rpc}",
duration: @duration,
request: request_hash,
rpc: @rpc,
backtrace: Gitlab::BacktraceCleaner.clean_backtrace(caller)
)
end end
end end
end end
......
...@@ -37,7 +37,7 @@ module Gitlab ...@@ -37,7 +37,7 @@ module Gitlab
def request(event) def request(event)
payload = event.payload payload = event.payload
add_to_detail_store(payload) add_to_detail_store(event.time, payload)
add_to_request_store(payload) add_to_request_store(payload)
expose_metrics(payload) expose_metrics(payload)
end end
...@@ -48,10 +48,11 @@ module Gitlab ...@@ -48,10 +48,11 @@ module Gitlab
::Gitlab::Metrics::Transaction.current ::Gitlab::Metrics::Transaction.current
end end
def add_to_detail_store(payload) def add_to_detail_store(start, payload)
return unless Gitlab::PerformanceBar.enabled_for_request? return unless Gitlab::PerformanceBar.enabled_for_request?
self.class.detail_store << { self.class.detail_store << {
start: start,
duration: payload[:duration], duration: payload[:duration],
scheme: payload[:scheme], scheme: payload[:scheme],
method: payload[:method], method: payload[:method],
......
...@@ -22634,9 +22634,15 @@ msgstr "" ...@@ -22634,9 +22634,15 @@ msgstr ""
msgid "Performance optimization" msgid "Performance optimization"
msgstr "" msgstr ""
msgid "PerformanceBar|Backend"
msgstr ""
msgid "PerformanceBar|Bullet notifications" msgid "PerformanceBar|Bullet notifications"
msgstr "" msgstr ""
msgid "PerformanceBar|DOM Content Loaded"
msgstr ""
msgid "PerformanceBar|Download" msgid "PerformanceBar|Download"
msgstr "" msgstr ""
...@@ -22646,6 +22652,9 @@ msgstr "" ...@@ -22646,6 +22652,9 @@ msgstr ""
msgid "PerformanceBar|External Http calls" msgid "PerformanceBar|External Http calls"
msgstr "" msgstr ""
msgid "PerformanceBar|First Contentful Paint"
msgstr ""
msgid "PerformanceBar|Frontend resources" msgid "PerformanceBar|Frontend resources"
msgstr "" msgstr ""
......
...@@ -120,6 +120,73 @@ describe('detailedMetric', () => { ...@@ -120,6 +120,73 @@ describe('detailedMetric', () => {
}); });
}); });
describe('when the details have summaryOptions option', () => {
const gitalyDetails = {
duration: '123ms',
calls: 456,
details: requestDetails,
warnings: ['gitaly calls: 456 over 30'],
};
describe('when the details have summaryOptions > hideTotal option', () => {
beforeEach(() => {
createComponent({
currentRequest: {
details: {
gitaly: { ...gitalyDetails, summaryOptions: { hideTotal: true } },
},
},
});
});
it('displays a summary section', () => {
expect(findAllSummaryItems()).toEqual(['Total duration 123ms']);
});
});
describe('when the details have summaryOptions > hideDuration option', () => {
beforeEach(() => {
createComponent({
currentRequest: {
details: {
gitaly: { ...gitalyDetails, summaryOptions: { hideDuration: true } },
},
},
});
});
it('displays a summary section', () => {
expect(findAllSummaryItems()).toEqual(['Total 456']);
});
});
describe('when the details have both summary and summaryOptions field', () => {
beforeEach(() => {
createComponent({
currentRequest: {
details: {
gitaly: {
...gitalyDetails,
summary: {
'In controllers': 100,
'In middlewares': 20,
},
summaryOptions: {
hideDuration: true,
hideTotal: true,
},
},
},
},
});
});
it('displays a summary section', () => {
expect(findAllSummaryItems()).toEqual(['In controllers 100', 'In middlewares 20']);
});
});
});
describe("when the details don't have a start field", () => { describe("when the details don't have a start field", () => {
beforeEach(() => { beforeEach(() => {
createComponent({ createComponent({
......
...@@ -24,11 +24,14 @@ RSpec.describe Gitlab::GitalyClient::Call do ...@@ -24,11 +24,14 @@ RSpec.describe Gitlab::GitalyClient::Call do
def expect_call_details_to_match(duration_higher_than: 0) def expect_call_details_to_match(duration_higher_than: 0)
expect(client.list_call_details.size).to eq(1) expect(client.list_call_details.size).to eq(1)
expect(client.list_call_details.first) expect(client.list_call_details.first)
.to match a_hash_including(feature: "#{service}##{rpc}", .to match a_hash_including(
duration: a_value > duration_higher_than, start: a_value > 0,
request: an_instance_of(Hash), feature: "#{service}##{rpc}",
rpc: rpc, duration: a_value > duration_higher_than,
backtrace: an_instance_of(Array)) request: an_instance_of(Hash),
rpc: rpc,
backtrace: an_instance_of(Array)
)
end end
context 'when the response is not an enumerator' do context 'when the response is not an enumerator' do
......
...@@ -6,29 +6,45 @@ RSpec.describe Gitlab::Metrics::Subscribers::ExternalHttp, :request_store do ...@@ -6,29 +6,45 @@ RSpec.describe Gitlab::Metrics::Subscribers::ExternalHttp, :request_store do
let(:transaction) { Gitlab::Metrics::Transaction.new } let(:transaction) { Gitlab::Metrics::Transaction.new }
let(:subscriber) { described_class.new } let(:subscriber) { described_class.new }
around do |example|
freeze_time { example.run }
end
let(:event_1) do let(:event_1) do
double(:event, payload: { double(
method: 'POST', code: "200", duration: 0.321, :event,
scheme: 'https', host: 'gitlab.com', port: 80, path: '/api/v4/projects', payload: {
query: 'current=true' method: 'POST', code: "200", duration: 0.321,
}) scheme: 'https', host: 'gitlab.com', port: 80, path: '/api/v4/projects',
query: 'current=true'
},
time: Time.current
)
end end
let(:event_2) do let(:event_2) do
double(:event, payload: { double(
method: 'GET', code: "301", duration: 0.12, :event,
scheme: 'http', host: 'gitlab.com', port: 80, path: '/api/v4/projects/2', payload: {
query: 'current=true' method: 'GET', code: "301", duration: 0.12,
}) scheme: 'http', host: 'gitlab.com', port: 80, path: '/api/v4/projects/2',
query: 'current=true'
},
time: Time.current
)
end end
let(:event_3) do let(:event_3) do
double(:event, payload: { double(
method: 'POST', duration: 5.3, :event,
scheme: 'http', host: 'gitlab.com', port: 80, path: '/api/v4/projects/2/issues', payload: {
query: 'current=true', method: 'POST', duration: 5.3,
exception_object: Net::ReadTimeout.new scheme: 'http', host: 'gitlab.com', port: 80, path: '/api/v4/projects/2/issues',
}) query: 'current=true',
exception_object: Net::ReadTimeout.new
},
time: Time.current
)
end end
describe '.detail_store' do describe '.detail_store' do
...@@ -134,19 +150,22 @@ RSpec.describe Gitlab::Metrics::Subscribers::ExternalHttp, :request_store do ...@@ -134,19 +150,22 @@ RSpec.describe Gitlab::Metrics::Subscribers::ExternalHttp, :request_store do
subscriber.request(event_3) subscriber.request(event_3)
expect(Gitlab::SafeRequestStore[:external_http_detail_store].length).to eq(3) expect(Gitlab::SafeRequestStore[:external_http_detail_store].length).to eq(3)
expect(Gitlab::SafeRequestStore[:external_http_detail_store][0]).to include( expect(Gitlab::SafeRequestStore[:external_http_detail_store][0]).to match a_hash_including(
start: be_like_time(Time.current),
method: 'POST', code: "200", duration: 0.321, method: 'POST', code: "200", duration: 0.321,
scheme: 'https', host: 'gitlab.com', port: 80, path: '/api/v4/projects', scheme: 'https', host: 'gitlab.com', port: 80, path: '/api/v4/projects',
query: 'current=true', exception_object: nil, query: 'current=true', exception_object: nil,
backtrace: be_a(Array) backtrace: be_a(Array)
) )
expect(Gitlab::SafeRequestStore[:external_http_detail_store][1]).to include( expect(Gitlab::SafeRequestStore[:external_http_detail_store][1]).to match a_hash_including(
start: be_like_time(Time.current),
method: 'GET', code: "301", duration: 0.12, method: 'GET', code: "301", duration: 0.12,
scheme: 'http', host: 'gitlab.com', port: 80, path: '/api/v4/projects/2', scheme: 'http', host: 'gitlab.com', port: 80, path: '/api/v4/projects/2',
query: 'current=true', exception_object: nil, query: 'current=true', exception_object: nil,
backtrace: be_a(Array) backtrace: be_a(Array)
) )
expect(Gitlab::SafeRequestStore[:external_http_detail_store][2]).to include( expect(Gitlab::SafeRequestStore[:external_http_detail_store][2]).to match a_hash_including(
start: be_like_time(Time.current),
method: 'POST', duration: 5.3, method: 'POST', duration: 5.3,
scheme: 'http', host: 'gitlab.com', port: 80, path: '/api/v4/projects/2/issues', scheme: 'http', host: 'gitlab.com', port: 80, path: '/api/v4/projects/2/issues',
query: 'current=true', query: 'current=true',
......
...@@ -11,6 +11,10 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do ...@@ -11,6 +11,10 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do
allow(Gitlab::PerformanceBar).to receive(:enabled_for_request?).and_return(true) allow(Gitlab::PerformanceBar).to receive(:enabled_for_request?).and_return(true)
end end
around do |example|
freeze_time { example.run }
end
let(:event_1) do let(:event_1) do
{ {
method: 'POST', code: "200", duration: 0.03, method: 'POST', code: "200", duration: 0.03,
...@@ -44,9 +48,9 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do ...@@ -44,9 +48,9 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do
end end
it 'returns aggregated results' do it 'returns aggregated results' do
subscriber.request(double(:event, payload: event_1)) subscriber.request(double(:event, payload: event_1, time: Time.current))
subscriber.request(double(:event, payload: event_2)) subscriber.request(double(:event, payload: event_2, time: Time.current))
subscriber.request(double(:event, payload: event_3)) subscriber.request(double(:event, payload: event_3, time: Time.current))
results = subject.results results = subject.results
expect(results[:calls]).to eq(3) expect(results[:calls]).to eq(3)
...@@ -55,6 +59,7 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do ...@@ -55,6 +59,7 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do
expected = [ expected = [
{ {
start: be_like_time(Time.current),
duration: 30.0, duration: 30.0,
label: "POST https://gitlab.com:80/api/v4/projects?current=true", label: "POST https://gitlab.com:80/api/v4/projects?current=true",
code: "Response status: 200", code: "Response status: 200",
...@@ -63,6 +68,7 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do ...@@ -63,6 +68,7 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do
warnings: [] warnings: []
}, },
{ {
start: be_like_time(Time.current),
duration: 1300, duration: 1300,
label: "POST http://gitlab.com:80/api/v4/projects/2/issues?current=true", label: "POST http://gitlab.com:80/api/v4/projects/2/issues?current=true",
code: nil, code: nil,
...@@ -71,6 +77,7 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do ...@@ -71,6 +77,7 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do
warnings: ["1300.0 over 100"] warnings: ["1300.0 over 100"]
}, },
{ {
start: be_like_time(Time.current),
duration: 5.0, duration: 5.0,
label: "GET http://gitlab.com:80/api/v4/projects/2?current=true", label: "GET http://gitlab.com:80/api/v4/projects/2?current=true",
code: "Response status: 301", code: "Response status: 301",
...@@ -81,7 +88,7 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do ...@@ -81,7 +88,7 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do
] ]
expect( expect(
results[:details].map { |data| data.slice(:duration, :label, :code, :proxy, :error, :warnings) } results[:details].map { |data| data.slice(:start, :duration, :label, :code, :proxy, :error, :warnings) }
).to match_array(expected) ).to match_array(expected)
end end
...@@ -91,10 +98,11 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do ...@@ -91,10 +98,11 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do
end end
it 'displays IPv4 in the label' do it 'displays IPv4 in the label' do
subscriber.request(double(:event, payload: event_1)) subscriber.request(double(:event, payload: event_1, time: Time.current))
expect(subject.results[:details]).to contain_exactly( expect(subject.results[:details]).to contain_exactly(
a_hash_including( a_hash_including(
start: be_like_time(Time.current),
duration: 30.0, duration: 30.0,
label: "POST https://1.2.3.4:80/api/v4/projects?current=true", label: "POST https://1.2.3.4:80/api/v4/projects?current=true",
code: "Response status: 200", code: "Response status: 200",
...@@ -112,10 +120,11 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do ...@@ -112,10 +120,11 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do
end end
it 'displays IPv6 in the label' do it 'displays IPv6 in the label' do
subscriber.request(double(:event, payload: event_1)) subscriber.request(double(:event, payload: event_1, time: Time.current))
expect(subject.results[:details]).to contain_exactly( expect(subject.results[:details]).to contain_exactly(
a_hash_including( a_hash_including(
start: be_like_time(Time.current),
duration: 30.0, duration: 30.0,
label: "POST https://[2606:4700:90:0:f22e:fbec:5bed:a9b9]:80/api/v4/projects?current=true", label: "POST https://[2606:4700:90:0:f22e:fbec:5bed:a9b9]:80/api/v4/projects?current=true",
code: "Response status: 200", code: "Response status: 200",
...@@ -133,10 +142,11 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do ...@@ -133,10 +142,11 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do
end end
it 'converts query hash into a query string' do it 'converts query hash into a query string' do
subscriber.request(double(:event, payload: event_1)) subscriber.request(double(:event, payload: event_1, time: Time.current))
expect(subject.results[:details]).to contain_exactly( expect(subject.results[:details]).to contain_exactly(
a_hash_including( a_hash_including(
start: be_like_time(Time.current),
duration: 30.0, duration: 30.0,
label: "POST https://gitlab.com:80/api/v4/projects?current=true&item1=string&item2%5B%5D=1&item2%5B%5D=2", label: "POST https://gitlab.com:80/api/v4/projects?current=true&item1=string&item2%5B%5D=1&item2%5B%5D=2",
code: "Response status: 200", code: "Response status: 200",
...@@ -154,10 +164,11 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do ...@@ -154,10 +164,11 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do
end end
it 'displays unknown in the label' do it 'displays unknown in the label' do
subscriber.request(double(:event, payload: event_1)) subscriber.request(double(:event, payload: event_1, time: Time.current))
expect(subject.results[:details]).to contain_exactly( expect(subject.results[:details]).to contain_exactly(
a_hash_including( a_hash_including(
start: be_like_time(Time.current),
duration: 30.0, duration: 30.0,
label: "POST unknown", label: "POST unknown",
code: "Response status: 200", code: "Response status: 200",
...@@ -176,10 +187,11 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do ...@@ -176,10 +187,11 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do
end end
it 'displays unknown in the label' do it 'displays unknown in the label' do
subscriber.request(double(:event, payload: event_1)) subscriber.request(double(:event, payload: event_1, time: Time.current))
expect(subject.results[:details]).to contain_exactly( expect(subject.results[:details]).to contain_exactly(
a_hash_including( a_hash_including(
start: be_like_time(Time.current),
duration: 30.0, duration: 30.0,
label: "POST unknown", label: "POST unknown",
code: "Response status: 200", code: "Response status: 200",
...@@ -198,10 +210,11 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do ...@@ -198,10 +210,11 @@ RSpec.describe Peek::Views::ExternalHttp, :request_store do
end end
it 'displays unknown in the label' do it 'displays unknown in the label' do
subscriber.request(double(:event, payload: event_1)) subscriber.request(double(:event, payload: event_1, time: Time.current))
expect(subject.results[:details]).to contain_exactly( expect(subject.results[:details]).to contain_exactly(
a_hash_including( a_hash_including(
start: be_like_time(Time.current),
duration: 30.0, duration: 30.0,
label: "POST unknown", label: "POST unknown",
code: "Response status: 200", code: "Response status: 200",
......
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