From 5940e90217bee3495e22f68eaf3cf8cd47fa56e2 Mon Sep 17 00:00:00 2001 From: Phil Hughes <me@iamphill.com> Date: Thu, 12 Sep 2024 09:42:59 +0100 Subject: [PATCH] Improved perceived performance on the new merge request dashboard The database query to get the count for the number of merge requests in a list can take much longer than the query to get the merge requests. With this change we request the count separately so that we can load the merge requests quickly and display them without waiting for the count query to finish. --- .../components/merge_requests_query.vue | 31 ++++++++++++++++--- .../queries/assignee.query.graphql | 1 - .../queries/assignee_count.query.graphql | 20 ++++++++++++ .../assignee_or_reviewer.query.graphql | 1 - .../assignee_or_reviewer_count.query.graphql | 18 +++++++++++ .../queries/reviewer.query.graphql | 1 - .../queries/reviewer_count.query.graphql | 18 +++++++++++ .../components/app_spec.js | 19 ++++++++++-- .../components/merge_requests_query_spec.js | 28 +++++++++++++---- 9 files changed, 121 insertions(+), 16 deletions(-) create mode 100644 app/assets/javascripts/merge_request_dashboard/queries/assignee_count.query.graphql create mode 100644 app/assets/javascripts/merge_request_dashboard/queries/assignee_or_reviewer_count.query.graphql create mode 100644 app/assets/javascripts/merge_request_dashboard/queries/reviewer_count.query.graphql diff --git a/app/assets/javascripts/merge_request_dashboard/components/merge_requests_query.vue b/app/assets/javascripts/merge_request_dashboard/components/merge_requests_query.vue index 573b7afcf325..34761f9d0468 100644 --- a/app/assets/javascripts/merge_request_dashboard/components/merge_requests_query.vue +++ b/app/assets/javascripts/merge_request_dashboard/components/merge_requests_query.vue @@ -1,21 +1,27 @@ <script> import reviewerQuery from '../queries/reviewer.query.graphql'; +import reviewerCountQuery from '../queries/reviewer_count.query.graphql'; import assigneeQuery from '../queries/assignee.query.graphql'; +import assigneeCountQuery from '../queries/assignee_count.query.graphql'; import assigneeOrReviewerQuery from '../queries/assignee_or_reviewer.query.graphql'; +import assigneeOrReviewerCountQuery from '../queries/assignee_or_reviewer_count.query.graphql'; const PER_PAGE = 20; const QUERIES = { - reviewRequestedMergeRequests: reviewerQuery, - assignedMergeRequests: assigneeQuery, - assigneeOrReviewerMergeRequests: assigneeOrReviewerQuery, + reviewRequestedMergeRequests: { dataQuery: reviewerQuery, countQuery: reviewerCountQuery }, + assignedMergeRequests: { dataQuery: assigneeQuery, countQuery: assigneeCountQuery }, + assigneeOrReviewerMergeRequests: { + dataQuery: assigneeOrReviewerQuery, + countQuery: assigneeOrReviewerCountQuery, + }, }; export default { apollo: { mergeRequests: { query() { - return QUERIES[this.query]; + return QUERIES[this.query].dataQuery; }, update(d) { return d.currentUser?.mergeRequests || {}; @@ -30,6 +36,20 @@ export default { this.error = true; }, }, + count: { + query() { + return QUERIES[this.query].countQuery; + }, + update(d) { + return d.currentUser?.mergeRequests?.count; + }, + variables() { + return { + ...this.variables, + perPage: PER_PAGE, + }; + }, + }, }, props: { query: { @@ -44,6 +64,7 @@ export default { data() { return { mergeRequests: null, + count: null, error: false, }; }, @@ -69,7 +90,7 @@ export default { render() { return this.$scopedSlots.default({ mergeRequests: this.mergeRequests?.nodes || [], - count: this.mergeRequests ? this.mergeRequests.count : null, + count: this.count, hasNextPage: this.hasNextPage, loadMore: this.loadMore, loading: this.isLoading, diff --git a/app/assets/javascripts/merge_request_dashboard/queries/assignee.query.graphql b/app/assets/javascripts/merge_request_dashboard/queries/assignee.query.graphql index 25dc21a07b27..e7361721e649 100644 --- a/app/assets/javascripts/merge_request_dashboard/queries/assignee.query.graphql +++ b/app/assets/javascripts/merge_request_dashboard/queries/assignee.query.graphql @@ -22,7 +22,6 @@ query requestingReview( after: $afterCursor sort: UPDATED_DESC ) { - count pageInfo { ...PageInfo } diff --git a/app/assets/javascripts/merge_request_dashboard/queries/assignee_count.query.graphql b/app/assets/javascripts/merge_request_dashboard/queries/assignee_count.query.graphql new file mode 100644 index 000000000000..d533a157a486 --- /dev/null +++ b/app/assets/javascripts/merge_request_dashboard/queries/assignee_count.query.graphql @@ -0,0 +1,20 @@ +query requestingReviewCount( + $state: MergeRequestState = opened + $reviewState: MergeRequestReviewState + $reviewStates: [MergeRequestReviewState!] + $reviewerWildcardId: ReviewerWildcardId + $mergedAfter: Time +) { + currentUser { + id + mergeRequests: assignedMergeRequests( + state: $state + reviewState: $reviewState + reviewStates: $reviewStates + reviewerWildcardId: $reviewerWildcardId + mergedAfter: $mergedAfter + ) { + count + } + } +} diff --git a/app/assets/javascripts/merge_request_dashboard/queries/assignee_or_reviewer.query.graphql b/app/assets/javascripts/merge_request_dashboard/queries/assignee_or_reviewer.query.graphql index 5a743656a194..a0b65e70867d 100644 --- a/app/assets/javascripts/merge_request_dashboard/queries/assignee_or_reviewer.query.graphql +++ b/app/assets/javascripts/merge_request_dashboard/queries/assignee_or_reviewer.query.graphql @@ -20,7 +20,6 @@ query assigneeOrReviewer( after: $afterCursor sort: UPDATED_DESC ) { - count pageInfo { ...PageInfo } diff --git a/app/assets/javascripts/merge_request_dashboard/queries/assignee_or_reviewer_count.query.graphql b/app/assets/javascripts/merge_request_dashboard/queries/assignee_or_reviewer_count.query.graphql new file mode 100644 index 000000000000..c27f0a159fb4 --- /dev/null +++ b/app/assets/javascripts/merge_request_dashboard/queries/assignee_or_reviewer_count.query.graphql @@ -0,0 +1,18 @@ +query assigneeOrReviewerCount( + $state: MergeRequestState = opened + $assignedReviewStates: [MergeRequestReviewState!] + $reviewerReviewStates: [MergeRequestReviewState!] + $mergedAfter: Time +) { + currentUser { + id + mergeRequests: assigneeOrReviewerMergeRequests( + state: $state + assignedReviewStates: $assignedReviewStates + reviewerReviewStates: $reviewerReviewStates + mergedAfter: $mergedAfter + ) { + count + } + } +} diff --git a/app/assets/javascripts/merge_request_dashboard/queries/reviewer.query.graphql b/app/assets/javascripts/merge_request_dashboard/queries/reviewer.query.graphql index fbdfcb46c06f..e0c74ffed5ac 100644 --- a/app/assets/javascripts/merge_request_dashboard/queries/reviewer.query.graphql +++ b/app/assets/javascripts/merge_request_dashboard/queries/reviewer.query.graphql @@ -20,7 +20,6 @@ query reviewRequests( after: $afterCursor sort: UPDATED_DESC ) { - count pageInfo { ...PageInfo } diff --git a/app/assets/javascripts/merge_request_dashboard/queries/reviewer_count.query.graphql b/app/assets/javascripts/merge_request_dashboard/queries/reviewer_count.query.graphql new file mode 100644 index 000000000000..c7232688c8dd --- /dev/null +++ b/app/assets/javascripts/merge_request_dashboard/queries/reviewer_count.query.graphql @@ -0,0 +1,18 @@ +query reviewRequestsCount( + $state: MergeRequestState = opened + $reviewState: MergeRequestReviewState + $reviewStates: [MergeRequestReviewState!] + $mergedAfter: Time +) { + currentUser { + id + mergeRequests: reviewRequestedMergeRequests( + state: $state + reviewState: $reviewState + reviewStates: $reviewStates + mergedAfter: $mergedAfter + ) { + count + } + } +} diff --git a/spec/frontend/merge_request_dashboard/components/app_spec.js b/spec/frontend/merge_request_dashboard/components/app_spec.js index cbd98607e26e..81e0b97363a4 100644 --- a/spec/frontend/merge_request_dashboard/components/app_spec.js +++ b/spec/frontend/merge_request_dashboard/components/app_spec.js @@ -8,6 +8,7 @@ import MergeRequestsQuery from '~/merge_request_dashboard/components/merge_reque import CollapsibleSection from '~/merge_request_dashboard/components/collapsible_section.vue'; import MergeRequest from '~/merge_request_dashboard/components/merge_request.vue'; import assigneeQuery from '~/merge_request_dashboard/queries/assignee.query.graphql'; +import assigneeCountQuery from '~/merge_request_dashboard/queries/assignee_count.query.graphql'; import { createMockMergeRequest } from '../mock_data'; Vue.use(VueApollo); @@ -27,7 +28,6 @@ describe('Merge requests app component', () => { currentUser: { id: 1, mergeRequests: { - count: 1, pageInfo: { hasNextPage: true, hasPreviousPage: false, @@ -41,7 +41,22 @@ describe('Merge requests app component', () => { }, }); const apolloProvider = createMockApollo( - [[assigneeQuery, assigneeQueryMock]], + [ + [assigneeQuery, assigneeQueryMock], + [ + assigneeCountQuery, + jest.fn().mockResolvedValue({ + data: { + currentUser: { + id: 1, + mergeRequests: { + count: 1, + }, + }, + }, + }), + ], + ], {}, { typePolicies: { Query: { fields: { currentUser: { merge: false } } } } }, ); diff --git a/spec/frontend/merge_request_dashboard/components/merge_requests_query_spec.js b/spec/frontend/merge_request_dashboard/components/merge_requests_query_spec.js index 4b1fc388c9b8..99cf15c0bab2 100644 --- a/spec/frontend/merge_request_dashboard/components/merge_requests_query_spec.js +++ b/spec/frontend/merge_request_dashboard/components/merge_requests_query_spec.js @@ -5,7 +5,9 @@ import createMockApollo from 'helpers/mock_apollo_helper'; import waitForPromises from 'helpers/wait_for_promises'; import MergeRequestQuery from '~/merge_request_dashboard/components/merge_requests_query.vue'; import reviewerQuery from '~/merge_request_dashboard/queries/reviewer.query.graphql'; +import reviewerCountQuery from '~/merge_request_dashboard/queries/reviewer_count.query.graphql'; import assigneeQuery from '~/merge_request_dashboard/queries/assignee.query.graphql'; +import assigneeCountQuery from '~/merge_request_dashboard/queries/assignee_count.query.graphql'; import { createMockMergeRequest } from '../mock_data'; Vue.use(VueApollo); @@ -23,7 +25,6 @@ describe('Merge requests query component', () => { currentUser: { id: 1, mergeRequests: { - count: 0, pageInfo: { __typename: 'PageInfo', hasNextPage: false, @@ -41,7 +42,6 @@ describe('Merge requests query component', () => { currentUser: { id: 1, mergeRequests: { - count: 0, pageInfo: { hasNextPage: false, hasPreviousPage: false, @@ -54,10 +54,26 @@ describe('Merge requests query component', () => { }, }, }); - const apolloProvider = createMockApollo([ - [reviewerQuery, reviewerQueryMock], - [assigneeQuery, assigneeQueryMock], - ]); + const apolloProvider = createMockApollo( + [ + [reviewerQuery, reviewerQueryMock], + [assigneeQuery, assigneeQueryMock], + [ + reviewerCountQuery, + jest + .fn() + .mockResolvedValue({ data: { currentUser: { id: 1, mergeRequests: { count: 1 } } } }), + ], + [ + assigneeCountQuery, + jest + .fn() + .mockResolvedValue({ data: { currentUser: { id: 1, mergeRequests: { count: 1 } } } }), + ], + ], + {}, + { typePolicies: { Query: { fields: { currentUser: { merge: false } } } } }, + ); slotSpy = jest.fn(); -- GitLab