Skip to content

Commit b082491

Browse files
committed
[Obs AI Assistant][ES|QL] Add 10s request timeout to ES|QL query execution (#238200)
Closes #234135 ## Summary Add 10s request timeout to ES|QL query execution --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> (cherry picked from commit 0f4c777) # Conflicts: # x-pack/solutions/observability/plugins/observability_ai_assistant_app/server/functions/query/index.ts # x-pack/solutions/observability/plugins/observability_ai_assistant_app/tsconfig.json
1 parent db9f538 commit b082491

3 files changed

Lines changed: 91 additions & 4 deletions

File tree

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
3+
* or more contributor license agreements. Licensed under the Elastic License
4+
* 2.0; you may not use this file except in compliance with the Elastic License
5+
* 2.0.
6+
*/
7+
8+
import type { ElasticsearchClient, Logger } from '@kbn/core/server';
9+
import type { DeeplyMockedKeys } from '@kbn/utility-types-jest';
10+
import { ChatFunctionClient } from '@kbn/observability-ai-assistant-plugin/server/service/chat_function_client';
11+
import { registerExecuteQueryFunction } from '.';
12+
import { EXECUTE_QUERY_FUNCTION_NAME } from '@kbn/observability-ai-assistant-plugin/server';
13+
14+
describe('executeQuery function', () => {
15+
const currentUserEsClientMock: DeeplyMockedKeys<ElasticsearchClient> = {
16+
search: jest.fn(),
17+
fieldCaps: jest.fn(),
18+
esql: {
19+
query: () => Promise.resolve({ columns: [], values: [] }),
20+
},
21+
} as any;
22+
23+
const consoleOrPassThrough = () => {};
24+
25+
const loggerMock: DeeplyMockedKeys<Logger> = {
26+
log: jest.fn().mockImplementation(consoleOrPassThrough),
27+
error: jest.fn().mockImplementation(consoleOrPassThrough),
28+
debug: jest.fn().mockImplementation(consoleOrPassThrough),
29+
trace: jest.fn().mockImplementation(consoleOrPassThrough),
30+
isLevelEnabled: jest.fn().mockReturnValue(true),
31+
} as any;
32+
33+
let result: any;
34+
const timeoutError = new Error('Request timed out');
35+
beforeEach(async () => {
36+
jest.clearAllMocks();
37+
const functionClient = new ChatFunctionClient([]);
38+
39+
// mock the esql query to throw a timeout error
40+
currentUserEsClientMock.esql.query = jest.fn().mockImplementation(() => {
41+
const error = timeoutError;
42+
error.name = 'TimeoutError';
43+
throw error;
44+
});
45+
46+
registerExecuteQueryFunction({
47+
functions: functionClient,
48+
resources: {
49+
context: {
50+
// @ts-expect-error Type mismatch is expected in test mocks
51+
core: Promise.resolve({
52+
elasticsearch: { client: { asCurrentUser: currentUserEsClientMock } },
53+
}),
54+
},
55+
},
56+
signal: new AbortController().signal,
57+
});
58+
59+
result = await functionClient.executeFunction({
60+
chat: jest.fn(),
61+
name: EXECUTE_QUERY_FUNCTION_NAME,
62+
args: JSON.stringify({ query: 'FROM logs-* | LIMIT 10' }),
63+
messages: [],
64+
signal: new AbortController().signal,
65+
logger: loggerMock,
66+
connectorId: 'foo',
67+
simulateFunctionCalling: false,
68+
});
69+
});
70+
71+
it('return an error message when the execute_query function throws a timeout', () => {
72+
expect(result).toEqual({
73+
content: {
74+
message: 'The query failed to execute',
75+
error: timeoutError,
76+
errorMessages: undefined,
77+
},
78+
});
79+
});
80+
});

x-pack/solutions/observability/plugins/observability_ai_assistant_app/server/functions/query/index.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,11 @@ import { runAndValidateEsqlQuery } from './validate_esql_query';
2424
export const QUERY_FUNCTION_NAME = 'query';
2525
export const EXECUTE_QUERY_NAME = 'execute_query';
2626

27-
export function registerQueryFunction({
27+
export const registerExecuteQueryFunction = ({
2828
functions,
2929
resources,
30-
pluginsStart,
3130
signal,
32-
}: FunctionRegistrationParameters) {
31+
}: FunctionRegistrationParameters) => {
3332
functions.registerInstruction(({ availableFunctionNames }) => {
3433
if (!availableFunctionNames.includes(QUERY_FUNCTION_NAME)) {
3534
return;
@@ -102,6 +101,11 @@ export function registerQueryFunction({
102101
};
103102
}
104103
);
104+
};
105+
106+
export function registerQueryFunction(params: FunctionRegistrationParameters) {
107+
const { functions, resources, pluginsStart } = params;
108+
registerExecuteQueryFunction(params);
105109
functions.registerFunction(
106110
{
107111
name: QUERY_FUNCTION_NAME,

x-pack/solutions/observability/plugins/observability_ai_assistant_app/server/functions/query/validate_esql_query.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,10 @@ export async function runAndValidateEsqlQuery({
5050
});
5151

5252
try {
53-
const res = await client.esql.query({ query, drop_null_columns: true }, { signal });
53+
const res = await client.esql.query(
54+
{ query, drop_null_columns: true },
55+
{ signal, requestTimeout: '10s' }
56+
);
5457
const esqlResponse = res as unknown as ESQLSearchResponse;
5558

5659
const columns =

0 commit comments

Comments
 (0)