Commit 4a726bd1 authored by GitLab Bot's avatar GitLab Bot

Automatic merge of gitlab-org/gitlab-ce master

parents c70d390b fd5772a8
...@@ -22,7 +22,7 @@ export default (resolvers = {}, config = {}) => { ...@@ -22,7 +22,7 @@ export default (resolvers = {}, config = {}) => {
return new ApolloClient({ return new ApolloClient({
link: ApolloLink.split( link: ApolloLink.split(
operation => operation.getContext().hasUpload, operation => operation.getContext().hasUpload || operation.getContext().isSingleRequest,
createUploadLink(httpOptions), createUploadLink(httpOptions),
new BatchHttpLink(httpOptions), new BatchHttpLink(httpOptions),
), ),
......
<script> <script>
import { GlTooltipDirective, GlLink, GlButton } from '@gitlab/ui'; import { GlTooltipDirective, GlLink, GlButton, GlLoadingIcon } from '@gitlab/ui';
import { sprintf, s__ } from '~/locale'; import { sprintf, s__ } from '~/locale';
import Icon from '../../vue_shared/components/icon.vue'; import Icon from '../../vue_shared/components/icon.vue';
import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue'; import UserAvatarLink from '../../vue_shared/components/user_avatar/user_avatar_link.vue';
import TimeagoTooltip from '../../vue_shared/components/time_ago_tooltip.vue'; import TimeagoTooltip from '../../vue_shared/components/time_ago_tooltip.vue';
import CommitPipelineStatus from '../../projects/tree/components/commit_pipeline_status_component.vue';
import CiIcon from '../../vue_shared/components/ci_icon.vue'; import CiIcon from '../../vue_shared/components/ci_icon.vue';
import ClipboardButton from '../../vue_shared/components/clipboard_button.vue'; import ClipboardButton from '../../vue_shared/components/clipboard_button.vue';
import getRefMixin from '../mixins/get_ref'; import getRefMixin from '../mixins/get_ref';
...@@ -16,11 +15,11 @@ export default { ...@@ -16,11 +15,11 @@ export default {
Icon, Icon,
UserAvatarLink, UserAvatarLink,
TimeagoTooltip, TimeagoTooltip,
CommitPipelineStatus,
ClipboardButton, ClipboardButton,
CiIcon, CiIcon,
GlLink, GlLink,
GlButton, GlButton,
GlLoadingIcon,
}, },
directives: { directives: {
GlTooltip: GlTooltipDirective, GlTooltip: GlTooltipDirective,
...@@ -39,7 +38,10 @@ export default { ...@@ -39,7 +38,10 @@ export default {
path: this.currentPath.replace(/^\//, ''), path: this.currentPath.replace(/^\//, ''),
}; };
}, },
update: data => data.project.repository.tree.commit, update: data => data.project.repository.tree.lastCommit,
context: {
isSingleRequest: true,
},
}, },
}, },
props: { props: {
...@@ -59,14 +61,14 @@ export default { ...@@ -59,14 +61,14 @@ export default {
computed: { computed: {
statusTitle() { statusTitle() {
return sprintf(s__('Commits|Commit: %{commitText}'), { return sprintf(s__('Commits|Commit: %{commitText}'), {
commitText: this.commit.pipeline.detailedStatus.text, commitText: this.commit.latestPipeline.detailedStatus.text,
}); });
}, },
isLoading() { isLoading() {
return this.$apollo.queries.commit.loading; return this.$apollo.queries.commit.loading;
}, },
showCommitId() { showCommitId() {
return this.commit.id.substr(0, 8); return this.commit.sha.substr(0, 8);
}, },
}, },
methods: { methods: {
...@@ -78,68 +80,75 @@ export default { ...@@ -78,68 +80,75 @@ export default {
</script> </script>
<template> <template>
<div v-if="!isLoading" class="info-well d-none d-sm-flex project-last-commit commit p-3"> <div class="info-well d-none d-sm-flex project-last-commit commit p-3">
<user-avatar-link <gl-loading-icon v-if="isLoading" size="md" class="mx-auto" />
v-if="commit.author" <template v-else>
:link-href="commit.author.webUrl" <user-avatar-link
:img-src="commit.author.avatarUrl" v-if="commit.author"
:img-size="40" :link-href="commit.author.webUrl"
class="avatar-cell" :img-src="commit.author.avatarUrl"
/> :img-size="40"
<div class="commit-detail flex-list"> class="avatar-cell"
<div class="commit-content qa-commit-content"> />
<gl-link :href="commit.webUrl" class="commit-row-message item-title"> <div class="commit-detail flex-list">
{{ commit.title }} <div class="commit-content qa-commit-content">
</gl-link> <gl-link :href="commit.webUrl" class="commit-row-message item-title">
<gl-button {{ commit.title }}
v-if="commit.description" </gl-link>
:class="{ open: showDescription }" <gl-button
:aria-label="__('Show commit description')" v-if="commit.description"
class="text-expander" :class="{ open: showDescription }"
@click="toggleShowDescription" :aria-label="__('Show commit description')"
> class="text-expander"
<icon name="ellipsis_h" /> @click="toggleShowDescription"
</gl-button> >
<div class="committer"> <icon name="ellipsis_h" />
</gl-button>
<div class="committer">
<gl-link
v-if="commit.author"
:href="commit.author.webUrl"
class="commit-author-link js-user-link"
>
{{ commit.author.name }}
</gl-link>
authored
<timeago-tooltip :time="commit.authoredDate" tooltip-placement="bottom" />
</div>
<pre
v-if="commit.description"
v-show="showDescription"
class="commit-row-description append-bottom-8"
>
{{ commit.description }}
</pre>
</div>
<div class="commit-actions flex-row">
<gl-link <gl-link
v-if="commit.author" v-if="commit.latestPipeline"
:href="commit.author.webUrl" v-gl-tooltip
class="commit-author-link js-user-link" :href="commit.latestPipeline.detailedStatus.detailsPath"
:title="statusTitle"
class="js-commit-pipeline"
> >
{{ commit.author.name }} <ci-icon
:status="commit.latestPipeline.detailedStatus"
:size="24"
:aria-label="statusTitle"
/>
</gl-link> </gl-link>
authored <div class="commit-sha-group d-flex">
<timeago-tooltip :time="commit.authoredDate" tooltip-placement="bottom" /> <div class="label label-monospace monospace">
</div> {{ showCommitId }}
<pre </div>
v-if="commit.description" <clipboard-button
v-show="showDescription" :text="commit.sha"
class="commit-row-description append-bottom-8" :title="__('Copy commit SHA to clipboard')"
> tooltip-placement="bottom"
{{ commit.description }} />
</pre>
</div>
<div class="commit-actions flex-row">
<gl-link
v-if="commit.pipeline"
v-gl-tooltip
:href="commit.pipeline.detailedStatus.detailsPath"
:title="statusTitle"
class="js-commit-pipeline"
>
<ci-icon :status="commit.pipeline.detailedStatus" :size="24" :aria-label="statusTitle" />
</gl-link>
<div class="commit-sha-group d-flex">
<div class="label label-monospace monospace">
{{ showCommitId }}
</div> </div>
<clipboard-button
:text="commit.id"
:title="__('Copy commit SHA to clipboard')"
tooltip-placement="bottom"
/>
</div> </div>
</div> </div>
</div> </template>
</div> </div>
</template> </template>
...@@ -50,23 +50,19 @@ export default function setupVueRepositoryList() { ...@@ -50,23 +50,19 @@ export default function setupVueRepositoryList() {
}, },
}); });
const commitEl = document.getElementById('js-last-commit'); // eslint-disable-next-line no-new
new Vue({
if (commitEl) { el: document.getElementById('js-last-commit'),
// eslint-disable-next-line no-new router,
new Vue({ apolloProvider,
el: commitEl, render(h) {
router, return h(LastCommit, {
apolloProvider, props: {
render(h) { currentPath: this.$route.params.pathMatch,
return h(LastCommit, { },
props: { });
currentPath: this.$route.params.pathMatch, },
}, });
});
},
});
}
return new Vue({ return new Vue({
el, el,
......
...@@ -2,8 +2,8 @@ query pathLastCommit($projectPath: ID!, $path: String, $ref: String!) { ...@@ -2,8 +2,8 @@ query pathLastCommit($projectPath: ID!, $path: String, $ref: String!) {
project(fullPath: $projectPath) { project(fullPath: $projectPath) {
repository { repository {
tree(path: $path, ref: $ref) { tree(path: $path, ref: $ref) {
commit { lastCommit {
id sha
title title
message message
webUrl webUrl
...@@ -13,7 +13,7 @@ query pathLastCommit($projectPath: ID!, $path: String, $ref: String!) { ...@@ -13,7 +13,7 @@ query pathLastCommit($projectPath: ID!, $path: String, $ref: String!) {
avatarUrl avatarUrl
webUrl webUrl
} }
pipeline { latestPipeline {
detailedStatus { detailedStatus {
detailsPath detailsPath
icon icon
......
...@@ -9,7 +9,9 @@ ...@@ -9,7 +9,9 @@
.nav-block .nav-block
= render 'projects/tree/tree_header', tree: @tree = render 'projects/tree/tree_header', tree: @tree
- if commit - if vue_file_list_enabled?
#js-last-commit
- elsif commit
= render 'shared/commit_well', commit: commit, ref: ref, project: project = render 'shared/commit_well', commit: commit, ref: ref, project: project
- if is_project_overview - if is_project_overview
......
---
title: Adds metrics to measure cost of expensive operations
merge_request: 29928
author:
type: other
# frozen_string_literal: true
Gitlab::Database.install_monkey_patches
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
module Gitlab module Gitlab
module Database module Database
include Gitlab::Metrics::Methods
# The max value of INTEGER type is the same between MySQL and PostgreSQL: # The max value of INTEGER type is the same between MySQL and PostgreSQL:
# https://www.postgresql.org/docs/9.2/static/datatype-numeric.html # https://www.postgresql.org/docs/9.2/static/datatype-numeric.html
# http://dev.mysql.com/doc/refman/5.7/en/integer-types.html # http://dev.mysql.com/doc/refman/5.7/en/integer-types.html
...@@ -11,6 +13,10 @@ module Gitlab ...@@ -11,6 +13,10 @@ module Gitlab
# https://dev.mysql.com/doc/refman/5.7/en/datetime.html # https://dev.mysql.com/doc/refman/5.7/en/datetime.html
MAX_TIMESTAMP_VALUE = Time.at((1 << 31) - 1).freeze MAX_TIMESTAMP_VALUE = Time.at((1 << 31) - 1).freeze
define_histogram :gitlab_database_transaction_seconds do
docstring "Time spent in database transactions, in seconds"
end
def self.config def self.config
ActiveRecord::Base.configurations[Rails.env] ActiveRecord::Base.configurations[Rails.env]
end end
...@@ -286,6 +292,33 @@ module Gitlab ...@@ -286,6 +292,33 @@ module Gitlab
0 0
end end
private_class_method :open_transactions_baseline private_class_method :open_transactions_baseline
# Monkeypatch rails with upgraded database observability
def self.install_monkey_patches
ActiveRecord::Base.prepend(ActiveRecordBaseTransactionMetrics)
end
# observe_transaction_duration is called from ActiveRecordBaseTransactionMetrics.transaction and used to
# record transaction durations.
def self.observe_transaction_duration(duration_seconds)
labels = Gitlab::Metrics::Transaction.current&.labels || {}
gitlab_database_transaction_seconds.observe(labels, duration_seconds)
rescue Prometheus::Client::LabelSetValidator::LabelSetError => err
# Ensure that errors in recording these metrics don't affect the operation of the application
Rails.logger.error("Unable to observe database transaction duration: #{err}")
end
# MonkeyPatch for ActiveRecord::Base for adding observability
module ActiveRecordBaseTransactionMetrics
# A monkeypatch over ActiveRecord::Base.transaction.
# It provides observability into transactional methods.
def transaction(options = {}, &block)
start_time = Gitlab::Metrics::System.monotonic_time
super(options, &block)
ensure
Gitlab::Database.observe_transaction_duration(Gitlab::Metrics::System.monotonic_time - start_time)
end
end
end end
end end
......
...@@ -5,6 +5,7 @@ module Gitlab ...@@ -5,6 +5,7 @@ module Gitlab
module_function module_function
def retry_lock(subject, retries = 100, &block) def retry_lock(subject, retries = 100, &block)
# TODO(Observability): We should be recording details of the number of retries and the duration of the total execution here
ActiveRecord::Base.transaction do ActiveRecord::Base.transaction do
yield(subject) yield(subject)
end end
......
...@@ -7,6 +7,8 @@ describe 'user reads pipeline status', :js do ...@@ -7,6 +7,8 @@ describe 'user reads pipeline status', :js do
let(:x110_pipeline) { create_pipeline('x1.1.0', 'failed') } let(:x110_pipeline) { create_pipeline('x1.1.0', 'failed') }
before do before do
stub_feature_flags(vue_file_list: false)
project.add_maintainer(user) project.add_maintainer(user)
project.repository.add_tag(user, 'x1.1.0', 'v1.1.0') project.repository.add_tag(user, 'x1.1.0', 'v1.1.0')
......
...@@ -3,6 +3,10 @@ require 'spec_helper' ...@@ -3,6 +3,10 @@ require 'spec_helper'
describe 'Projects > Show > User sees last commit CI status' do describe 'Projects > Show > User sees last commit CI status' do
set(:project) { create(:project, :repository, :public) } set(:project) { create(:project, :repository, :public) }
before do
stub_feature_flags(vue_file_list: false)
end
it 'shows the project README', :js do it 'shows the project README', :js do
project.enable_ci project.enable_ci
pipeline = create(:ci_pipeline, project: project, sha: project.commit.sha, ref: 'master') pipeline = create(:ci_pipeline, project: project, sha: project.commit.sha, ref: 'master')
......
...@@ -27,8 +27,8 @@ exports[`Repository last commit component renders commit widget 1`] = ` ...@@ -27,8 +27,8 @@ exports[`Repository last commit component renders commit widget 1`] = `
href="https://test.com/commit/123" href="https://test.com/commit/123"
> >
Commit title Commit title
</gllink-stub> </gllink-stub>
<!----> <!---->
...@@ -41,12 +41,12 @@ exports[`Repository last commit component renders commit widget 1`] = ` ...@@ -41,12 +41,12 @@ exports[`Repository last commit component renders commit widget 1`] = `
href="https://test.com/test" href="https://test.com/test"
> >
Test Test
</gllink-stub> </gllink-stub>
authored authored
<timeagotooltip-stub <timeagotooltip-stub
cssclass="" cssclass=""
time="2019-01-01" time="2019-01-01"
...@@ -81,8 +81,8 @@ exports[`Repository last commit component renders commit widget 1`] = ` ...@@ -81,8 +81,8 @@ exports[`Repository last commit component renders commit widget 1`] = `
class="label label-monospace monospace" class="label label-monospace monospace"
> >
12345678 12345678
</div> </div>
<clipboardbutton-stub <clipboardbutton-stub
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { GlLoadingIcon } from '@gitlab/ui';
import LastCommit from '~/repository/components/last_commit.vue'; import LastCommit from '~/repository/components/last_commit.vue';
import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue'; import UserAvatarLink from '~/vue_shared/components/user_avatar/user_avatar_link.vue';
...@@ -6,7 +7,7 @@ let vm; ...@@ -6,7 +7,7 @@ let vm;
function createCommitData(data = {}) { function createCommitData(data = {}) {
return { return {
id: '123456789', sha: '123456789',
title: 'Commit title', title: 'Commit title',
message: 'Commit message', message: 'Commit message',
webUrl: 'https://test.com/commit/123', webUrl: 'https://test.com/commit/123',
...@@ -16,7 +17,7 @@ function createCommitData(data = {}) { ...@@ -16,7 +17,7 @@ function createCommitData(data = {}) {
avatarUrl: 'https://test.com', avatarUrl: 'https://test.com',
webUrl: 'https://test.com/test', webUrl: 'https://test.com/test',
}, },
pipeline: { latestPipeline: {
detailedStatus: { detailedStatus: {
detailsPath: 'https://test.com/pipeline', detailsPath: 'https://test.com/pipeline',
icon: 'failed', icon: 'failed',
...@@ -52,12 +53,12 @@ describe('Repository last commit component', () => { ...@@ -52,12 +53,12 @@ describe('Repository last commit component', () => {
it.each` it.each`
loading | label loading | label
${true} | ${'hides'} ${true} | ${'shows'}
${false} | ${'shows'} ${false} | ${'hides'}
`('$label when $loading is true', ({ loading }) => { `('$label when loading icon $loading is true', ({ loading }) => {
factory(createCommitData(), loading); factory(createCommitData(), loading);
expect(vm.isEmpty()).toBe(loading); expect(vm.find(GlLoadingIcon).exists()).toBe(loading);
}); });
it('renders commit widget', () => { it('renders commit widget', () => {
...@@ -73,11 +74,17 @@ describe('Repository last commit component', () => { ...@@ -73,11 +74,17 @@ describe('Repository last commit component', () => {
}); });
it('hides pipeline components when pipeline does not exist', () => { it('hides pipeline components when pipeline does not exist', () => {
factory(createCommitData({ pipeline: null })); factory(createCommitData({ latestPipeline: null }));
expect(vm.find('.js-commit-pipeline').exists()).toBe(false); expect(vm.find('.js-commit-pipeline').exists()).toBe(false);
}); });
it('renders pipeline components', () => {
factory();
expect(vm.find('.js-commit-pipeline').exists()).toBe(true);
});
it('hides author component when author does not exist', () => { it('hides author component when author does not exist', () => {
factory(createCommitData({ author: null })); factory(createCommitData({ author: null }));
......
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