diff --git a/api_docs/case.mdx b/api_docs/case.mdx deleted file mode 100644 index 00a5d3c04060b..0000000000000 --- a/api_docs/case.mdx +++ /dev/null @@ -1,18 +0,0 @@ ---- -id: kibCasePluginApi -slug: /kibana-dev-docs/casePluginApi -title: case -image: https://source.unsplash.com/400x175/?github -summary: API docs for the case plugin -date: 2020-11-16 -tags: ['contributor', 'dev', 'apidocs', 'kibana', 'case'] -warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. ---- - -import caseObj from './case.json'; - -## Server - -### Interfaces - - diff --git a/api_docs/cases.json b/api_docs/cases.json index ca02367539e60..8ac2fb86061bd 100644 --- a/api_docs/cases.json +++ b/api_docs/cases.json @@ -1,5 +1,5 @@ { - "id": "case", + "id": "cases", "client": { "classes": [], "functions": [], @@ -34,7 +34,7 @@ { "pluginId": "cases", "scope": "server", - "docId": "kibCasePluginApi", + "docId": "kibCasesPluginApi", "section": "def-server.CasesClient", "text": "CasesClient" } @@ -60,4 +60,4 @@ "misc": [], "objects": [] } -} +} \ No newline at end of file diff --git a/api_docs/cases.mdx b/api_docs/cases.mdx new file mode 100644 index 0000000000000..36429f257d357 --- /dev/null +++ b/api_docs/cases.mdx @@ -0,0 +1,18 @@ +--- +id: kibCasesPluginApi +slug: /kibana-dev-docs/casesPluginApi +title: cases +image: https://source.unsplash.com/400x175/?github +summary: API docs for the cases plugin +date: 2020-11-16 +tags: ['contributor', 'dev', 'apidocs', 'kibana', 'cases'] +warning: This document is auto-generated and is meant to be viewed inside our experimental, new docs system. Reach out in #docs-engineering for more info. +--- + +import casesObj from './cases.json'; + +## Server + +### Interfaces + + diff --git a/api_docs/data_search.json b/api_docs/data_search.json index 01d00914cb293..def169091aa75 100644 --- a/api_docs/data_search.json +++ b/api_docs/data_search.json @@ -1435,7 +1435,15 @@ "section": "def-server.SearchUsage", "text": "SearchUsage" }, - " | undefined) => { next(response: ", + " | undefined, { isRestore }: ", + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.ISearchOptions", + "text": "ISearchOptions" + }, + ") => { next(response: ", { "pluginId": "data", "scope": "common", @@ -1459,7 +1467,7 @@ "description": [], "source": { "path": "src/plugins/data/server/search/collectors/usage.ts", - "lineNumber": 64 + "lineNumber": 83 } }, { @@ -1479,7 +1487,26 @@ "description": [], "source": { "path": "src/plugins/data/server/search/collectors/usage.ts", - "lineNumber": 64 + "lineNumber": 84 + } + }, + { + "type": "Object", + "label": "{ isRestore }", + "isRequired": true, + "signature": [ + { + "pluginId": "data", + "scope": "common", + "docId": "kibDataSearchPluginApi", + "section": "def-common.ISearchOptions", + "text": "ISearchOptions" + } + ], + "description": [], + "source": { + "path": "src/plugins/data/server/search/collectors/usage.ts", + "lineNumber": 85 } } ], @@ -1487,7 +1514,7 @@ "returnComment": [], "source": { "path": "src/plugins/data/server/search/collectors/usage.ts", - "lineNumber": 64 + "lineNumber": 82 }, "initialIsOpen": false }, diff --git a/api_docs/lists.json b/api_docs/lists.json index 8c6639e0ac85e..3e6a22c538504 100644 --- a/api_docs/lists.json +++ b/api_docs/lists.json @@ -4489,6 +4489,22 @@ ], "initialIsOpen": false }, + { + "tags": [], + "id": "def-common.osType", + "type": "Object", + "label": "osType", + "description": [], + "source": { + "path": "x-pack/plugins/lists/common/schemas/common/schemas.ts", + "lineNumber": 317 + }, + "signature": [ + "KeyofC", + "<{ linux: null; macos: null; windows: null; }>" + ], + "initialIsOpen": false + }, { "tags": [], "id": "def-common.osTypeArray", diff --git a/api_docs/security_solution.json b/api_docs/security_solution.json index 01fef476278b9..cbcd660749f2d 100644 --- a/api_docs/security_solution.json +++ b/api_docs/security_solution.json @@ -607,7 +607,7 @@ "description": [], "source": { "path": "x-pack/plugins/security_solution/server/plugin.ts", - "lineNumber": 336 + "lineNumber": 337 } }, { @@ -626,7 +626,7 @@ "description": [], "source": { "path": "x-pack/plugins/security_solution/server/plugin.ts", - "lineNumber": 336 + "lineNumber": 337 } } ], @@ -634,7 +634,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/security_solution/server/plugin.ts", - "lineNumber": 336 + "lineNumber": 337 } }, { @@ -650,7 +650,7 @@ "returnComment": [], "source": { "path": "x-pack/plugins/security_solution/server/plugin.ts", - "lineNumber": 397 + "lineNumber": 398 } } ], diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md index 491babcdfdecf..fd9ed1e8f635c 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.md @@ -35,7 +35,7 @@ | [getTime(indexPattern, timeRange, options)](./kibana-plugin-plugins-data-server.gettime.md) | | | [parseInterval(interval)](./kibana-plugin-plugins-data-server.parseinterval.md) | | | [plugin(initializerContext)](./kibana-plugin-plugins-data-server.plugin.md) | Static code to be shared externally | -| [searchUsageObserver(logger, usage)](./kibana-plugin-plugins-data-server.searchusageobserver.md) | Rxjs observer for easily doing tap(searchUsageObserver(logger, usage)) in an rxjs chain. | +| [searchUsageObserver(logger, usage, { isRestore })](./kibana-plugin-plugins-data-server.searchusageobserver.md) | Rxjs observer for easily doing tap(searchUsageObserver(logger, usage)) in an rxjs chain. | | [shouldReadFieldFromDocValues(aggregatable, esType)](./kibana-plugin-plugins-data-server.shouldreadfieldfromdocvalues.md) | | | [usageProvider(core)](./kibana-plugin-plugins-data-server.usageprovider.md) | | diff --git a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchusageobserver.md b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchusageobserver.md index 5e03bb381527e..e9c7b33766e56 100644 --- a/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchusageobserver.md +++ b/docs/development/plugins/data/server/kibana-plugin-plugins-data-server.searchusageobserver.md @@ -9,7 +9,7 @@ Rxjs observer for easily doing `tap(searchUsageObserver(logger, usage))` in an r Signature: ```typescript -export declare function searchUsageObserver(logger: Logger, usage?: SearchUsage): { +export declare function searchUsageObserver(logger: Logger, usage?: SearchUsage, { isRestore }?: ISearchOptions): { next(response: IEsSearchResponse): void; error(): void; }; @@ -21,6 +21,7 @@ export declare function searchUsageObserver(logger: Logger, usage?: SearchUsage) | --- | --- | --- | | logger | Logger | | | usage | SearchUsage | | +| { isRestore } | ISearchOptions | | Returns: diff --git a/src/plugins/data/server/search/collectors/usage.ts b/src/plugins/data/server/search/collectors/usage.ts index c9f0a5bf24944..f9d703900fc04 100644 --- a/src/plugins/data/server/search/collectors/usage.ts +++ b/src/plugins/data/server/search/collectors/usage.ts @@ -6,13 +6,13 @@ * Side Public License, v 1. */ -import { once } from 'lodash'; +import { once, debounce } from 'lodash'; import type { CoreSetup, Logger } from 'kibana/server'; -import { SavedObjectsErrorHelpers } from '../../../../../core/server'; -import type { IEsSearchResponse } from '../../../common'; +import type { IEsSearchResponse, ISearchOptions } from '../../../common'; +import { isCompleteResponse } from '../../../common'; +import { CollectedUsage } from './register'; const SAVED_OBJECT_ID = 'search-telemetry'; -const MAX_RETRY_COUNT = 3; export interface SearchUsage { trackError(): Promise; @@ -25,34 +25,52 @@ export function usageProvider(core: CoreSetup): SearchUsage { return coreStart.savedObjects.createInternalRepository(); }); - const trackSuccess = async (duration: number, retryCount = 0) => { - const repository = await getRepository(); - try { - await repository.incrementCounter(SAVED_OBJECT_ID, SAVED_OBJECT_ID, [ - { fieldName: 'successCount' }, - { - fieldName: 'totalDuration', - incrementBy: duration, - }, - ]); - } catch (e) { - if (SavedObjectsErrorHelpers.isConflictError(e) && retryCount < MAX_RETRY_COUNT) { - setTimeout(() => trackSuccess(duration, retryCount + 1), 1000); - } - } + const collectedUsage: CollectedUsage = { + successCount: 0, + errorCount: 0, + totalDuration: 0, }; - const trackError = async (retryCount = 0) => { - const repository = await getRepository(); - try { - await repository.incrementCounter(SAVED_OBJECT_ID, SAVED_OBJECT_ID, [ - { fieldName: 'errorCount' }, - ]); - } catch (e) { - if (SavedObjectsErrorHelpers.isConflictError(e) && retryCount < MAX_RETRY_COUNT) { - setTimeout(() => trackError(retryCount + 1), 1000); + // Instead of updating the search count every time a search completes, we update some in-memory + // counts and only update the saved object every ~5 seconds + const updateSearchUsage = debounce( + async () => { + const repository = await getRepository(); + const { successCount, errorCount, totalDuration } = collectedUsage; + const counterFields = Object.entries(collectedUsage) + .map(([fieldName, incrementBy]) => ({ fieldName, incrementBy })) + // Filter out any zero values because `incrementCounter` will still increment them + .filter(({ incrementBy }) => incrementBy > 0); + + try { + await repository.incrementCounter( + SAVED_OBJECT_ID, + SAVED_OBJECT_ID, + counterFields + ); + + // Since search requests may have completed while the saved object was being updated, we minus + // what was just updated in the saved object rather than resetting the values to 0 + collectedUsage.successCount -= successCount; + collectedUsage.errorCount -= errorCount; + collectedUsage.totalDuration -= totalDuration; + } catch (e) { + // We didn't reset the counters so we'll retry when the next search request completes } - } + }, + 5000, + { maxWait: 5000 } + ); + + const trackSuccess = (duration: number) => { + collectedUsage.successCount++; + collectedUsage.totalDuration += duration; + return updateSearchUsage(); + }; + + const trackError = () => { + collectedUsage.errorCount++; + return updateSearchUsage(); }; return { trackSuccess, trackError }; @@ -61,9 +79,14 @@ export function usageProvider(core: CoreSetup): SearchUsage { /** * Rxjs observer for easily doing `tap(searchUsageObserver(logger, usage))` in an rxjs chain. */ -export function searchUsageObserver(logger: Logger, usage?: SearchUsage) { +export function searchUsageObserver( + logger: Logger, + usage?: SearchUsage, + { isRestore }: ISearchOptions = {} +) { return { next(response: IEsSearchResponse) { + if (isRestore || !isCompleteResponse(response)) return; logger.debug(`trackSearchStatus:next ${response.rawResponse.took}`); usage?.trackSuccess(response.rawResponse.took); }, diff --git a/src/plugins/data/server/search/es_search/es_search_strategy.ts b/src/plugins/data/server/search/es_search/es_search_strategy.ts index 4b6f1f9786958..cc81dce94c4ec 100644 --- a/src/plugins/data/server/search/es_search/es_search_strategy.ts +++ b/src/plugins/data/server/search/es_search/es_search_strategy.ts @@ -53,6 +53,6 @@ export const esSearchStrategyProvider = ( } }; - return from(search()).pipe(tap(searchUsageObserver(logger, usage))); + return from(search()).pipe(tap(searchUsageObserver(logger, usage, options))); }, }); diff --git a/src/plugins/data/server/server.api.md b/src/plugins/data/server/server.api.md index 0205bdfd759f7..41f4d2d52a786 100644 --- a/src/plugins/data/server/server.api.md +++ b/src/plugins/data/server/server.api.md @@ -1356,7 +1356,7 @@ export interface SearchUsage { // Warning: (ae-missing-release-tag) "searchUsageObserver" is exported by the package, but it is missing a release tag (@alpha, @beta, @public, or @internal) // // @public -export function searchUsageObserver(logger: Logger_2, usage?: SearchUsage): { +export function searchUsageObserver(logger: Logger_2, usage?: SearchUsage, { isRestore }?: ISearchOptions): { next(response: IEsSearchResponse): void; error(): void; };