Skip to content

Commit 161d8b9

Browse files
authored
[Synthetics] Use createPointInTimeFinder to fetch savedObjects in sync (#142640)
1 parent 195ce74 commit 161d8b9

File tree

3 files changed

+96
-122
lines changed

3 files changed

+96
-122
lines changed

x-pack/plugins/synthetics/server/routes/synthetics_service/test_now_monitor.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export const testNowMonitorRoute: SyntheticsRestApiRouteFactory = () => ({
5656

5757
const testRunId = uuidv4();
5858

59-
const errors = await syntheticsService.triggerConfigs(request, [
59+
const errors = await syntheticsService.runOnceConfigs([
6060
formatHeartbeatRequest({
6161
// making it enabled, even if it's disabled in the UI
6262
monitor: { ...normalizedMonitor.attributes, enabled: true },

x-pack/plugins/synthetics/server/synthetics_service/synthetics_service.test.ts

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -157,31 +157,14 @@ describe('SyntheticsService', () => {
157157
});
158158

159159
describe('pushConfigs', () => {
160-
it('does not include the isEdit flag on normal push requests', async () => {
161-
const { service, locations } = getMockedService();
162-
163-
(axios as jest.MockedFunction<typeof axios>).mockResolvedValue({} as AxiosResponse);
164-
165-
const payload = getFakePayload([locations[0]]);
166-
167-
await service.pushConfigs([payload] as HeartbeatConfig[]);
168-
169-
expect(axios).toHaveBeenCalledTimes(1);
170-
expect(axios).toHaveBeenCalledWith(
171-
expect.objectContaining({
172-
data: expect.objectContaining({ is_edit: false }),
173-
})
174-
);
175-
});
176-
177160
it('includes the isEdit flag on edit requests', async () => {
178161
const { service, locations } = getMockedService();
179162

180163
(axios as jest.MockedFunction<typeof axios>).mockResolvedValue({} as AxiosResponse);
181164

182165
const payload = getFakePayload([locations[0]]);
183166

184-
await service.pushConfigs([payload] as HeartbeatConfig[], true);
167+
await service.editConfig([payload] as HeartbeatConfig[]);
185168

186169
expect(axios).toHaveBeenCalledTimes(1);
187170
expect(axios).toHaveBeenCalledWith(

x-pack/plugins/synthetics/server/synthetics_service/synthetics_service.ts

Lines changed: 94 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,14 @@
88
/* eslint-disable max-classes-per-file */
99

1010
import { SavedObject } from '@kbn/core/server';
11-
import { KibanaRequest, Logger } from '@kbn/core/server';
11+
import { Logger } from '@kbn/core/server';
1212
import {
1313
ConcreteTaskInstance,
1414
TaskManagerSetupContract,
1515
TaskManagerStartContract,
1616
TaskInstance,
1717
} from '@kbn/task-manager-plugin/server';
18+
import { Subject } from 'rxjs';
1819
import { sendErrorTelemetryEvents } from '../routes/telemetry/monitor_upgrade_sender';
1920
import { UptimeServerSetup } from '../legacy_uptime/lib/adapters';
2021
import { installSyntheticsIndexTemplates } from '../routes/synthetics_service/install_index_templates';
@@ -170,7 +171,7 @@ export class SyntheticsService {
170171

171172
if (service.isAllowed) {
172173
service.setupIndexTemplates();
173-
service.syncErrors = await service.pushConfigs();
174+
await service.pushConfigs();
174175
}
175176
} catch (e) {
176177
sendErrorTelemetryEvents(service.logger, service.server.telemetry, {
@@ -306,65 +307,44 @@ export class SyntheticsService {
306307
}
307308
}
308309

309-
async pushConfigs(configs?: HeartbeatConfig[], isEdit?: boolean) {
310-
const monitorConfigs = configs ?? (await this.getMonitorConfigs());
311-
const monitors = this.formatConfigs(monitorConfigs);
312-
313-
if (monitors.length === 0) {
314-
this.logger.debug('No monitor found which can be pushed to service.');
315-
return null;
316-
}
317-
318-
this.apiKey = await this.getApiKey();
319-
320-
if (!this.apiKey) {
321-
return null;
322-
}
310+
async pushConfigs() {
311+
const service = this;
312+
const subject = new Subject<SyntheticsMonitorWithId[]>();
323313

324-
const data = {
325-
monitors,
326-
output: await this.getOutput(this.apiKey),
327-
isEdit: !!isEdit,
328-
};
314+
subject.subscribe(async (monitorConfigs) => {
315+
const monitors = this.formatConfigs(monitorConfigs);
329316

330-
this.logger.debug(`${monitors.length} monitors will be pushed to synthetics service.`);
317+
if (monitors.length === 0) {
318+
this.logger.debug('No monitor found which can be pushed to service.');
319+
return null;
320+
}
331321

332-
try {
333-
this.syncErrors = await this.apiClient.put(data);
334-
return this.syncErrors;
335-
} catch (e) {
336-
this.logger.error(e);
337-
throw e;
338-
}
339-
}
322+
this.apiKey = await this.getApiKey();
340323

341-
async runOnceConfigs(configs?: HeartbeatConfig[]) {
342-
const monitors = this.formatConfigs(configs || (await this.getMonitorConfigs()));
343-
if (monitors.length === 0) {
344-
return;
345-
}
324+
if (!this.apiKey) {
325+
return null;
326+
}
346327

347-
this.apiKey = await this.getApiKey();
328+
const data = {
329+
monitors,
330+
output: await this.getOutput(this.apiKey),
331+
};
348332

349-
if (!this.apiKey) {
350-
return null;
351-
}
333+
this.logger.debug(`${monitors.length} monitors will be pushed to synthetics service.`);
352334

353-
const data = {
354-
monitors,
355-
output: await this.getOutput(this.apiKey),
356-
};
335+
try {
336+
service.syncErrors = await this.apiClient.put(data);
337+
} catch (e) {
338+
this.logger.error(e);
339+
throw e;
340+
}
341+
});
357342

358-
try {
359-
return await this.apiClient.runOnce(data);
360-
} catch (e) {
361-
this.logger.error(e);
362-
throw e;
363-
}
343+
await this.getMonitorConfigs(subject);
364344
}
365345

366-
async triggerConfigs(request?: KibanaRequest, configs?: HeartbeatConfig[]) {
367-
const monitors = this.formatConfigs(configs || (await this.getMonitorConfigs()));
346+
async runOnceConfigs(configs: HeartbeatConfig[]) {
347+
const monitors = this.formatConfigs(configs);
368348
if (monitors.length === 0) {
369349
return;
370350
}
@@ -401,81 +381,92 @@ export class SyntheticsService {
401381
};
402382
const result = await this.apiClient.delete(data);
403383
if (this.syncErrors && this.syncErrors?.length > 0) {
404-
this.syncErrors = await this.pushConfigs();
384+
await this.pushConfigs();
405385
}
406386
return result;
407387
}
408388

409389
async deleteAllConfigs() {
410-
const configs = await this.getMonitorConfigs();
411-
return await this.deleteConfigs(configs);
390+
const subject = new Subject<SyntheticsMonitorWithId[]>();
391+
392+
subject.subscribe(async (monitors) => {
393+
await this.deleteConfigs(monitors);
394+
});
395+
396+
await this.getMonitorConfigs(subject);
412397
}
413398

414-
async getMonitorConfigs() {
415-
const savedObjectsClient = this.server.savedObjectsClient;
399+
async getMonitorConfigs(subject: Subject<SyntheticsMonitorWithId[]>) {
400+
const soClient = this.server.savedObjectsClient;
416401
const encryptedClient = this.server.encryptedSavedObjects.getClient();
417402

418-
if (!savedObjectsClient?.find) {
403+
if (!soClient?.find) {
419404
return [] as SyntheticsMonitorWithId[];
420405
}
421406

422-
const { saved_objects: encryptedMonitors } = await savedObjectsClient.find<SyntheticsMonitor>({
407+
const finder = soClient.createPointInTimeFinder({
423408
type: syntheticsMonitorType,
409+
perPage: 500,
424410
namespaces: ['*'],
425-
perPage: 10000,
426411
});
427412

428413
const start = performance.now();
429414

430-
const monitors: Array<SavedObject<SyntheticsMonitorWithSecrets>> = (
431-
await Promise.all(
432-
encryptedMonitors.map(
433-
(monitor) =>
434-
new Promise((resolve) => {
435-
encryptedClient
436-
.getDecryptedAsInternalUser<SyntheticsMonitorWithSecrets>(
437-
syntheticsMonitor.name,
438-
monitor.id,
439-
{
440-
namespace: monitor.namespaces?.[0],
441-
}
442-
)
443-
.then((decryptedMonitor) => resolve(decryptedMonitor))
444-
.catch((e) => {
445-
this.logger.error(e);
446-
sendErrorTelemetryEvents(this.logger, this.server.telemetry, {
447-
reason: 'Failed to decrypt monitor',
448-
message: e?.message,
449-
type: 'runTaskError',
450-
code: e?.code,
451-
status: e.status,
452-
kibanaVersion: this.server.kibanaVersion,
415+
for await (const result of finder.find()) {
416+
const encryptedMonitors = result.saved_objects;
417+
418+
const monitors: Array<SavedObject<SyntheticsMonitorWithSecrets>> = (
419+
await Promise.all(
420+
encryptedMonitors.map(
421+
(monitor) =>
422+
new Promise((resolve) => {
423+
encryptedClient
424+
.getDecryptedAsInternalUser<SyntheticsMonitorWithSecrets>(
425+
syntheticsMonitor.name,
426+
monitor.id,
427+
{
428+
namespace: monitor.namespaces?.[0],
429+
}
430+
)
431+
.then((decryptedMonitor) => resolve(decryptedMonitor))
432+
.catch((e) => {
433+
this.logger.error(e);
434+
sendErrorTelemetryEvents(this.logger, this.server.telemetry, {
435+
reason: 'Failed to decrypt monitor',
436+
message: e?.message,
437+
type: 'runTaskError',
438+
code: e?.code,
439+
status: e.status,
440+
kibanaVersion: this.server.kibanaVersion,
441+
});
442+
resolve(null);
453443
});
454-
resolve(null);
455-
});
456-
})
444+
})
445+
)
457446
)
458-
)
459-
).filter((monitor) => monitor !== null) as Array<SavedObject<SyntheticsMonitorWithSecrets>>;
460-
461-
const end = performance.now();
462-
const duration = end - start;
447+
).filter((monitor) => monitor !== null) as Array<SavedObject<SyntheticsMonitorWithSecrets>>;
463448

464-
this.logger.debug(`Decrypted ${monitors.length} monitors. Took ${duration} milliseconds`, {
465-
event: {
466-
duration,
467-
},
468-
monitors: monitors.length,
469-
});
449+
const end = performance.now();
450+
const duration = end - start;
470451

471-
return (monitors ?? []).map((monitor) => {
472-
const attributes = monitor.attributes as unknown as MonitorFields;
473-
return formatHeartbeatRequest({
474-
monitor: normalizeSecrets(monitor).attributes,
475-
monitorId: monitor.id,
476-
customHeartbeatId: attributes[ConfigKey.CUSTOM_HEARTBEAT_ID],
452+
this.logger.debug(`Decrypted ${monitors.length} monitors. Took ${duration} milliseconds`, {
453+
event: {
454+
duration,
455+
},
456+
monitors: monitors.length,
477457
});
478-
});
458+
459+
subject.next(
460+
(monitors ?? []).map((monitor) => {
461+
const attributes = monitor.attributes as unknown as MonitorFields;
462+
return formatHeartbeatRequest({
463+
monitor: normalizeSecrets(monitor).attributes,
464+
monitorId: monitor.id,
465+
customHeartbeatId: attributes[ConfigKey.CUSTOM_HEARTBEAT_ID],
466+
});
467+
})
468+
);
469+
}
479470
}
480471

481472
formatConfigs(configs: SyntheticsMonitorWithId[]) {

0 commit comments

Comments
 (0)