Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Search Sessions] Save all sessions, with persisted flag #89570

Merged
merged 84 commits into from
Feb 3, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
84 commits
Select commit Hold shift + click to select a range
c22024f
[data.search] Add search session methods to search service contract
lukasolson Jan 12, 2021
1046c54
Merge branch 'master' into search-session-api
lukasolson Jan 15, 2021
7040716
Fix types
lukasolson Jan 15, 2021
eeddf84
Fix tests and switch to cancel
lukasolson Jan 15, 2021
f09eb38
Update docs
lukasolson Jan 15, 2021
e6124af
Merge branch 'master' into search-session-api
lukasolson Jan 19, 2021
e160019
Fix types/tests
lukasolson Jan 19, 2021
3c08c3d
Fix tests
lukasolson Jan 19, 2021
0cd92ed
Update status of SO before cancelling search requests
lukasolson Jan 19, 2021
f1a1d0b
Add API integration test
lukasolson Jan 19, 2021
552e545
Merge branch 'master' into search-session-api
lukasolson Jan 20, 2021
ee6e337
Merge branch 'master' into search-session-api
lukasolson Jan 25, 2021
32315b7
Fix types
lukasolson Jan 25, 2021
542de95
Update expiration route to use config defaultExpiration
lukasolson Jan 25, 2021
5b22249
Fix test
lukasolson Jan 25, 2021
66420b4
Update docs
lukasolson Jan 25, 2021
8a5e3d3
New logic for extend
lukasolson Jan 26, 2021
71582c7
Remove declare module
lukasolson Jan 26, 2021
03f413a
Search Sessions: Unskip Flaky Functional Test
tsullivan Jan 26, 2021
65840b3
Review feedback
lukasolson Jan 26, 2021
fdb940b
Merge branch 'master' of github.com:elastic/kibana into pr/87966
Jan 27, 2021
b2bce4b
fix ts
Jan 27, 2021
2e09f56
Merge branch 'master' into flaky/search-sessions
tsullivan Jan 28, 2021
cb1de74
Save all search sessions and then manage them based on their persiste…
Jan 28, 2021
2b3b90e
Get default search session expiration from config
Jan 28, 2021
cd23cef
randomize sleep time
Jan 28, 2021
5f21181
fix test
Jan 28, 2021
50081b5
Merge branch 'master' into search-session-api
lukasolson Jan 28, 2021
e24181c
Remove test that is no longer valid
lukasolson Jan 28, 2021
08ed572
Merge remote-tracking branch 'origin/search-session-api' into search-…
lukasolson Jan 28, 2021
cdcf562
fix test
Jan 28, 2021
bdbc748
Merge branch 'master' into sessions/save-all-sessions
kibanamachine Jan 28, 2021
667c9d3
Make sure we poll, and dont persist, searches not in the context of a…
Jan 28, 2021
867f1f4
Merge branch 'sessions/save-all-sessions' of github.com:lizozom/kiban…
Jan 28, 2021
51545ae
Merge branch 'master' of github.com:elastic/kibana into sessions/save…
Jan 28, 2021
763746b
Added keepalive unit tests
Jan 28, 2021
44ef260
Merge branch 'master' into search-session-api
lukasolson Jan 28, 2021
bbf85c5
Merge branch 'master' into sessions/save-all-sessions
kibanamachine Jan 29, 2021
01f292d
fix ts
Dosant Jan 29, 2021
7a85729
Merge branch 'master' into flaky/search-sessions
kibanamachine Jan 29, 2021
46cdf7f
Merge branch 'master' of github.com:elastic/kibana into sessions/save…
Jan 31, 2021
393d800
Merge branch 'sessions/save-all-sessions' of github.com:lizozom/kiban…
Jan 31, 2021
2d75b96
code review @lukasolson
Jan 31, 2021
13691e6
Merge branch 'master' of github.com:elastic/kibana into sessions/save…
Jan 31, 2021
1cd21fd
ts
Jan 31, 2021
a9cc263
More tests, rename onScreenTimeout to completedTimeout
Jan 31, 2021
bfcbdb2
lint
Jan 31, 2021
9b914f6
lint
Jan 31, 2021
ddb86a7
Merge branch 'master' into flaky/search-sessions
kibanamachine Jan 31, 2021
7ab767d
Delete async seaches
Jan 31, 2021
d924604
Support saved object pagination
Feb 1, 2021
11f429e
Merge branch 'master' of github.com:elastic/kibana into sessions/save…
Feb 1, 2021
48a90fb
better PersistedSearchSessionSavedObjectAttributes ts
Feb 1, 2021
de99c73
test titles
Feb 1, 2021
1bad580
Merge branch 'master' of github.com:elastic/kibana into sessions/save…
Feb 1, 2021
53a820e
Merge branch 'master' into sessions/save-all-sessions
kibanamachine Feb 1, 2021
ab0ce8f
Merge branch 'master' into sessions/save-all-sessions
kibanamachine Feb 1, 2021
99b4ca0
Merge branch 'master' into search-session-api
lukasolson Feb 1, 2021
99dc347
Fix undefined bug
lukasolson Feb 1, 2021
89d0887
Merge branch 'master' into sessions/save-all-sessions
kibanamachine Feb 2, 2021
eb735ef
Merge branch 'master' of github.com:elastic/kibana into sessions/save…
Feb 2, 2021
f305245
Remove runAt from monitoring task
Feb 2, 2021
68c163d
Merge branch 'sessions/save-all-sessions' of github.com:lizozom/kiban…
Feb 2, 2021
d92996e
support workload histograms that take into account overdue tasks
gmmorris Feb 2, 2021
0587c27
Merge branch 'sessions/save-all-sessions' of https://github.com/lizoz…
gmmorris Feb 2, 2021
353f722
Update touched when changing session status to complete \ error
Feb 2, 2021
899d547
Merge branch 'sessions/save-all-sessions' of github.com:lizozom/kiban…
Feb 2, 2021
6bd93ac
removed test
Feb 2, 2021
a6491dd
Updated management test data
Feb 2, 2021
fc29c7f
Rename configs
Feb 2, 2021
d61fa45
Merge branch 'sessions/save-all-sessions' into pr/89370
Feb 2, 2021
e582910
Merge branch 'master' into search-session-api
lukasolson Feb 2, 2021
fbfc1b1
Fix types
lukasolson Feb 2, 2021
2bd7e2b
delete tap first
Feb 3, 2021
cf45bb3
Merge branch 'master' of github.com:elastic/kibana into sessions/save…
Feb 3, 2021
19fa7e1
Merge branch 'sessions/save-all-sessions' of github.com:lizozom/kiban…
Feb 3, 2021
5c564c2
Merge branch 'master' of github.com:elastic/kibana into pr/87966
Feb 3, 2021
2b5c2aa
Merge branch 'pr/87966' into sessions/save-all-sessions
Feb 3, 2021
3eb8f01
Use DataRequestHandlerContext in maps
Feb 3, 2021
6462da6
ts
Feb 3, 2021
4b89dbf
Merge branch 'pr/87966' into sessions/save-all-sessions
Feb 3, 2021
c890574
Merge branch 'master' of github.com:elastic/kibana into sessions/save…
Feb 3, 2021
592c81b
Fixed ts
Feb 3, 2021
07523f3
Merge branch 'master' of github.com:elastic/kibana into sessions/save…
Feb 3, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/plugins/data/common/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,3 @@

