Improve on-demand scans polling performances

This reduces the GraphQL polling overhead in the on-demand scans page
by...
* ...including an ETag header in the query to prevent hitting the
  database on every request.
* ...putting the query behind a visibility check to pause the polling
  when the browser tab is not visible.
* ...increasing the polling interval from 1 to 3 seconds.

Changelog: performance
EE: true
parent 802314d3
......@@ -14,6 +14,10 @@ import TimeAgoTooltip from '~/vue_shared/components/time_ago_tooltip.vue';
import { DAST_SHORT_NAME } from '~/security_configuration/components/constants';
import { __, s__ } from '~/locale';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import {
getQueryHeaders,
toggleQueryPollingByVisibility,
} from '~/pipelines/components/graph/utils';
import handlesErrors from '../../mixins/handles_errors';
import Actions from '../actions.vue';
import EmptyState from '../empty_state.vue';
......@@ -45,7 +49,7 @@ export default {
EmptyState,
},
mixins: [handlesErrors],
inject: ['projectPath'],
inject: ['projectPath', 'projectOnDemandScanCountsEtag'],
props: {
isActive: {
type: Boolean,
......@@ -95,6 +99,9 @@ export default {
...this.cursor,
};
},
context() {
return getQueryHeaders(this.projectOnDemandScanCountsEtag);
},
update(data) {
const pipelines = data?.project?.pipelines;
if (!pipelines?.nodes?.length && (this.cursor.after || this.cursor.before)) {
......@@ -160,6 +167,9 @@ export default {
}
},
},
mounted() {
toggleQueryPollingByVisibility(this.$apollo.queries.pipelines, PIPELINES_POLL_INTERVAL);
},
methods: {
resetCursor() {
this.cursor = { ...defaultCursor };
......
......@@ -11,8 +11,8 @@ export const LEARN_MORE_TEXT = s__(
export const PIPELINE_TABS_KEYS = ['all', 'running', 'finished', 'scheduled', 'saved'];
export const PIPELINES_PER_PAGE = 20;
export const PIPELINES_POLL_INTERVAL = 1000;
export const PIPELINES_COUNT_POLL_INTERVAL = 1000;
export const PIPELINES_POLL_INTERVAL = 3000;
export const PIPELINES_COUNT_POLL_INTERVAL = 3000;
// Pipeline scopes
export const PIPELINES_SCOPE_RUNNING = 'RUNNING';
......
......@@ -18,6 +18,8 @@ import { scrollToElement } from '~/lib/utils/common_utils';
import setWindowLocation from 'helpers/set_window_location_helper';
import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { BASE_TABS_TABLE_FIELDS, PIPELINES_POLL_INTERVAL } from 'ee/on_demand_scans/constants';
import * as graphQlUtils from '~/pipelines/components/graph/utils';
import { PROJECT_ON_DEMAND_SCAN_COUNTS_ETAG_MOCK } from '../../mocks';
jest.mock('~/lib/utils/common_utils');
......@@ -86,6 +88,7 @@ describe('BaseTab', () => {
},
provide: {
projectPath,
projectOnDemandScanCountsEtag: PROJECT_ON_DEMAND_SCAN_COUNTS_ETAG_MOCK,
},
stubs: {
GlTab: stubComponent(GlTab, {
......@@ -122,6 +125,16 @@ describe('BaseTab', () => {
});
describe('when the app loads', () => {
it('controls the pipelines query with a visibility check', () => {
jest.spyOn(graphQlUtils, 'toggleQueryPollingByVisibility');
createComponent();
expect(graphQlUtils.toggleQueryPollingByVisibility).toHaveBeenCalledWith(
wrapper.vm.$apollo.queries.pipelines,
PIPELINES_POLL_INTERVAL,
);
});
it('fetches the pipelines', () => {
createComponent();
......@@ -134,6 +147,15 @@ describe('BaseTab', () => {
});
});
it('computes the ETag header', () => {
jest.spyOn(graphQlUtils, 'getQueryHeaders');
createComponent();
expect(graphQlUtils.getQueryHeaders).toHaveBeenCalledWith(
PROJECT_ON_DEMAND_SCAN_COUNTS_ETAG_MOCK,
);
});
it('polls for pipelines as long as the tab is active', async () => {
createComponent();
......
......@@ -15,6 +15,7 @@ import { s__ } from '~/locale';
import ScanTypeBadge from 'ee/security_configuration/dast_profiles/components/dast_scan_type_badge.vue';
import flushPromises from 'helpers/flush_promises';
import { redirectTo } from '~/lib/utils/url_utility';
import { PROJECT_ON_DEMAND_SCAN_COUNTS_ETAG_MOCK } from '../../mocks';
Vue.use(VueApollo);
......@@ -86,6 +87,7 @@ describe('Saved tab', () => {
},
provide: {
projectPath,
projectOnDemandScanCountsEtag: PROJECT_ON_DEMAND_SCAN_COUNTS_ETAG_MOCK,
},
stubs: {
BaseTab,
......
......@@ -13,6 +13,7 @@ import { SCHEDULED_TAB_TABLE_FIELDS, LEARN_MORE_TEXT } from 'ee/on_demand_scans/
import { __, s__ } from '~/locale';
import { stripTimezoneFromISODate } from '~/lib/utils/datetime/date_format_utility';
import DastScanSchedule from 'ee/security_configuration/dast_profiles/components/dast_scan_schedule.vue';
import { PROJECT_ON_DEMAND_SCAN_COUNTS_ETAG_MOCK } from '../../mocks';
jest.mock('~/lib/utils/common_utils');
......@@ -51,6 +52,7 @@ describe('Scheduled tab', () => {
},
provide: {
projectPath,
projectOnDemandScanCountsEtag: PROJECT_ON_DEMAND_SCAN_COUNTS_ETAG_MOCK,
timezones: mockTimezones,
},
stubs: {
......
export const PROJECT_ON_DEMAND_SCAN_COUNTS_ETAG_MOCK = `/api/graphql:on_demand_scan/counts/namespace/project`;
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