diff --git a/src/plugins/telemetry/public/services/telemetry_service.test.ts b/src/plugins/telemetry/public/services/telemetry_service.test.ts index 8987dde232e86..778dd2fbfe0b0 100644 --- a/src/plugins/telemetry/public/services/telemetry_service.test.ts +++ b/src/plugins/telemetry/public/services/telemetry_service.test.ts @@ -18,17 +18,20 @@ describe('TelemetryService', () => { await telemetryService.fetchTelemetry(); expect(telemetryService['http'].post).toBeCalledWith('/api/telemetry/v2/clusters/_stats', { - body: JSON.stringify({ unencrypted: false }), + body: JSON.stringify({ unencrypted: false, refreshCache: false }), }); }); }); describe('fetchExample', () => { - it('calls fetchTelemetry with unencrupted: true', async () => { + it('calls fetchTelemetry with unencrypted: true, refreshCache: true', async () => { const telemetryService = mockTelemetryService(); telemetryService.fetchTelemetry = jest.fn(); await telemetryService.fetchExample(); - expect(telemetryService.fetchTelemetry).toBeCalledWith({ unencrypted: true }); + expect(telemetryService.fetchTelemetry).toBeCalledWith({ + unencrypted: true, + refreshCache: true, + }); }); }); diff --git a/src/plugins/telemetry/public/services/telemetry_service.ts b/src/plugins/telemetry/public/services/telemetry_service.ts index 63e9b66a49a92..d8732b3d4bba9 100644 --- a/src/plugins/telemetry/public/services/telemetry_service.ts +++ b/src/plugins/telemetry/public/services/telemetry_service.ts @@ -140,7 +140,7 @@ export class TelemetryService { /** Fetches an unencrypted telemetry payload so we can show it to the user **/ public fetchExample = async (): Promise => { - return await this.fetchTelemetry({ unencrypted: true }); + return await this.fetchTelemetry({ unencrypted: true, refreshCache: true }); }; /** @@ -149,9 +149,10 @@ export class TelemetryService { */ public fetchTelemetry = async ({ unencrypted = false, + refreshCache = false, } = {}): Promise => { return this.http.post('/api/telemetry/v2/clusters/_stats', { - body: JSON.stringify({ unencrypted }), + body: JSON.stringify({ unencrypted, refreshCache }), }); }; diff --git a/src/plugins/telemetry/schema/oss_root.json b/src/plugins/telemetry/schema/oss_root.json index 3748485465cc0..e526dc6413916 100644 --- a/src/plugins/telemetry/schema/oss_root.json +++ b/src/plugins/telemetry/schema/oss_root.json @@ -18,6 +18,22 @@ "collectionSource": { "type": "keyword" }, + "cacheDetails": { + "properties": { + "updatedAt": { + "type": "date", + "_meta": { + "description": "The timestamp the payload was last cached." + } + }, + "fetchedAt": { + "type": "date", + "_meta": { + "description": "The timestamp the payload was grabbed from cache." + } + } + } + }, "stack_stats": { "properties": { "data": { diff --git a/src/plugins/telemetry/server/routes/telemetry_usage_stats.ts b/src/plugins/telemetry/server/routes/telemetry_usage_stats.ts index e07a6cb8f724d..e3ce8cbc5190a 100644 --- a/src/plugins/telemetry/server/routes/telemetry_usage_stats.ts +++ b/src/plugins/telemetry/server/routes/telemetry_usage_stats.ts @@ -24,16 +24,18 @@ export function registerTelemetryUsageStatsRoutes( validate: { body: schema.object({ unencrypted: schema.boolean({ defaultValue: false }), + refreshCache: schema.boolean({ defaultValue: false }), }), }, }, async (context, req, res) => { - const { unencrypted } = req.body; + const { unencrypted, refreshCache } = req.body; try { const statsConfig: StatsGetterConfig = { request: req, unencrypted, + refreshCache, }; const stats = await telemetryCollectionManager.getStats(statsConfig); diff --git a/src/plugins/telemetry/server/telemetry_collection/get_local_stats.test.ts b/src/plugins/telemetry/server/telemetry_collection/get_local_stats.test.ts index 191e857c777d2..aacc91793a03f 100644 --- a/src/plugins/telemetry/server/telemetry_collection/get_local_stats.test.ts +++ b/src/plugins/telemetry/server/telemetry_collection/get_local_stats.test.ts @@ -80,6 +80,7 @@ function mockStatsCollectionConfig( esClient: mockGetLocalStats(clusterInfo, clusterStats), usageCollection: mockUsageCollection(kibana), kibanaRequest: httpServerMock.createKibanaRequest(), + refreshCache: false, }; } diff --git a/src/plugins/telemetry_collection_manager/common/index.ts b/src/plugins/telemetry_collection_manager/common/index.ts index 208c9e0ce3cb3..4f9c8a851bd4c 100644 --- a/src/plugins/telemetry_collection_manager/common/index.ts +++ b/src/plugins/telemetry_collection_manager/common/index.ts @@ -8,3 +8,10 @@ export const PLUGIN_ID = 'telemetryCollectionManager'; export const PLUGIN_NAME = 'telemetry_collection_manager'; + +/** + * The duration, in milliseconds, to cache stats + * Currently 4 hours. + */ +const hour = 1000 * 60 * 60; +export const CACHE_DURATION_MS = 4 * hour; diff --git a/src/plugins/telemetry_collection_manager/server/cache/cache_manager.test.ts b/src/plugins/telemetry_collection_manager/server/cache/cache_manager.test.ts new file mode 100644 index 0000000000000..01db6e2422003 --- /dev/null +++ b/src/plugins/telemetry_collection_manager/server/cache/cache_manager.test.ts @@ -0,0 +1,40 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import { CacheManager } from './cache_manager'; + +describe('CacheManager', () => { + const mockCacheKey = 'mock_key'; + const mockCacheItem = 'cache_item'; + const cacheDurationMs = 10000; + let mockNow: number; + + beforeEach(() => { + jest.useFakeTimers('modern'); + mockNow = jest.getRealSystemTime(); + jest.setSystemTime(mockNow); + }); + afterEach(() => jest.clearAllMocks()); + afterAll(() => jest.useRealTimers()); + + it('caches object for the cache duration only', () => { + const cacheManager = new CacheManager({ cacheDurationMs }); + cacheManager.setCache(mockCacheKey, mockCacheItem); + expect(cacheManager.getFromCache(mockCacheKey)).toEqual(mockCacheItem); + jest.advanceTimersByTime(cacheDurationMs + 100); + expect(cacheManager.getFromCache(mockCacheKey)).toEqual(undefined); + }); + + it('#resetCache removes cached objects', () => { + const cacheManager = new CacheManager({ cacheDurationMs }); + cacheManager.setCache(mockCacheKey, mockCacheItem); + expect(cacheManager.getFromCache(mockCacheKey)).toEqual(mockCacheItem); + cacheManager.resetCache(); + expect(cacheManager.getFromCache(mockCacheKey)).toEqual(undefined); + }); +}); diff --git a/src/plugins/telemetry_collection_manager/server/cache/cache_manager.ts b/src/plugins/telemetry_collection_manager/server/cache/cache_manager.ts new file mode 100644 index 0000000000000..6fa2482d6f942 --- /dev/null +++ b/src/plugins/telemetry_collection_manager/server/cache/cache_manager.ts @@ -0,0 +1,46 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +import LRUCache from 'lru-cache'; + +export interface CacheManagerConfig { + // cache duration of objects in ms + cacheDurationMs: number; +} + +export class CacheManager { + private readonly cache: LRUCache; + + constructor({ cacheDurationMs }: CacheManagerConfig) { + this.cache = new LRUCache({ + max: 1, + maxAge: cacheDurationMs, + }); + } + + /** + * Cache an object by key + */ + public setCache = (cacheKey: string, data: unknown): void => { + this.cache.set(cacheKey, data); + }; + + /** + * returns cached object. If the key is not found will return undefined. + */ + public getFromCache = (cacheKey: string): T | undefined => { + return this.cache.get(cacheKey) as T; + }; + + /** + * Removes all cached objects + */ + public resetCache(): void { + this.cache.reset(); + } +} diff --git a/src/plugins/telemetry_collection_manager/server/cache/index.ts b/src/plugins/telemetry_collection_manager/server/cache/index.ts new file mode 100644 index 0000000000000..8a2ad483f6c6d --- /dev/null +++ b/src/plugins/telemetry_collection_manager/server/cache/index.ts @@ -0,0 +1,10 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0 and the Server Side Public License, v 1; you may not use this file except + * in compliance with, at your election, the Elastic License 2.0 or the Server + * Side Public License, v 1. + */ + +export { CacheManager } from './cache_manager'; +export type { CacheManagerConfig } from './cache_manager'; diff --git a/src/plugins/telemetry_collection_manager/server/plugin.test.ts b/src/plugins/telemetry_collection_manager/server/plugin.test.ts index 6e37ef5ffd4f5..97cb4618d5d13 100644 --- a/src/plugins/telemetry_collection_manager/server/plugin.test.ts +++ b/src/plugins/telemetry_collection_manager/server/plugin.test.ts @@ -45,6 +45,12 @@ describe('Telemetry Collection Manager', () => { const telemetryCollectionManager = new TelemetryCollectionManagerPlugin(initializerContext); const setupApi = telemetryCollectionManager.setup(coreMock.createSetup(), { usageCollection }); const collectionStrategy = createCollectionStrategy(1); + beforeEach(() => { + // Reset cache on every request. + // 10s cache to avoid misatekly invalidating cache during test runs + // eslint-disable-next-line dot-notation + telemetryCollectionManager['cacheManager'].resetCache(); + }); describe('before start', () => { test('registers a collection strategy', () => { @@ -196,13 +202,37 @@ describe('Telemetry Collection Manager', () => { await expect(setupApi.getStats(config)).resolves.toStrictEqual([ { clusterUuid: 'clusterUuid', - stats: { ...basicStats, collectionSource: 'test_collection' }, + stats: { + ...basicStats, + cacheDetails: { updatedAt: expect.any(String), fetchedAt: expect.any(String) }, + collectionSource: 'test_collection', + }, }, ]); + expect( collectionStrategy.clusterDetailsGetter.mock.calls[0][0].soClient ).not.toBeInstanceOf(TelemetrySavedObjectsClient); }); + + test('returns cached object on multiple calls', async () => { + collectionStrategy.clusterDetailsGetter.mockResolvedValue([ + { clusterUuid: 'clusterUuid' }, + ]); + collectionStrategy.statsGetter.mockResolvedValue([basicStats]); + await setupApi.getStats(config); + + await expect(setupApi.getStats(config)).resolves.toStrictEqual([ + { + clusterUuid: 'clusterUuid', + stats: { + ...basicStats, + cacheDetails: { updatedAt: expect.any(String), fetchedAt: expect.any(String) }, + collectionSource: 'test_collection', + }, + }, + ]); + }); }); describe('getOptInStats', () => { diff --git a/src/plugins/telemetry_collection_manager/server/plugin.ts b/src/plugins/telemetry_collection_manager/server/plugin.ts index 6dd1de65a8bdc..2f4576a6a6f4f 100644 --- a/src/plugins/telemetry_collection_manager/server/plugin.ts +++ b/src/plugins/telemetry_collection_manager/server/plugin.ts @@ -32,9 +32,12 @@ import type { StatsCollectionContext, UnencryptedStatsGetterConfig, EncryptedStatsGetterConfig, + ClusterDetails, } from './types'; import { encryptTelemetry } from './encryption'; import { TelemetrySavedObjectsClient } from './telemetry_saved_objects_client'; +import { CacheManager } from './cache'; +import { CACHE_DURATION_MS } from '../common'; interface TelemetryCollectionPluginsDepsSetup { usageCollection: UsageCollectionSetup; @@ -51,6 +54,7 @@ export class TelemetryCollectionManagerPlugin private savedObjectsService?: SavedObjectsServiceStart; private readonly isDistributable: boolean; private readonly version: string; + private cacheManager = new CacheManager({ cacheDurationMs: CACHE_DURATION_MS }); constructor(initializerContext: PluginInitializerContext) { this.logger = initializerContext.logger.get(); @@ -125,9 +129,10 @@ export class TelemetryCollectionManagerPlugin const soClient = this.getSavedObjectsClient(config); // Provide the kibanaRequest so opted-in plugins can scope their custom clients only if the request is not encrypted const kibanaRequest = config.unencrypted ? config.request : void 0; + const refreshCache = !!config.refreshCache; if (esClient && soClient) { - return { usageCollection, esClient, soClient, kibanaRequest }; + return { usageCollection, esClient, soClient, kibanaRequest, refreshCache }; } } @@ -284,6 +289,25 @@ export class TelemetryCollectionManagerPlugin return []; } + private createCacheKey(collectionSource: string, clustersDetails: ClusterDetails[]) { + const clusterUUids = clustersDetails + .map(({ clusterUuid }) => clusterUuid) + .sort() + .join('_'); + + return `${collectionSource}::${clusterUUids}`; + } + + private updateFetchedAt(statsPayload: UsageStatsPayload[]): UsageStatsPayload[] { + return statsPayload.map((stat) => ({ + ...stat, + cacheDetails: { + ...stat.cacheDetails, + fetchedAt: new Date().toISOString(), + }, + })); + } + private async getUsageForCollection( collection: CollectionStrategy, statsCollectionConfig: StatsCollectionConfig @@ -292,17 +316,34 @@ export class TelemetryCollectionManagerPlugin logger: this.logger.get(collection.title), version: this.version, }; - const clustersDetails = await collection.clusterDetailsGetter(statsCollectionConfig, context); + const { refreshCache } = statsCollectionConfig; + const { title: collectionSource } = collection; + + // on `refreshCache: true` clear all cache to store a fresh copy + if (refreshCache) { + this.cacheManager.resetCache(); + } if (clustersDetails.length === 0) { - // don't bother doing a further lookup. return []; } + const cacheKey = this.createCacheKey(collectionSource, clustersDetails); + const cachedUsageStatsPayload = this.cacheManager.getFromCache(cacheKey); + if (cachedUsageStatsPayload) { + return this.updateFetchedAt(cachedUsageStatsPayload); + } + + const now = new Date().toISOString(); const stats = await collection.statsGetter(clustersDetails, statsCollectionConfig, context); + const usageStatsPayload = stats.map((stat) => ({ + collectionSource, + cacheDetails: { updatedAt: now, fetchedAt: now }, + ...stat, + })); + this.cacheManager.setCache(cacheKey, usageStatsPayload); - // Add the `collectionSource` to the resulting payload - return stats.map((stat) => ({ collectionSource: collection.title, ...stat })); + return this.updateFetchedAt(usageStatsPayload); } } diff --git a/src/plugins/telemetry_collection_manager/server/types.ts b/src/plugins/telemetry_collection_manager/server/types.ts index 648e457f9a238..b9b570e3f287b 100644 --- a/src/plugins/telemetry_collection_manager/server/types.ts +++ b/src/plugins/telemetry_collection_manager/server/types.ts @@ -37,6 +37,7 @@ export interface TelemetryOptInStats { export interface BaseStatsGetterConfig { unencrypted: boolean; + refreshCache?: boolean; request?: KibanaRequest; } @@ -58,6 +59,12 @@ export interface StatsCollectionConfig { esClient: ElasticsearchClient; soClient: SavedObjectsClientContract; kibanaRequest: KibanaRequest | undefined; // intentionally `| undefined` to enforce providing the parameter + refreshCache: boolean; +} + +export interface CacheDetails { + updatedAt: string; + fetchedAt: string; } export interface BasicStatsPayload { @@ -71,6 +78,7 @@ export interface BasicStatsPayload { } export interface UsageStatsPayload extends BasicStatsPayload { + cacheDetails: CacheDetails; collectionSource: string; } diff --git a/x-pack/plugins/monitoring/server/telemetry_collection/register_monitoring_telemetry_collection.ts b/x-pack/plugins/monitoring/server/telemetry_collection/register_monitoring_telemetry_collection.ts index bb5d379f3c80c..bce6f57d6f950 100644 --- a/x-pack/plugins/monitoring/server/telemetry_collection/register_monitoring_telemetry_collection.ts +++ b/x-pack/plugins/monitoring/server/telemetry_collection/register_monitoring_telemetry_collection.ts @@ -14,7 +14,7 @@ import { getAllStats } from './get_all_stats'; import { getClusterUuids } from './get_cluster_uuids'; import { getLicenses } from './get_licenses'; -interface MonitoringStats extends UsageStatsPayload { +interface MonitoringStats extends Omit { stack_stats: { logstash?: LogstashBaseStats; beats?: BeatsBaseStats; @@ -149,6 +149,7 @@ export function registerMonitoringTelemetryCollection( getLicenses(clusterDetails, callCluster, maxBucketSize), getAllStats(clusterDetails, callCluster, timestamp, maxBucketSize), ]); + return { stats: stats.map((stat) => { const license = licenses[stat.cluster_uuid]; diff --git a/x-pack/plugins/telemetry_collection_xpack/server/telemetry_collection/get_stats_with_xpack.test.ts b/x-pack/plugins/telemetry_collection_xpack/server/telemetry_collection/get_stats_with_xpack.test.ts index 2d1fea23c5ef3..39ffc566a06b0 100644 --- a/x-pack/plugins/telemetry_collection_xpack/server/telemetry_collection/get_stats_with_xpack.test.ts +++ b/x-pack/plugins/telemetry_collection_xpack/server/telemetry_collection/get_stats_with_xpack.test.ts @@ -119,6 +119,7 @@ describe('Telemetry Collection: Get Aggregated Stats', () => { usageCollection, soClient, kibanaRequest: undefined, + refreshCache: false, }, context ); @@ -141,6 +142,7 @@ describe('Telemetry Collection: Get Aggregated Stats', () => { usageCollection, soClient, kibanaRequest: undefined, + refreshCache: false, }, context ); @@ -168,6 +170,7 @@ describe('Telemetry Collection: Get Aggregated Stats', () => { usageCollection, soClient, kibanaRequest: undefined, + refreshCache: false, }, context ); diff --git a/x-pack/test/api_integration/apis/telemetry/telemetry.ts b/x-pack/test/api_integration/apis/telemetry/telemetry.ts index 527d755123f26..fb0d56f7d7532 100644 --- a/x-pack/test/api_integration/apis/telemetry/telemetry.ts +++ b/x-pack/test/api_integration/apis/telemetry/telemetry.ts @@ -20,6 +20,22 @@ import ossPluginsTelemetrySchema from '../../../../../src/plugins/telemetry/sche import xpackPluginsTelemetrySchema from '../../../../plugins/telemetry_collection_xpack/schema/xpack_plugins.json'; import { assertTelemetryPayload } from '../../../../../test/api_integration/apis/telemetry/utils'; import type { UnencryptedTelemetryPayload } from '../../../../../src/plugins/telemetry/common/types'; +import type { + UsageStatsPayload, + CacheDetails, +} from '../../../../../src/plugins/telemetry_collection_manager/server/types'; + +function omitCacheDetails(usagePayload: Array>) { + return usagePayload.map(({ cacheDetails, ...item }) => item); +} + +function updateFixtureTimestamps(fixture: Array>, timestamp: string) { + return fixture.map((item) => ({ ...item, timestamp })); +} + +function getCacheDetails(body: UnencryptedTelemetryPayload): CacheDetails[] { + return body.map(({ stats }) => (stats as UsageStatsPayload).cacheDetails); +} /** * Update the .monitoring-* documents loaded via the archiver to the recent `timestamp` @@ -96,7 +112,7 @@ export default function ({ getService }: FtrProviderContext) { const { body }: { body: UnencryptedTelemetryPayload } = await supertest .post('/api/telemetry/v2/clusters/_stats') .set('kbn-xsrf', 'xxx') - .send({ unencrypted: true }) + .send({ unencrypted: true, refreshCache: true }) .expect(200); expect(body.length).to.be.greaterThan(1); @@ -116,6 +132,7 @@ export default function ({ getService }: FtrProviderContext) { monitoringRootTelemetrySchema.properties.monitoringTelemetry.properties.stats.items ); const plugins = deepmerge(ossPluginsTelemetrySchema, xpackPluginsTelemetrySchema); + try { assertTelemetryPayload({ root, plugins }, localXPack); monitoring.forEach((stats) => { @@ -130,7 +147,10 @@ export default function ({ getService }: FtrProviderContext) { it('should load multiple trial-license clusters', async () => { expect(monitoring).length(3); expect(localXPack.collectionSource).to.eql('local_xpack'); - expect(monitoring).to.eql(multiClusterFixture.map((item) => ({ ...item, timestamp }))); + + expect(omitCacheDetails(monitoring)).to.eql( + updateFixtureTimestamps(multiClusterFixture, timestamp) + ); }); }); @@ -147,15 +167,76 @@ export default function ({ getService }: FtrProviderContext) { const { body }: { body: UnencryptedTelemetryPayload } = await supertest .post('/api/telemetry/v2/clusters/_stats') .set('kbn-xsrf', 'xxx') - .send({ unencrypted: true }) + .send({ unencrypted: true, refreshCache: true }) .expect(200); expect(body).length(2); const telemetryStats = body.map(({ stats }) => stats); - const [localXPack, ...monitoring] = telemetryStats; - expect((localXPack as Record).collectionSource).to.eql('local_xpack'); - expect(monitoring).to.eql(basicClusterFixture.map((item) => ({ ...item, timestamp }))); + const [localXPack, ...monitoring] = telemetryStats as Array>; + expect(localXPack.collectionSource).to.eql('local_xpack'); + expect(omitCacheDetails(monitoring)).to.eql( + updateFixtureTimestamps(basicClusterFixture, timestamp) + ); + }); + }); + + describe('Telemetry caching', () => { + const archive = 'x-pack/test/functional/es_archives/monitoring/basic_6.3.x'; + const fromTimestamp = '2018-07-23T22:54:59.087Z'; + const toTimestamp = '2018-07-23T22:55:05.933Z'; + let cacheLastUpdated: string[] = []; + + before(async () => { + await esArchiver.load(archive); + await updateMonitoringDates(esSupertest, fromTimestamp, toTimestamp, timestamp); + + // hit the endpoint to cache results + const { body }: { body: UnencryptedTelemetryPayload } = await supertest + .post('/api/telemetry/v2/clusters/_stats') + .set('kbn-xsrf', 'xxx') + .send({ unencrypted: true, refreshCache: true }) + .expect(200); + + cacheLastUpdated = getCacheDetails(body).map(({ updatedAt }) => updatedAt); + }); + after(() => esArchiver.unload(archive)); + + it('returns cached results by default', async () => { + const now = Date.now(); + const { body }: { body: UnencryptedTelemetryPayload } = await supertest + .post('/api/telemetry/v2/clusters/_stats') + .set('kbn-xsrf', 'xxx') + .send({ unencrypted: true }) + .expect(200); + + expect(body).length(2); + + const cacheDetails = getCacheDetails(body); + // Check that the fetched payload is actually cached by comparing cache and updatedAt timestamps + expect(cacheDetails.map(({ updatedAt }) => updatedAt)).to.eql(cacheLastUpdated); + // Check that the fetchedAt timestamp is updated when the data is fethed + cacheDetails.forEach(({ fetchedAt }) => { + expect(new Date(fetchedAt).getTime()).to.be.greaterThan(now); + }); + }); + }); + + it('grabs a fresh copy on refresh', async () => { + const now = Date.now(); + const { body }: { body: UnencryptedTelemetryPayload } = await supertest + .post('/api/telemetry/v2/clusters/_stats') + .set('kbn-xsrf', 'xxx') + .send({ unencrypted: true, refreshCache: true }) + .expect(200); + + expect(body).length(1); + getCacheDetails(body).forEach(({ updatedAt, fetchedAt }) => { + // Check that the cache is fresh by comparing updatedAt timestamp with + // the timestamp the data was fetched. + expect(new Date(updatedAt).getTime()).to.be.greaterThan(now); + // Check that the fetchedAt timestamp is updated when the data is fethed + expect(new Date(fetchedAt).getTime()).to.be.greaterThan(now); }); }); }); diff --git a/x-pack/test/api_integration/apis/telemetry/telemetry_local.ts b/x-pack/test/api_integration/apis/telemetry/telemetry_local.ts index e34e0fff25888..630fdef6a8d1f 100644 --- a/x-pack/test/api_integration/apis/telemetry/telemetry_local.ts +++ b/x-pack/test/api_integration/apis/telemetry/telemetry_local.ts @@ -43,7 +43,7 @@ export default function ({ getService }: FtrProviderContext) { const { body } = await supertest .post('/api/telemetry/v2/clusters/_stats') .set('kbn-xsrf', 'xxx') - .send({ unencrypted: true }) + .send({ unencrypted: true, refreshCache: true }) .expect(200); expect(body.length).to.be(1); diff --git a/x-pack/test/detection_engine_api_integration/utils.ts b/x-pack/test/detection_engine_api_integration/utils.ts index 858c4901b2dd3..e13174d66fb1d 100644 --- a/x-pack/test/detection_engine_api_integration/utils.ts +++ b/x-pack/test/detection_engine_api_integration/utils.ts @@ -1930,7 +1930,7 @@ export const getStats = async ( const response = await supertest .post(getStatsUrl()) .set('kbn-xsrf', 'true') - .send({ unencrypted: true }); + .send({ unencrypted: true, refreshCache: true }); if (response.status !== 200) { log.error( `Did not get an expected 200 "ok" when getting the stats for detections. CI issues could happen. Suspect this line if you are seeing CI issues. body: ${JSON.stringify( diff --git a/x-pack/test/fleet_api_integration/apis/fleet_telemetry.ts b/x-pack/test/fleet_api_integration/apis/fleet_telemetry.ts index 0d8f38c55c7f8..4d06de83f6917 100644 --- a/x-pack/test/fleet_api_integration/apis/fleet_telemetry.ts +++ b/x-pack/test/fleet_api_integration/apis/fleet_telemetry.ts @@ -113,6 +113,7 @@ export default function (providerContext: FtrProviderContext) { .set('kbn-xsrf', 'xxxx') .send({ unencrypted: true, + refreshCache: true, }) .expect(200);