/** @internal */
export { shortenDottedString } from './shorten_dotted_string';
export { tapFirst } from './tap_first';
19 changes: 0 additions & 19 deletions src/plugins/data/common/utils/tap_first.test.ts

This file was deleted.

20 changes: 0 additions & 20 deletions src/plugins/data/common/utils/tap_first.ts

This file was deleted.

144 changes: 144 additions & 0 deletions src/plugins/data/server/search/search_service.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,18 @@ import { createIndexPatternsStartMock } from '../index_patterns/mocks';
import { SearchService, SearchServiceSetupDependencies } from './search_service';
import { bfetchPluginMock } from '../../../bfetch/server/mocks';
import { of } from 'rxjs';
import {
IEsSearchRequest,
IEsSearchResponse,
IScopedSearchClient,
IScopedSearchSessionsClient,
ISearchSessionService,
ISearchStart,
ISearchStrategy,
} from '.';
// eslint-disable-next-line @kbn/eslint/no-restricted-paths
import { expressionsPluginMock } from '../../../expressions/public/mocks';
import { createSearchSessionsClientMock } from './mocks';

describe('Search service', () => {
let plugin: SearchService;
Expand Down Expand Up @@ -70,4 +82,136 @@ describe('Search service', () => {
expect(start).toHaveProperty('getSearchStrategy');
});
});

