Skip to content

Commit 7dd46a3

Browse files
committed
optimize ES|QL polling
1 parent fa549d3 commit 7dd46a3

File tree

2 files changed

+207
-7
lines changed

2 files changed

+207
-7
lines changed

src/platform/plugins/shared/data/public/search/search_interceptor/search_interceptor.test.ts

Lines changed: 202 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import * as searchPhaseException from '../../../common/search/test_data/search_p
2222
import * as resourceNotFoundException from '../../../common/search/test_data/resource_not_found_exception.json';
2323
import { BehaviorSubject } from 'rxjs';
2424
import { dataPluginMock } from '../../mocks';
25-
import { UI_SETTINGS } from '../../../common';
25+
import { ESQL_ASYNC_SEARCH_STRATEGY, UI_SETTINGS } from '../../../common';
2626
import type { SearchServiceStartDependencies } from '../search_service';
2727
import type { Start as InspectorStart } from '@kbn/inspector-plugin/public';
2828
import { SearchTimeoutError, TimeoutErrorMode } from './timeout_error';
@@ -292,7 +292,207 @@ describe('SearchInterceptor', () => {
292292
expect(error).not.toHaveBeenCalled();
293293
});
294294

295-
test('should make secondary request if first call returns partial result', async () => {
295+
test('should make secondary request if first call returns partial result (ES|QL)', async () => {
296+
const responses = [
297+
{
298+
time: 10,
299+
value: {
300+
body: {
301+
id: '1',
302+
is_running: true,
303+
documents_found: 0,
304+
values_loaded: 0,
305+
all_columns: [],
306+
columns: [],
307+
values: [],
308+
_clusters: {},
309+
},
310+
},
311+
},
312+
{
313+
time: 20,
314+
value: {
315+
body: {
316+
id: '1',
317+
is_running: false,
318+
took: 8,
319+
is_partial: false,
320+
documents_found: 5,
321+
values_loaded: 5,
322+
all_columns: [
323+
{
324+
name: 'results',
325+
type: 'long',
326+
},
327+
{
328+
name: 'timestamp',
329+
type: 'date',
330+
},
331+
],
332+
columns: [
333+
{
334+
name: 'results',
335+
type: 'long',
336+
},
337+
{
338+
name: 'timestamp',
339+
type: 'date',
340+
},
341+
],
342+
values: [
343+
[1, '2025-11-17T11:00:00.000Z'],
344+
[1, '2025-11-17T09:30:00.000Z'],
345+
[1, '2025-11-17T12:00:00.000Z'],
346+
[1, '2025-11-17T11:30:00.000Z'],
347+
[1, '2025-11-17T16:30:00.000Z'],
348+
],
349+
},
350+
},
351+
},
352+
];
353+
354+
mockCoreSetup.http.post.mockImplementation(getHttpMock(responses));
355+
356+
const response = searchInterceptor.search(
357+
{
358+
params: {
359+
query:
360+
'FROM kibana_sample_data_logs | LIMIT 5 |EVAL DELAY(1s)\n| STATS results = COUNT(*) BY timestamp = BUCKET(@timestamp, 30 minute)',
361+
locale: 'en',
362+
include_execution_metadata: true,
363+
filter: {
364+
bool: {
365+
must: [],
366+
filter: [
367+
{
368+
range: {
369+
'@timestamp': {
370+
format: 'strict_date_optional_time',
371+
gte: '2025-11-17T07:00:00.000Z',
372+
lte: '2025-11-18T06:59:59.999Z',
373+
},
374+
},
375+
},
376+
],
377+
should: [],
378+
must_not: [],
379+
},
380+
},
381+
dropNullColumns: true,
382+
},
383+
},
384+
{ pollInterval: 0, strategy: ESQL_ASYNC_SEARCH_STRATEGY }
385+
);
386+
response.subscribe({ next, error, complete });
387+
388+
await timeTravel(10);
389+
390+
expect(next).toHaveBeenCalled();
391+
expect(next.mock.calls[0][0]).toMatchInlineSnapshot(`
392+
Object {
393+
"id": "1",
394+
"isPartial": undefined,
395+
"isRestored": false,
396+
"isRunning": true,
397+
"rawResponse": Object {
398+
"_clusters": Object {},
399+
"all_columns": Array [],
400+
"columns": Array [],
401+
"documents_found": 0,
402+
"id": "1",
403+
"is_running": true,
404+
"values": Array [],
405+
"values_loaded": 0,
406+
},
407+
"requestParams": Object {},
408+
"warning": undefined,
409+
}
410+
`);
411+
expect(complete).not.toHaveBeenCalled();
412+
expect(error).not.toHaveBeenCalled();
413+
414+
await timeTravel(20);
415+
416+
expect(next).toHaveBeenCalledTimes(2);
417+
expect(next.mock.calls[1][0]).toMatchInlineSnapshot(`
418+
Object {
419+
"id": "1",
420+
"isPartial": false,
421+
"isRestored": false,
422+
"isRunning": false,
423+
"rawResponse": Object {
424+
"all_columns": Array [
425+
Object {
426+
"name": "results",
427+
"type": "long",
428+
},
429+
Object {
430+
"name": "timestamp",
431+
"type": "date",
432+
},
433+
],
434+
"columns": Array [
435+
Object {
436+
"name": "results",
437+
"type": "long",
438+
},
439+
Object {
440+
"name": "timestamp",
441+
"type": "date",
442+
},
443+
],
444+
"documents_found": 5,
445+
"id": "1",
446+
"is_partial": false,
447+
"is_running": false,
448+
"took": 8,
449+
"values": Array [
450+
Array [
451+
1,
452+
"2025-11-17T11:00:00.000Z",
453+
],
454+
Array [
455+
1,
456+
"2025-11-17T09:30:00.000Z",
457+
],
458+
Array [
459+
1,
460+
"2025-11-17T12:00:00.000Z",
461+
],
462+
Array [
463+
1,
464+
"2025-11-17T11:30:00.000Z",
465+
],
466+
Array [
467+
1,
468+
"2025-11-17T16:30:00.000Z",
469+
],
470+
],
471+
"values_loaded": 5,
472+
},
473+
"requestParams": Object {},
474+
"warning": undefined,
475+
}
476+
`);
477+
expect(complete).toHaveBeenCalled();
478+
expect(error).not.toHaveBeenCalled();
479+
480+
// check that the query and filter weren't included in the polling request
481+
expect(mockCoreSetup.http.post).toHaveBeenCalledTimes(2);
482+
const firstRequest = (
483+
mockCoreSetup.http.post.mock.calls[0] as unknown as [string, HttpFetchOptions]
484+
)[1];
485+
expect(JSON.parse(firstRequest?.body as string).params.query).toBeDefined();
486+
expect(JSON.parse(firstRequest?.body as string).params.filter).toBeDefined();
487+
488+
const secondRequest = (
489+
mockCoreSetup.http.post.mock.calls[1] as unknown as [string, HttpFetchOptions]
490+
)[1];
491+
expect(JSON.parse(secondRequest?.body as string).params.query).not.toBeDefined();
492+
expect(JSON.parse(secondRequest?.body as string).params.filter).not.toBeDefined();
493+
});
494+
495+
test('should make secondary request if first call returns partial result (DSL)', async () => {
296496
const responses = [
297497
{
298498
time: 10,

src/platform/plugins/shared/data/public/search/search_interceptor/search_interceptor.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -710,16 +710,16 @@ const optimizeParamsForPoll = (
710710
request: IKibanaSearchRequest,
711711
strategy: string | undefined
712712
): IKibanaSearchRequest => {
713-
let optimizedParams = request.params;
713+
if (!request.params) return request;
714714

715-
if (strategy === ENHANCED_ES_SEARCH_STRATEGY) {
716-
const { body, ...paramsWithoutBody } = request.params;
717-
optimizedParams = paramsWithoutBody;
718-
}
715+
let optimizedParams = request.params;
719716

720717
if (strategy === ESQL_ASYNC_SEARCH_STRATEGY) {
721718
const { query, filter: _filter, ...paramsWithoutQueryAndFilter } = request.params;
722719
optimizedParams = paramsWithoutQueryAndFilter;
720+
} else {
721+
const { body, ...paramsWithoutBody } = request.params;
722+
optimizedParams = paramsWithoutBody;
723723
}
724724

725725
return {

0 commit comments

Comments
 (0)