Only derive row count from max counter when job is running.

This commit is contained in:
Jake McDermott
2021-06-22 18:14:54 -04:00
committed by Shane McDonald
parent dc327ceaeb
commit 1211faf8df
2 changed files with 61 additions and 47 deletions

View File

@@ -434,28 +434,42 @@ function JobOutput({ job, eventRelatedSearchableKeys, eventSearchableKeys }) {
); );
} }
const eventPromise = getJobModel(job.type).readEvents(job.id, {
...params,
...parseQueryString(QS_CONFIG, location.search),
});
let countRequest;
if (isJobRunning(job?.status)) {
// If the job is running, it means we're using limit-offset pagination. Requests
// with limit-offset pagination won't return a total event count for performance
// reasons. In this situation, we derive the remote row count by using the highest
// counter available in the database.
countRequest = async () => {
const {
data: { results: lastEvents = [] },
} = await getJobModel(job.type).readEvents(job.id, {
order_by: '-counter',
limit: 1,
});
return lastEvents.length >= 1 ? lastEvents[0].counter : 0;
};
} else {
countRequest = async () => {
const {
data: { count: eventCount },
} = await eventPromise;
return eventCount;
};
}
try { try {
const [ const [
{ {
data: { results: fetchedEvents = [] }, data: { results: fetchedEvents = [] },
}, },
{ count,
data: { results: lastEvents = [] }, ] = await Promise.all([eventPromise, countRequest()]);
},
] = await Promise.all([
getJobModel(job.type).readEvents(job.id, {
...params,
...parseQueryString(QS_CONFIG, location.search),
}),
getJobModel(job.type).readEvents(job.id, {
order_by: '-counter',
limit: 1,
}),
]);
let count = 0;
if (lastEvents.length >= 1 && lastEvents[0]?.counter) {
count = lastEvents[0]?.counter;
}
if (isMounted.current) { if (isMounted.current) {
let countOffset = 0; let countOffset = 0;

View File

@@ -13,6 +13,22 @@ import mockFilteredJobEventsData from './data.filtered_job_events.json';
jest.mock('../../../api'); jest.mock('../../../api');
const applyJobEventMock = mockJobEvents => {
const mockReadEvents = async (jobId, params) => {
const [...results] = mockJobEvents.results;
if (params.order_by && params.order_by.includes('-')) {
results.reverse();
}
return {
data: {
results,
count: mockJobEvents.count,
},
};
};
JobsAPI.readEvents = jest.fn().mockImplementation(mockReadEvents);
};
const generateChattyRows = () => { const generateChattyRows = () => {
const rows = [ const rows = [
'', '',
@@ -82,24 +98,13 @@ const originalOffsetWidth = Object.getOwnPropertyDescriptor(
describe('<JobOutput />', () => { describe('<JobOutput />', () => {
let wrapper; let wrapper;
const mockJob = mockJobData; const mockJob = mockJobData;
const mockJobEvents = mockJobEventsData;
beforeEach(() => { beforeEach(() => {
JobsAPI.readEvents = (jobId, params) => { applyJobEventMock(mockJobEventsData);
const [...results] = mockJobEvents.results;
if (params.order_by && params.order_by.includes('-')) {
results.reverse();
}
return {
data: {
results,
},
};
};
}); });
afterEach(() => { afterEach(() => {
jest.clearAllMocks(); jest.clearAllMocks();
wrapper.unmount();
}); });
test('initially renders successfully', async () => { test('initially renders successfully', async () => {
@@ -141,7 +146,7 @@ describe('<JobOutput />', () => {
}); });
wrapper.update(); wrapper.update();
jobEvents = wrapper.find('JobEvent'); jobEvents = wrapper.find('JobEvent');
expect(jobEvents.at(jobEvents.length - 1).prop('stdout')).toBe( expect(jobEvents.at(jobEvents.length - 2).prop('stdout')).toBe(
'\r\nPLAY RECAP *********************************************************************\r\n\u001b[0;32mlocalhost\u001b[0m : \u001b[0;32mok=1 \u001b[0m changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 \r\n' '\r\nPLAY RECAP *********************************************************************\r\n\u001b[0;32mlocalhost\u001b[0m : \u001b[0;32mok=1 \u001b[0m changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 \r\n'
); );
await act(async () => { await act(async () => {
@@ -149,10 +154,10 @@ describe('<JobOutput />', () => {
}); });
wrapper.update(); wrapper.update();
jobEvents = wrapper.find('JobEvent'); jobEvents = wrapper.find('JobEvent');
expect(jobEvents.at(1).prop('stdout')).toBe( expect(jobEvents.at(0).prop('stdout')).toBe(
'\u001b[0;32mok: [localhost] => (item=76) => {\u001b[0m\r\n\u001b[0;32m "msg": "This is a debug message: 76"\u001b[0m\r\n\u001b[0;32m}\u001b[0m' '\u001b[0;32mok: [localhost] => (item=76) => {\u001b[0m\r\n\u001b[0;32m "msg": "This is a debug message: 76"\u001b[0m\r\n\u001b[0;32m}\u001b[0m'
); );
expect(jobEvents.at(2).prop('stdout')).toBe( expect(jobEvents.at(1).prop('stdout')).toBe(
'\u001b[0;32mok: [localhost] => (item=77) => {\u001b[0m\r\n\u001b[0;32m "msg": "This is a debug message: 77"\u001b[0m\r\n\u001b[0;32m}\u001b[0m' '\u001b[0;32mok: [localhost] => (item=77) => {\u001b[0m\r\n\u001b[0;32m "msg": "This is a debug message: 77"\u001b[0m\r\n\u001b[0;32m}\u001b[0m'
); );
await act(async () => { await act(async () => {
@@ -169,7 +174,7 @@ describe('<JobOutput />', () => {
}); });
wrapper.update(); wrapper.update();
jobEvents = wrapper.find('JobEvent'); jobEvents = wrapper.find('JobEvent');
expect(jobEvents.at(jobEvents.length - 1).prop('stdout')).toBe( expect(jobEvents.at(jobEvents.length - 2).prop('stdout')).toBe(
'\r\nPLAY RECAP *********************************************************************\r\n\u001b[0;32mlocalhost\u001b[0m : \u001b[0;32mok=1 \u001b[0m changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 \r\n' '\r\nPLAY RECAP *********************************************************************\r\n\u001b[0;32mlocalhost\u001b[0m : \u001b[0;32mok=1 \u001b[0m changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 \r\n'
); );
Object.defineProperty( Object.defineProperty(
@@ -266,11 +271,7 @@ describe('<JobOutput />', () => {
wrapper = mountWithContexts(<JobOutput job={mockJob} />); wrapper = mountWithContexts(<JobOutput job={mockJob} />);
}); });
await waitForElement(wrapper, 'JobEvent', el => el.length > 0); await waitForElement(wrapper, 'JobEvent', el => el.length > 0);
JobsAPI.readEvents = jest.fn(); applyJobEventMock(mockFilteredJobEventsData);
JobsAPI.readEvents.mockClear();
JobsAPI.readEvents.mockResolvedValueOnce({
data: mockFilteredJobEventsData,
});
await act(async () => { await act(async () => {
wrapper.find(searchTextInput).instance().value = '99'; wrapper.find(searchTextInput).instance().value = '99';
wrapper.find(searchTextInput).simulate('change'); wrapper.find(searchTextInput).simulate('change');
@@ -281,14 +282,13 @@ describe('<JobOutput />', () => {
}); });
wrapper.update(); wrapper.update();
expect(JobsAPI.readEvents).toHaveBeenCalled(); expect(JobsAPI.readEvents).toHaveBeenCalled();
// TODO: Fix these assertions const jobEvents = wrapper.find('JobEvent');
// const jobEvents = wrapper.find('JobEvent'); expect(jobEvents.at(0).prop('stdout')).toBe(
// expect(jobEvents.at(0).prop('stdout')).toBe( '\u001b[0;32mok: [localhost] => (item=99) => {\u001b[0m\r\n\u001b[0;32m "msg": "This is a debug message: 99"\u001b[0m\r\n\u001b[0;32m}\u001b[0m'
// '\u001b[0;32mok: [localhost] => (item=99) => {\u001b[0m\r\n\u001b[0;32m "msg": "This is a debug message: 99"\u001b[0m\r\n\u001b[0;32m}\u001b[0m' );
// ); expect(jobEvents.at(1).prop('stdout')).toBe(
// expect(jobEvents.at(1).prop('stdout')).toBe( '\u001b[0;32mok: [localhost] => (item=199) => {\u001b[0m\r\n\u001b[0;32m "msg": "This is a debug message: 199"\u001b[0m\r\n\u001b[0;32m}\u001b[0m'
// '\u001b[0;32mok: [localhost] => (item=199) => {\u001b[0m\r\n\u001b[0;32m "msg": "This is a debug message: 199"\u001b[0m\r\n\u001b[0;32m}\u001b[0m' );
// );
}); });
test('should throw error', async () => { test('should throw error', async () => {