describe('asScopedProvider', () => {
let mockScopedClient: IScopedSearchClient;
let searcPluginStart: ISearchStart<IEsSearchRequest, IEsSearchResponse<any>>;
let mockStrategy: jest.Mocked<ISearchStrategy>;
let mockSessionService: ISearchSessionService<any>;
let mockSessionClient: jest.Mocked<IScopedSearchSessionsClient>;
const sessionId = '1234';

beforeEach(() => {
mockStrategy = { search: jest.fn().mockReturnValue(of({})) };

mockSessionClient = createSearchSessionsClientMock();
mockSessionService = {
asScopedProvider: () => (request: any) => mockSessionClient,
};

const pluginSetup = plugin.setup(mockCoreSetup, {
bfetch: bfetchPluginMock.createSetupContract(),
expressions: expressionsPluginMock.createSetupContract(),
});
pluginSetup.registerSearchStrategy('es', mockStrategy);
pluginSetup.__enhance({
defaultStrategy: 'es',
sessionService: mockSessionService,
});

searcPluginStart = plugin.start(mockCoreStart, {
fieldFormats: createFieldFormatsStartMock(),
indexPatterns: createIndexPatternsStartMock(),
});

const r: any = {};

mockScopedClient = searcPluginStart.asScoped(r);
});

describe('search', () => {
it('searches using the original request if not restoring, trackId is not called if there is no id in the response', async () => {
const searchRequest = { params: {} };
const options = { sessionId, isStored: false, isRestore: false };
mockSessionClient.trackId = jest.fn();

mockStrategy.search.mockReturnValue(
of({
rawResponse: {} as any,
})
);

await mockScopedClient.search(searchRequest, options).toPromise();

const [request, callOptions] = mockStrategy.search.mock.calls[0];

expect(callOptions).toBe(options);
expect(request).toBe(searchRequest);
expect(mockSessionClient.trackId).not.toBeCalled();
});

it('searches using the original request if `id` is provided', async () => {
const searchId = 'FnpFYlBpeXdCUTMyZXhCLTc1TWFKX0EbdDFDTzJzTE1Sck9PVTBIcW1iU05CZzo4MDA0';
const searchRequest = { id: searchId, params: {} };
const options = { sessionId, isStored: true, isRestore: true };

await mockScopedClient.search(searchRequest, options).toPromise();

const [request, callOptions] = mockStrategy.search.mock.calls[0];
expect(callOptions).toBe(options);
expect(request).toBe(searchRequest);
});

it('searches by looking up an `id` if restoring and `id` is not provided', async () => {
const searchRequest = { params: {} };
const options = { sessionId, isStored: true, isRestore: true };

mockSessionClient.getId = jest.fn().mockResolvedValueOnce('my_id');

await mockScopedClient.search(searchRequest, options).toPromise();

const [request, callOptions] = mockStrategy.search.mock.calls[0];
expect(callOptions).toBe(options);
expect(request).toStrictEqual({ ...searchRequest, id: 'my_id' });
});

it('calls `trackId` for every response, if the response contains an `id` and not restoring', async () => {
const searchRequest = { params: {} };
const options = { sessionId, isStored: false, isRestore: false };
mockSessionClient.trackId = jest.fn();

mockStrategy.search.mockReturnValue(
of(
{
id: 'my_id',
rawResponse: {} as any,
},
{
id: 'my_id',
rawResponse: {} as any,
}
)
);

await mockScopedClient.search(searchRequest, options).toPromise();

expect(mockSessionClient.trackId).toBeCalledTimes(2);

expect(mockSessionClient.trackId.mock.calls[0]).toEqual([searchRequest, 'my_id', options]);
expect(mockSessionClient.trackId.mock.calls[1]).toEqual([searchRequest, 'my_id', options]);
});

it('does not call `trackId` if restoring', async () => {
const searchRequest = { params: {} };
const options = { sessionId, isStored: true, isRestore: true };
mockSessionClient.getId = jest.fn().mockResolvedValueOnce('my_id');
mockSessionClient.trackId = jest.fn();

await mockScopedClient.search(searchRequest, options).toPromise();

expect(mockSessionClient.trackId).not.toBeCalled();
});

it('does not call `trackId` if no session id provided', async () => {
const searchRequest = { params: {} };
const options = {};
mockSessionClient.getId = jest.fn().mockResolvedValueOnce('my_id');
mockSessionClient.trackId = jest.fn();

await mockScopedClient.search(searchRequest, options).toPromise();

expect(mockSessionClient.trackId).not.toBeCalled();
});
});
});
});
7 changes: 3 additions & 4 deletions src/plugins/data/server/search/search_service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
SharedGlobalConfig,
StartServicesAccessor,
} from 'src/core/server';
import { first, switchMap } from 'rxjs/operators';
import { first, switchMap, tap } from 'rxjs/operators';
import { BfetchServerSetup } from 'src/plugins/bfetch/server';
import { ExpressionsServerSetup } from 'src/plugins/expressions/server';
import type {
Expand Down Expand Up @@ -65,7 +65,6 @@ import { aggShardDelay } from '../../common/search/aggs/buckets/shard_delay_fn';
import { ConfigSchema } from '../../config';
import { ISearchSessionService, SearchSessionService } from './session';
import { KbnServerError } from '../../../kibana_utils/server';
import { tapFirst } from '../../common';
import { registerBsearchRoute } from './routes/bsearch';

type StrategyMap = Record<string, ISearchStrategy<any, any>>;
Expand Down Expand Up @@ -274,8 +273,8 @@ export class SearchService implements Plugin<ISearchSetup, ISearchStart> {

return from(getSearchRequest()).pipe(
switchMap((searchRequest) => strategy.search(searchRequest, options, deps)),
tapFirst((response) => {
if (request.id || !options.sessionId || !response.id || options.isRestore) return;
tap((response) => {
if (!options.sessionId || !response.id || options.isRestore) return;
deps.searchSessionsClient.trackId(request, response.id, options);
})
);
Expand Down
2 changes: 1 addition & 1 deletion src/plugins/data/server/search/session/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export interface IScopedSearchSessionsClient<T = unknown> {
options: ISearchOptions
) => Promise<void>;
getSearchIdMapping: (sessionId: string) => Promise<Map<string, string>>;
save: (sessionId: string, attributes: Partial<T>) => Promise<SavedObject<T>>;
save: (sessionId: string, attributes: Partial<T>) => Promise<SavedObject<T> | undefined>;
get: (sessionId: string) => Promise<SavedObject<T>>;
find: (options: Omit<SavedObjectsFindOptions, 'type'>) => Promise<SavedObjectsFindResponse<T>>;
update: (sessionId: string, attributes: Partial<T>) => Promise<SavedObjectsUpdateResponse<T>>;
Expand Down
1 change: 1 addition & 0 deletions x-pack/plugins/data_enhanced/common/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

export {
SEARCH_SESSION_TYPE,
ENHANCED_ES_SEARCH_STRATEGY,
EQL_SEARCH_STRATEGY,
EqlRequestParams,
Expand Down
22 changes: 17 additions & 5 deletions x-pack/plugins/data_enhanced/common/search/session/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,25 @@

import { SearchSessionStatus } from './';

export const SEARCH_SESSION_TYPE = 'search-session';
export interface SearchSessionSavedObjectAttributes {
sessionId: string;
/**
* User-facing session name to be displayed in session management
*/
name: string;
name?: string;
/**
* App that created the session. e.g 'discover'
*/
appId: string;
appId?: string;
/**
* Creation time of the session
*/
created: string;
/**
* Last touch time of the session
*/
touched: string;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need an explicit touched property or can we just go by the updated_at which every saved object already has?

/**
* Expiration time of the session. Expiration itself is managed by Elasticsearch.
*/
Expand All @@ -30,22 +36,28 @@ export interface SearchSessionSavedObjectAttributes {
/**
* urlGeneratorId
*/
urlGeneratorId: string;
urlGeneratorId?: string;
/**
* The application state that was used to create the session.
* Should be used, for example, to re-load an expired search session.
*/
initialState: Record<string, unknown>;
initialState?: Record<string, unknown>;
/**
* Application state that should be used to restore the session.
* For example, relative dates are conveted to absolute ones.
*/
restoreState: Record<string, unknown>;
restoreState?: Record<string, unknown>;
/**
* Mapping of search request hashes to their corresponsing info (async search id, etc.)
*/
idMapping: Record<string, SearchSessionRequestInfo>;

/**
* This value is true if the session was actively stored by the user. If it is false, the session may be purged by the system.
*/
persisted: boolean;
lukasolson marked this conversation as resolved.
Show resolved Hide resolved
}

export interface SearchSessionRequestInfo {
/**
* ID of the async search request
Expand Down
36 changes: 35 additions & 1 deletion x-pack/plugins/data_enhanced/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,49 @@ import { schema, TypeOf } from '@kbn/config-schema';
export const configSchema = schema.object({
search: schema.object({
sessions: schema.object({
/**
* Turns the feature on \ off (incl. removing indicator and management screens)
*/
enabled: schema.boolean({ defaultValue: false }),
/**
* pageSize controls how many search session objects we load at once while monitoring
* session completion
*/
pageSize: schema.number({ defaultValue: 10000 }),
/**
* trackingInterval controls how often we track search session objects progress
*/
trackingInterval: schema.duration({ defaultValue: '10s' }),
inMemTimeout: schema.duration({ defaultValue: '1m' }),
/**
* notTouchedTimeout controls how long do we store unpersisted search session results,
* after the last search in the session has completed
*/
notTouchedTimeout: schema.duration({ defaultValue: '5m' }),
lizozom marked this conversation as resolved.
Show resolved Hide resolved
/**
* notTouchedInProgressTimeout controls how long do allow a search session to run after
* a user has navigated away without persisting
*/
notTouchedInProgressTimeout: schema.duration({ defaultValue: '1m' }),
/**
* maxUpdateRetries controls how many retries we perform while attempting to save a search session
*/
maxUpdateRetries: schema.number({ defaultValue: 3 }),
/**
* defaultExpiration controls how long search sessions are valid for, until they are expired.
*/
defaultExpiration: schema.duration({ defaultValue: '7d' }),
management: schema.object({
/**
* maxSessions controls how many saved search sessions we display per page on the management screen.
*/
maxSessions: schema.number({ defaultValue: 10000 }),
/**
* refreshInterval controls how often we refresh the management screen.
*/
refreshInterval: schema.duration({ defaultValue: '10s' }),
/**
* refreshTimeout controls how often we refresh the management screen.
*/
refreshTimeout: schema.duration({ defaultValue: '1m' }),
expiresSoonWarning: schema.duration({ defaultValue: '1d' }),
}),
Expand Down
Loading