@oneuptime/common 10.5.23 → 10.5.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -32,16 +32,6 @@ import { LIMIT_PER_PROJECT } from "../../../Types/Database/LimitMax";
32
32
  export default class MonitorResourceUtil {
33
33
  static async monitorResource(dataToProcess) {
34
34
  var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
35
- let mutex = null;
36
- try {
37
- mutex = await Semaphore.lock({
38
- key: dataToProcess.monitorId.toString(),
39
- namespace: "MonitorResourceUtil.monitorResource",
40
- });
41
- }
42
- catch (err) {
43
- logger.error(err);
44
- }
45
35
  let response = {
46
36
  monitorId: dataToProcess.monitorId,
47
37
  criteriaMetId: undefined,
@@ -124,458 +114,486 @@ export default class MonitorResourceUtil {
124
114
  logger.debug(`${dataToProcess.monitorId.toString()} Monitor is disabled because one of the scheduled maintenance event this monitor is attached to has not ended. Please end the scheduled maintenance event to start monitoring again.`);
125
115
  throw new BadDataException("Monitor is disabled because one of the scheduled maintenance event this monitor is attached to has not ended. Please end the scheduled maintenance event to start monitoring again.");
126
116
  }
127
- let probeName = undefined;
128
- const monitorName = monitor.name || undefined;
129
- // save the last log to MonitorProbe.
130
- // get last log. We do this because there are many monitoring steps and we need to store those.
131
- logger.debug(`${dataToProcess.monitorId.toString()} - monitor type ${monitor.monitorType}`);
132
- if (monitor.monitorType &&
133
- MonitorTypeHelper.isProbableMonitor(monitor.monitorType)) {
134
- dataToProcess = dataToProcess;
135
- if (dataToProcess.probeId) {
136
- const monitorProbe = await MonitorProbeService.findOneBy({
137
- query: {
138
- monitorId: monitor.id,
139
- probeId: dataToProcess.probeId,
140
- },
141
- select: {
142
- lastMonitoringLog: true,
143
- probe: {
144
- name: true,
145
- },
146
- },
147
- props: {
148
- isRoot: true,
149
- },
150
- });
151
- if (!monitorProbe) {
152
- throw new BadDataException("Probe is not assigned to this monitor");
117
+ /*
118
+ * Acquire a per-monitor lock so concurrent results for the same monitor are
119
+ * processed serially. This MUST come after the validation above: acquiring
120
+ * before the not-found/disabled checks meant those throw paths held and
121
+ * leaked — the lock. redis-semaphore keeps a refresh timer alive until
122
+ * release() is called, so a leaked lock pinned the Redis key until pod
123
+ * restart and forced every other worker to spin on acquire. The try/finally
124
+ * below guarantees the lock is released on every exit path (return or
125
+ * throw); acquireTimeout/retryInterval cap the acquire spin so a contended
126
+ * lock fails fast instead of polling Redis for 10s.
127
+ */
128
+ let mutex = null;
129
+ try {
130
+ mutex = await Semaphore.lock({
131
+ key: dataToProcess.monitorId.toString(),
132
+ namespace: "MonitorResourceUtil.monitorResource",
133
+ acquireTimeout: 2000,
134
+ retryInterval: 100,
135
+ acquireAttemptsLimit: 20,
136
+ });
137
+ }
138
+ catch (err) {
139
+ logger.error(err);
140
+ }
141
+ const releaseMutex = async () => {
142
+ if (mutex) {
143
+ try {
144
+ await Semaphore.release(mutex);
153
145
  }
154
- probeName = ((_a = monitorProbe.probe) === null || _a === void 0 ? void 0 : _a.name) || undefined;
155
- await MonitorProbeService.updateOneBy({
156
- query: {
157
- monitorId: monitor.id,
158
- probeId: dataToProcess.probeId,
159
- },
160
- data: {
161
- lastMonitoringLog: Object.assign(Object.assign({}, (monitorProbe.lastMonitoringLog || {})), { [dataToProcess.monitorStepId.toString()]: Object.assign(Object.assign({}, JSON.parse(JSON.stringify(dataToProcess))), { monitoredAt: OneUptimeDate.getCurrentDate() }) }),
162
- },
163
- props: {
164
- isRoot: true,
165
- },
166
- });
146
+ catch (err) {
147
+ logger.error(err);
148
+ }
149
+ mutex = null;
167
150
  }
168
- }
169
- const serverMonitorResponse = monitor.monitorType === MonitorType.Server &&
170
- dataToProcess.requestReceivedAt
171
- ? dataToProcess
172
- : undefined;
173
- const incomingMonitorRequest = monitor.monitorType === MonitorType.IncomingRequest &&
174
- dataToProcess.incomingRequestReceivedAt &&
175
- !dataToProcess
176
- .onlyCheckForIncomingRequestReceivedAt
177
- ? dataToProcess
178
- : undefined;
179
- let hasPersistedMonitorData = false;
180
- const persistLatestMonitorPayload = async () => {
181
- if (hasPersistedMonitorData) {
182
- return;
151
+ };
152
+ try {
153
+ let probeName = undefined;
154
+ const monitorName = monitor.name || undefined;
155
+ // save the last log to MonitorProbe.
156
+ // get last log. We do this because there are many monitoring steps and we need to store those.
157
+ logger.debug(`${dataToProcess.monitorId.toString()} - monitor type ${monitor.monitorType}`);
158
+ if (monitor.monitorType &&
159
+ MonitorTypeHelper.isProbableMonitor(monitor.monitorType)) {
160
+ dataToProcess = dataToProcess;
161
+ if (dataToProcess.probeId) {
162
+ const monitorProbe = await MonitorProbeService.findOneBy({
163
+ query: {
164
+ monitorId: monitor.id,
165
+ probeId: dataToProcess.probeId,
166
+ },
167
+ select: {
168
+ lastMonitoringLog: true,
169
+ probe: {
170
+ name: true,
171
+ },
172
+ },
173
+ props: {
174
+ isRoot: true,
175
+ },
176
+ });
177
+ if (!monitorProbe) {
178
+ throw new BadDataException("Probe is not assigned to this monitor");
179
+ }
180
+ probeName = ((_a = monitorProbe.probe) === null || _a === void 0 ? void 0 : _a.name) || undefined;
181
+ await MonitorProbeService.updateOneBy({
182
+ query: {
183
+ monitorId: monitor.id,
184
+ probeId: dataToProcess.probeId,
185
+ },
186
+ data: {
187
+ lastMonitoringLog: Object.assign(Object.assign({}, (monitorProbe.lastMonitoringLog || {})), { [dataToProcess.monitorStepId.toString()]: Object.assign(Object.assign({}, JSON.parse(JSON.stringify(dataToProcess))), { monitoredAt: OneUptimeDate.getCurrentDate() }) }),
188
+ },
189
+ props: {
190
+ isRoot: true,
191
+ },
192
+ });
193
+ }
183
194
  }
184
- if (serverMonitorResponse) {
185
- logger.debug(`${dataToProcess.monitorId.toString()} - Server request received at ${serverMonitorResponse.requestReceivedAt}`);
186
- logger.debug(dataToProcess);
187
- /*
188
- * Skip persistence when this evaluation originated from the
189
- * CheckOnlineStatus cron (onlyCheckRequestReceivedAt=true). The cron
190
- * re-evaluates using the already-stale value read from the DB and has
191
- * no new heartbeat data to persist — writing it back would race with
192
- * (and overwrite) the ingest path's fresh heartbeat update, causing
193
- * the monitor to flap between Online and Offline every minute.
194
- */
195
- if (!serverMonitorResponse.onlyCheckRequestReceivedAt) {
195
+ const serverMonitorResponse = monitor.monitorType === MonitorType.Server &&
196
+ dataToProcess.requestReceivedAt
197
+ ? dataToProcess
198
+ : undefined;
199
+ const incomingMonitorRequest = monitor.monitorType === MonitorType.IncomingRequest &&
200
+ dataToProcess.incomingRequestReceivedAt &&
201
+ !dataToProcess
202
+ .onlyCheckForIncomingRequestReceivedAt
203
+ ? dataToProcess
204
+ : undefined;
205
+ let hasPersistedMonitorData = false;
206
+ const persistLatestMonitorPayload = async () => {
207
+ if (hasPersistedMonitorData) {
208
+ return;
209
+ }
210
+ if (serverMonitorResponse) {
211
+ logger.debug(`${dataToProcess.monitorId.toString()} - Server request received at ${serverMonitorResponse.requestReceivedAt}`);
212
+ logger.debug(dataToProcess);
213
+ /*
214
+ * Skip persistence when this evaluation originated from the
215
+ * CheckOnlineStatus cron (onlyCheckRequestReceivedAt=true). The cron
216
+ * re-evaluates using the already-stale value read from the DB and has
217
+ * no new heartbeat data to persist — writing it back would race with
218
+ * (and overwrite) the ingest path's fresh heartbeat update, causing
219
+ * the monitor to flap between Online and Offline every minute.
220
+ */
221
+ if (!serverMonitorResponse.onlyCheckRequestReceivedAt) {
222
+ await MonitorService.updateOneById({
223
+ id: monitor.id,
224
+ data: {
225
+ serverMonitorRequestReceivedAt: serverMonitorResponse.requestReceivedAt,
226
+ serverMonitorResponse,
227
+ },
228
+ props: {
229
+ isRoot: true,
230
+ ignoreHooks: true,
231
+ },
232
+ });
233
+ logger.debug(`${dataToProcess.monitorId.toString()} - Monitor Server Response Updated`);
234
+ }
235
+ else {
236
+ logger.debug(`${dataToProcess.monitorId.toString()} - Skipping Monitor Server Response persist (cron re-evaluation).`);
237
+ }
238
+ }
239
+ if (incomingMonitorRequest) {
240
+ logger.debug(`${dataToProcess.monitorId.toString()} - Incoming request received at ${incomingMonitorRequest.incomingRequestReceivedAt}`);
196
241
  await MonitorService.updateOneById({
197
242
  id: monitor.id,
198
243
  data: {
199
- serverMonitorRequestReceivedAt: serverMonitorResponse.requestReceivedAt,
200
- serverMonitorResponse,
244
+ incomingRequestMonitorHeartbeatCheckedAt: OneUptimeDate.getCurrentDate(),
245
+ incomingMonitorRequest: JSON.parse(JSON.stringify(incomingMonitorRequest)),
201
246
  },
202
247
  props: {
203
248
  isRoot: true,
204
249
  ignoreHooks: true,
205
250
  },
206
251
  });
207
- logger.debug(`${dataToProcess.monitorId.toString()} - Monitor Server Response Updated`);
208
- }
209
- else {
210
- logger.debug(`${dataToProcess.monitorId.toString()} - Skipping Monitor Server Response persist (cron re-evaluation).`);
252
+ logger.debug(`${dataToProcess.monitorId.toString()} - Monitor Incoming Request Updated`);
211
253
  }
254
+ hasPersistedMonitorData = true;
255
+ };
256
+ logger.debug(`${dataToProcess.monitorId.toString()} - Saving monitor metrics`);
257
+ try {
258
+ await MonitorMetricUtil.saveMonitorMetrics({
259
+ monitorId: monitor.id,
260
+ projectId: monitor.projectId,
261
+ dataToProcess: dataToProcess,
262
+ probeName: probeName || undefined,
263
+ monitorName: monitorName || undefined,
264
+ });
212
265
  }
213
- if (incomingMonitorRequest) {
214
- logger.debug(`${dataToProcess.monitorId.toString()} - Incoming request received at ${incomingMonitorRequest.incomingRequestReceivedAt}`);
215
- await MonitorService.updateOneById({
216
- id: monitor.id,
217
- data: {
218
- incomingRequestMonitorHeartbeatCheckedAt: OneUptimeDate.getCurrentDate(),
219
- incomingMonitorRequest: JSON.parse(JSON.stringify(incomingMonitorRequest)),
220
- },
221
- props: {
222
- isRoot: true,
223
- ignoreHooks: true,
224
- },
266
+ catch (err) {
267
+ logger.error("Unable to save metrics");
268
+ logger.error(err);
269
+ }
270
+ logger.debug(`${dataToProcess.monitorId.toString()} - Monitor metrics saved`);
271
+ const monitorSteps = monitor.monitorSteps;
272
+ if (!((_b = monitorSteps.data) === null || _b === void 0 ? void 0 : _b.monitorStepsInstanceArray) ||
273
+ ((_c = monitorSteps.data) === null || _c === void 0 ? void 0 : _c.monitorStepsInstanceArray.length) === 0) {
274
+ logger.debug(`${dataToProcess.monitorId.toString()} - No monitoring steps.`);
275
+ await persistLatestMonitorPayload();
276
+ MonitorLogUtil.saveMonitorLog({
277
+ monitorId: monitor.id,
278
+ projectId: monitor.projectId,
279
+ dataToProcess: dataToProcess,
225
280
  });
226
- logger.debug(`${dataToProcess.monitorId.toString()} - Monitor Incoming Request Updated`);
281
+ return response;
227
282
  }
228
- hasPersistedMonitorData = true;
229
- };
230
- logger.debug(`${dataToProcess.monitorId.toString()} - Saving monitor metrics`);
231
- try {
232
- await MonitorMetricUtil.saveMonitorMetrics({
233
- monitorId: monitor.id,
234
- projectId: monitor.projectId,
235
- dataToProcess: dataToProcess,
236
- probeName: probeName || undefined,
237
- monitorName: monitorName || undefined,
238
- });
239
- }
240
- catch (err) {
241
- logger.error("Unable to save metrics");
242
- logger.error(err);
243
- }
244
- logger.debug(`${dataToProcess.monitorId.toString()} - Monitor metrics saved`);
245
- const monitorSteps = monitor.monitorSteps;
246
- if (!((_b = monitorSteps.data) === null || _b === void 0 ? void 0 : _b.monitorStepsInstanceArray) ||
247
- ((_c = monitorSteps.data) === null || _c === void 0 ? void 0 : _c.monitorStepsInstanceArray.length) === 0) {
248
- logger.debug(`${dataToProcess.monitorId.toString()} - No monitoring steps.`);
249
- await persistLatestMonitorPayload();
250
- MonitorLogUtil.saveMonitorLog({
251
- monitorId: monitor.id,
252
- projectId: monitor.projectId,
253
- dataToProcess: dataToProcess,
254
- });
255
- return response;
256
- }
257
- logger.debug(`${dataToProcess.monitorId.toString()} - Auto resolving criteria instances.`);
258
- const criteriaInstances = monitorSteps.data.monitorStepsInstanceArray
259
- .map((step) => {
260
- var _a;
261
- return (_a = step.data) === null || _a === void 0 ? void 0 : _a.monitorCriteria;
262
- })
263
- .filter((criteria) => {
264
- return Boolean(criteria);
265
- })
266
- .map((criteria) => {
267
- var _a;
268
- return [...(((_a = criteria === null || criteria === void 0 ? void 0 : criteria.data) === null || _a === void 0 ? void 0 : _a.monitorCriteriaInstanceArray) || [])];
269
- })
270
- .flat();
271
- const autoResolveCriteriaInstanceIdIncidentIdsDictionary = {};
272
- const criteriaInstanceMap = {};
273
- for (const criteriaInstance of criteriaInstances) {
274
- criteriaInstanceMap[((_d = criteriaInstance.data) === null || _d === void 0 ? void 0 : _d.id) || ""] = criteriaInstance;
275
- if (((_e = criteriaInstance.data) === null || _e === void 0 ? void 0 : _e.incidents) &&
276
- ((_f = criteriaInstance.data) === null || _f === void 0 ? void 0 : _f.incidents.length) > 0) {
277
- for (const incidentTemplate of criteriaInstance.data.incidents) {
278
- if (incidentTemplate.autoResolveIncident) {
279
- if (!autoResolveCriteriaInstanceIdIncidentIdsDictionary[criteriaInstance.data.id.toString()]) {
280
- autoResolveCriteriaInstanceIdIncidentIdsDictionary[criteriaInstance.data.id.toString()] = [];
283
+ logger.debug(`${dataToProcess.monitorId.toString()} - Auto resolving criteria instances.`);
284
+ const criteriaInstances = monitorSteps.data.monitorStepsInstanceArray
285
+ .map((step) => {
286
+ var _a;
287
+ return (_a = step.data) === null || _a === void 0 ? void 0 : _a.monitorCriteria;
288
+ })
289
+ .filter((criteria) => {
290
+ return Boolean(criteria);
291
+ })
292
+ .map((criteria) => {
293
+ var _a;
294
+ return [...(((_a = criteria === null || criteria === void 0 ? void 0 : criteria.data) === null || _a === void 0 ? void 0 : _a.monitorCriteriaInstanceArray) || [])];
295
+ })
296
+ .flat();
297
+ const autoResolveCriteriaInstanceIdIncidentIdsDictionary = {};
298
+ const criteriaInstanceMap = {};
299
+ for (const criteriaInstance of criteriaInstances) {
300
+ criteriaInstanceMap[((_d = criteriaInstance.data) === null || _d === void 0 ? void 0 : _d.id) || ""] = criteriaInstance;
301
+ if (((_e = criteriaInstance.data) === null || _e === void 0 ? void 0 : _e.incidents) &&
302
+ ((_f = criteriaInstance.data) === null || _f === void 0 ? void 0 : _f.incidents.length) > 0) {
303
+ for (const incidentTemplate of criteriaInstance.data.incidents) {
304
+ if (incidentTemplate.autoResolveIncident) {
305
+ if (!autoResolveCriteriaInstanceIdIncidentIdsDictionary[criteriaInstance.data.id.toString()]) {
306
+ autoResolveCriteriaInstanceIdIncidentIdsDictionary[criteriaInstance.data.id.toString()] = [];
307
+ }
308
+ (_g = autoResolveCriteriaInstanceIdIncidentIdsDictionary[criteriaInstance.data.id.toString()]) === null || _g === void 0 ? void 0 : _g.push(incidentTemplate.id);
281
309
  }
282
- (_g = autoResolveCriteriaInstanceIdIncidentIdsDictionary[criteriaInstance.data.id.toString()]) === null || _g === void 0 ? void 0 : _g.push(incidentTemplate.id);
283
310
  }
284
311
  }
285
312
  }
286
- }
287
- // alerts.
288
- const autoResolveCriteriaInstanceIdAlertIdsDictionary = {};
289
- const criteriaInstanceAlertMap = {};
290
- for (const criteriaInstance of criteriaInstances) {
291
- criteriaInstanceAlertMap[((_h = criteriaInstance.data) === null || _h === void 0 ? void 0 : _h.id) || ""] =
292
- criteriaInstance;
293
- if (((_j = criteriaInstance.data) === null || _j === void 0 ? void 0 : _j.alerts) &&
294
- ((_k = criteriaInstance.data) === null || _k === void 0 ? void 0 : _k.alerts.length) > 0) {
295
- for (const alertTemplate of criteriaInstance.data.alerts) {
296
- if (alertTemplate.autoResolveAlert) {
297
- if (!autoResolveCriteriaInstanceIdAlertIdsDictionary[criteriaInstance.data.id.toString()]) {
298
- autoResolveCriteriaInstanceIdAlertIdsDictionary[criteriaInstance.data.id.toString()] = [];
313
+ // alerts.
314
+ const autoResolveCriteriaInstanceIdAlertIdsDictionary = {};
315
+ const criteriaInstanceAlertMap = {};
316
+ for (const criteriaInstance of criteriaInstances) {
317
+ criteriaInstanceAlertMap[((_h = criteriaInstance.data) === null || _h === void 0 ? void 0 : _h.id) || ""] =
318
+ criteriaInstance;
319
+ if (((_j = criteriaInstance.data) === null || _j === void 0 ? void 0 : _j.alerts) &&
320
+ ((_k = criteriaInstance.data) === null || _k === void 0 ? void 0 : _k.alerts.length) > 0) {
321
+ for (const alertTemplate of criteriaInstance.data.alerts) {
322
+ if (alertTemplate.autoResolveAlert) {
323
+ if (!autoResolveCriteriaInstanceIdAlertIdsDictionary[criteriaInstance.data.id.toString()]) {
324
+ autoResolveCriteriaInstanceIdAlertIdsDictionary[criteriaInstance.data.id.toString()] = [];
325
+ }
326
+ (_l = autoResolveCriteriaInstanceIdAlertIdsDictionary[criteriaInstance.data.id.toString()]) === null || _l === void 0 ? void 0 : _l.push(alertTemplate.id);
299
327
  }
300
- (_l = autoResolveCriteriaInstanceIdAlertIdsDictionary[criteriaInstance.data.id.toString()]) === null || _l === void 0 ? void 0 : _l.push(alertTemplate.id);
301
328
  }
302
329
  }
303
330
  }
304
- }
305
- const monitorStep = monitorSteps.data.monitorStepsInstanceArray[0];
306
- logger.debug(`Monitor Step: ${monitorStep ? monitorStep.id : "undefined"}`);
307
- if (dataToProcess.monitorStepId) {
308
- monitorSteps.data.monitorStepsInstanceArray.find((monitorStep) => {
309
- return (monitorStep.id.toString() ===
310
- dataToProcess.monitorStepId.toString());
311
- });
312
- logger.debug(`Found Monitor Step ID: ${dataToProcess.monitorStepId}`);
313
- }
314
- if (!monitorStep) {
315
- logger.debug("No steps found, ignoring everything.");
316
- await persistLatestMonitorPayload();
317
- MonitorLogUtil.saveMonitorLog({
318
- monitorId: monitor.id,
319
- projectId: monitor.projectId,
320
- dataToProcess: dataToProcess,
321
- });
322
- return response;
323
- }
324
- // now process the monitor step
325
- response.ingestedMonitorStepId = monitorStep.id;
326
- logger.debug(`Ingested Monitor Step ID: ${monitorStep.id}`);
327
- //find next monitor step after this one.
328
- const nextMonitorStepIndex = monitorSteps.data.monitorStepsInstanceArray.findIndex((step) => {
329
- return step.id.toString() === monitorStep.id.toString();
330
- });
331
- response.nextMonitorStepId =
332
- (_m = monitorSteps.data.monitorStepsInstanceArray[nextMonitorStepIndex + 1]) === null || _m === void 0 ? void 0 : _m.id;
333
- logger.debug(`Next Monitor Step ID: ${response.nextMonitorStepId}`);
334
- // now process probe response monitors
335
- logger.debug(`${dataToProcess.monitorId.toString()} - Processing monitor step...`);
336
- response = await MonitorCriteriaEvaluator.processMonitorStep({
337
- dataToProcess: dataToProcess,
338
- monitorStep: monitorStep,
339
- monitor: monitor,
340
- probeApiIngestResponse: response,
341
- evaluationSummary: evaluationSummary,
342
- });
343
- // Check probe agreement for probe-based monitors
344
- if (monitor.monitorType &&
345
- MonitorTypeHelper.isProbableMonitor(monitor.monitorType)) {
346
- const probeAgreementResult = await MonitorResourceUtil.checkProbeAgreement({
347
- monitor: monitor,
348
- monitorStep: monitorStep,
349
- currentCriteriaMetId: response.criteriaMetId || null,
350
- currentRootCause: response.rootCause || null,
351
- });
352
- // Add probe agreement event to evaluation summary
353
- evaluationSummary.events.push({
354
- type: "probe-agreement",
355
- title: "Probe Agreement Check",
356
- message: probeAgreementResult.hasAgreement
357
- ? `Probe agreement reached: ${probeAgreementResult.agreementCount}/${probeAgreementResult.requiredCount} probes agree (${probeAgreementResult.totalActiveProbes} active probes total).`
358
- : `Probe agreement not reached: ${probeAgreementResult.agreementCount}/${probeAgreementResult.requiredCount} probes agree (${probeAgreementResult.totalActiveProbes} active probes total). Skipping status change.`,
359
- at: OneUptimeDate.getCurrentDate(),
360
- });
361
- if (!probeAgreementResult.hasAgreement) {
362
- logger.debug(`${dataToProcess.monitorId.toString()} - Probe agreement not met. ${probeAgreementResult.agreementCount}/${probeAgreementResult.requiredCount} probes agree. Skipping status change.`);
363
- // Release lock and return early - no status change
364
- if (mutex) {
365
- try {
366
- await Semaphore.release(mutex);
367
- }
368
- catch (err) {
369
- logger.error(err);
370
- }
371
- }
331
+ const monitorStep = monitorSteps.data.monitorStepsInstanceArray[0];
332
+ logger.debug(`Monitor Step: ${monitorStep ? monitorStep.id : "undefined"}`);
333
+ if (dataToProcess.monitorStepId) {
334
+ monitorSteps.data.monitorStepsInstanceArray.find((monitorStep) => {
335
+ return (monitorStep.id.toString() ===
336
+ dataToProcess.monitorStepId.toString());
337
+ });
338
+ logger.debug(`Found Monitor Step ID: ${dataToProcess.monitorStepId}`);
339
+ }
340
+ if (!monitorStep) {
341
+ logger.debug("No steps found, ignoring everything.");
372
342
  await persistLatestMonitorPayload();
373
343
  MonitorLogUtil.saveMonitorLog({
374
344
  monitorId: monitor.id,
375
345
  projectId: monitor.projectId,
376
346
  dataToProcess: dataToProcess,
377
347
  });
378
- response.evaluationSummary = evaluationSummary;
379
348
  return response;
380
349
  }
381
- // Use the agreed criteria result
382
- response.criteriaMetId = probeAgreementResult.agreedCriteriaId
383
- ? probeAgreementResult.agreedCriteriaId
384
- : undefined;
385
- response.rootCause = probeAgreementResult.agreedRootCause;
386
- // Add probe names in agreement to the root cause
387
- if (response.rootCause &&
388
- probeAgreementResult.agreedProbeNames.length > 0) {
389
- response.rootCause += `
390
- **Probes in Agreement**: ${probeAgreementResult.agreedProbeNames.join(", ")}
391
- `;
392
- }
393
- }
394
- if (response.criteriaMetId && response.rootCause) {
395
- logger.debug(`${dataToProcess.monitorId.toString()} - Criteria met: ${response.criteriaMetId}`);
396
- logger.debug(`${dataToProcess.monitorId.toString()} - Root cause: ${response.rootCause}`);
397
- let telemetryQuery = undefined;
398
- if (dataToProcess && dataToProcess.logQuery) {
399
- telemetryQuery = {
400
- telemetryQuery: dataToProcess.logQuery,
401
- telemetryType: TelemetryType.Log,
402
- metricViewData: null,
403
- };
404
- logger.debug(`${dataToProcess.monitorId.toString()} - Log query found.`);
405
- }
406
- if (dataToProcess && dataToProcess.spanQuery) {
407
- telemetryQuery = {
408
- telemetryQuery: dataToProcess.spanQuery,
409
- telemetryType: TelemetryType.Trace,
410
- metricViewData: null,
411
- };
412
- logger.debug(`${dataToProcess.monitorId.toString()} - Span query found.`);
413
- }
414
- if (dataToProcess &&
415
- dataToProcess.metricViewConfig &&
416
- dataToProcess.startAndEndDate) {
417
- telemetryQuery = {
418
- telemetryQuery: null,
419
- telemetryType: TelemetryType.Metric,
420
- metricViewData: {
421
- startAndEndDate: dataToProcess.startAndEndDate || null,
422
- queryConfigs: dataToProcess
423
- .metricViewConfig.queryConfigs,
424
- formulaConfigs: dataToProcess
425
- .metricViewConfig.formulaConfigs,
426
- },
427
- };
428
- logger.debug(`${dataToProcess.monitorId.toString()} - Span query found.`);
429
- }
430
- if (dataToProcess &&
431
- dataToProcess.exceptionQuery) {
432
- const exceptionResponse = dataToProcess;
433
- telemetryQuery = {
434
- telemetryQuery: exceptionResponse.exceptionQuery,
435
- telemetryType: TelemetryType.Exception,
436
- metricViewData: null,
437
- };
438
- logger.debug(`${dataToProcess.monitorId.toString()} - Exception query found.`);
439
- }
440
- const matchedCriteriaInstance = criteriaInstanceMap[response.criteriaMetId];
441
- const monitorStatusTimelineChange = await MonitorStatusTimelineUtil.updateMonitorStatusTimeline({
442
- monitor: monitor,
443
- rootCause: response.rootCause,
350
+ // now process the monitor step
351
+ response.ingestedMonitorStepId = monitorStep.id;
352
+ logger.debug(`Ingested Monitor Step ID: ${monitorStep.id}`);
353
+ //find next monitor step after this one.
354
+ const nextMonitorStepIndex = monitorSteps.data.monitorStepsInstanceArray.findIndex((step) => {
355
+ return step.id.toString() === monitorStep.id.toString();
356
+ });
357
+ response.nextMonitorStepId =
358
+ (_m = monitorSteps.data.monitorStepsInstanceArray[nextMonitorStepIndex + 1]) === null || _m === void 0 ? void 0 : _m.id;
359
+ logger.debug(`Next Monitor Step ID: ${response.nextMonitorStepId}`);
360
+ // now process probe response monitors
361
+ logger.debug(`${dataToProcess.monitorId.toString()} - Processing monitor step...`);
362
+ response = await MonitorCriteriaEvaluator.processMonitorStep({
444
363
  dataToProcess: dataToProcess,
445
- criteriaInstance: matchedCriteriaInstance,
446
- props: {
447
- telemetryQuery: telemetryQuery,
448
- },
364
+ monitorStep: monitorStep,
365
+ monitor: monitor,
366
+ probeApiIngestResponse: response,
367
+ evaluationSummary: evaluationSummary,
449
368
  });
450
- if (monitorStatusTimelineChange) {
451
- const changedStatusName = await getMonitorStatusName(((_o = matchedCriteriaInstance.data) === null || _o === void 0 ? void 0 : _o.monitorStatusId) ||
452
- monitorStatusTimelineChange.monitorStatusId);
369
+ // Check probe agreement for probe-based monitors
370
+ if (monitor.monitorType &&
371
+ MonitorTypeHelper.isProbableMonitor(monitor.monitorType)) {
372
+ const probeAgreementResult = await MonitorResourceUtil.checkProbeAgreement({
373
+ monitor: monitor,
374
+ monitorStep: monitorStep,
375
+ currentCriteriaMetId: response.criteriaMetId || null,
376
+ currentRootCause: response.rootCause || null,
377
+ });
378
+ // Add probe agreement event to evaluation summary
453
379
  evaluationSummary.events.push({
454
- type: "monitor-status-changed",
455
- title: "Monitor status updated",
456
- message: changedStatusName
457
- ? `Monitor status changed to "${changedStatusName}" because criteria "${((_p = matchedCriteriaInstance.data) === null || _p === void 0 ? void 0 : _p.name) || "Unnamed criteria"}" was met.`
458
- : `Monitor status changed because criteria "${((_q = matchedCriteriaInstance.data) === null || _q === void 0 ? void 0 : _q.name) || "Unnamed criteria"}" was met.`,
459
- relatedCriteriaId: (_r = matchedCriteriaInstance.data) === null || _r === void 0 ? void 0 : _r.id,
380
+ type: "probe-agreement",
381
+ title: "Probe Agreement Check",
382
+ message: probeAgreementResult.hasAgreement
383
+ ? `Probe agreement reached: ${probeAgreementResult.agreementCount}/${probeAgreementResult.requiredCount} probes agree (${probeAgreementResult.totalActiveProbes} active probes total).`
384
+ : `Probe agreement not reached: ${probeAgreementResult.agreementCount}/${probeAgreementResult.requiredCount} probes agree (${probeAgreementResult.totalActiveProbes} active probes total). Skipping status change.`,
460
385
  at: OneUptimeDate.getCurrentDate(),
461
386
  });
387
+ if (!probeAgreementResult.hasAgreement) {
388
+ logger.debug(`${dataToProcess.monitorId.toString()} - Probe agreement not met. ${probeAgreementResult.agreementCount}/${probeAgreementResult.requiredCount} probes agree. Skipping status change.`);
389
+ // Release lock and return early - no status change
390
+ await releaseMutex();
391
+ await persistLatestMonitorPayload();
392
+ MonitorLogUtil.saveMonitorLog({
393
+ monitorId: monitor.id,
394
+ projectId: monitor.projectId,
395
+ dataToProcess: dataToProcess,
396
+ });
397
+ response.evaluationSummary = evaluationSummary;
398
+ return response;
399
+ }
400
+ // Use the agreed criteria result
401
+ response.criteriaMetId = probeAgreementResult.agreedCriteriaId
402
+ ? probeAgreementResult.agreedCriteriaId
403
+ : undefined;
404
+ response.rootCause = probeAgreementResult.agreedRootCause;
405
+ // Add probe names in agreement to the root cause
406
+ if (response.rootCause &&
407
+ probeAgreementResult.agreedProbeNames.length > 0) {
408
+ response.rootCause += `
409
+ **Probes in Agreement**: ${probeAgreementResult.agreedProbeNames.join(", ")}
410
+ `;
411
+ }
462
412
  }
463
- await MonitorIncident.criteriaMetCreateIncidentsAndUpdateMonitorStatus({
464
- monitor: monitor,
465
- rootCause: response.rootCause,
466
- dataToProcess: dataToProcess,
467
- autoResolveCriteriaInstanceIdIncidentIdsDictionary,
468
- criteriaInstance: matchedCriteriaInstance,
469
- evaluationSummary: evaluationSummary,
470
- props: {
471
- telemetryQuery: telemetryQuery,
472
- },
473
- matchesPerSeries: response.perSeriesMatches,
474
- });
475
- await MonitorAlert.criteriaMetCreateAlertsAndUpdateMonitorStatus({
476
- monitor: monitor,
477
- rootCause: response.rootCause,
478
- dataToProcess: dataToProcess,
479
- autoResolveCriteriaInstanceIdAlertIdsDictionary,
480
- criteriaInstance: criteriaInstanceAlertMap[response.criteriaMetId],
481
- evaluationSummary: evaluationSummary,
482
- props: {
483
- telemetryQuery: telemetryQuery,
484
- },
485
- matchesPerSeries: response.perSeriesMatches,
486
- });
487
- }
488
- else if (!response.criteriaMetId &&
489
- monitorSteps.data.defaultMonitorStatusId &&
490
- ((_s = monitor.currentMonitorStatusId) === null || _s === void 0 ? void 0 : _s.toString()) !==
491
- monitorSteps.data.defaultMonitorStatusId.toString()) {
492
- logger.debug(`${dataToProcess.monitorId.toString()} - No criteria met. Change to default status.`);
493
- await MonitorIncident.checkOpenIncidentsAndCloseIfResolved({
494
- monitorId: monitor.id,
495
- autoResolveCriteriaInstanceIdIncidentIdsDictionary,
496
- rootCause: "No monitoring criteria met. Change to default status.",
497
- criteriaInstance: null, // no criteria met!
498
- dataToProcess: dataToProcess,
499
- evaluationSummary: evaluationSummary,
500
- });
501
- await MonitorAlert.checkOpenAlertsAndCloseIfResolved({
502
- monitorId: monitor.id,
503
- autoResolveCriteriaInstanceIdAlertIdsDictionary,
504
- rootCause: "No monitoring criteria met. Change to default status.",
505
- criteriaInstance: null, // no criteria met!
506
- dataToProcess: dataToProcess,
507
- evaluationSummary: evaluationSummary,
508
- });
509
- // get last monitor status timeline.
510
- const lastMonitorStatusTimeline = await MonitorStatusTimelineService.findOneBy({
511
- query: {
512
- monitorId: monitor.id,
513
- projectId: monitor.projectId,
514
- },
515
- select: {
516
- _id: true,
517
- monitorStatusId: true,
518
- },
519
- sort: {
520
- startsAt: SortOrder.Descending,
521
- },
522
- props: {
523
- isRoot: true,
524
- },
525
- });
526
- if (lastMonitorStatusTimeline &&
527
- lastMonitorStatusTimeline.monitorStatusId &&
528
- lastMonitorStatusTimeline.monitorStatusId.toString() ===
529
- monitorSteps.data.defaultMonitorStatusId.toString()) {
530
- /*
531
- * status is same as last status. do not create new status timeline.
532
- * do nothing! status is same as last status.
533
- */
413
+ if (response.criteriaMetId && response.rootCause) {
414
+ logger.debug(`${dataToProcess.monitorId.toString()} - Criteria met: ${response.criteriaMetId}`);
415
+ logger.debug(`${dataToProcess.monitorId.toString()} - Root cause: ${response.rootCause}`);
416
+ let telemetryQuery = undefined;
417
+ if (dataToProcess && dataToProcess.logQuery) {
418
+ telemetryQuery = {
419
+ telemetryQuery: dataToProcess.logQuery,
420
+ telemetryType: TelemetryType.Log,
421
+ metricViewData: null,
422
+ };
423
+ logger.debug(`${dataToProcess.monitorId.toString()} - Log query found.`);
424
+ }
425
+ if (dataToProcess &&
426
+ dataToProcess.spanQuery) {
427
+ telemetryQuery = {
428
+ telemetryQuery: dataToProcess.spanQuery,
429
+ telemetryType: TelemetryType.Trace,
430
+ metricViewData: null,
431
+ };
432
+ logger.debug(`${dataToProcess.monitorId.toString()} - Span query found.`);
433
+ }
434
+ if (dataToProcess &&
435
+ dataToProcess.metricViewConfig &&
436
+ dataToProcess.startAndEndDate) {
437
+ telemetryQuery = {
438
+ telemetryQuery: null,
439
+ telemetryType: TelemetryType.Metric,
440
+ metricViewData: {
441
+ startAndEndDate: dataToProcess.startAndEndDate ||
442
+ null,
443
+ queryConfigs: dataToProcess
444
+ .metricViewConfig.queryConfigs,
445
+ formulaConfigs: dataToProcess
446
+ .metricViewConfig.formulaConfigs,
447
+ },
448
+ };
449
+ logger.debug(`${dataToProcess.monitorId.toString()} - Span query found.`);
450
+ }
451
+ if (dataToProcess &&
452
+ dataToProcess.exceptionQuery) {
453
+ const exceptionResponse = dataToProcess;
454
+ telemetryQuery = {
455
+ telemetryQuery: exceptionResponse.exceptionQuery,
456
+ telemetryType: TelemetryType.Exception,
457
+ metricViewData: null,
458
+ };
459
+ logger.debug(`${dataToProcess.monitorId.toString()} - Exception query found.`);
460
+ }
461
+ const matchedCriteriaInstance = criteriaInstanceMap[response.criteriaMetId];
462
+ const monitorStatusTimelineChange = await MonitorStatusTimelineUtil.updateMonitorStatusTimeline({
463
+ monitor: monitor,
464
+ rootCause: response.rootCause,
465
+ dataToProcess: dataToProcess,
466
+ criteriaInstance: matchedCriteriaInstance,
467
+ props: {
468
+ telemetryQuery: telemetryQuery,
469
+ },
470
+ });
471
+ if (monitorStatusTimelineChange) {
472
+ const changedStatusName = await getMonitorStatusName(((_o = matchedCriteriaInstance.data) === null || _o === void 0 ? void 0 : _o.monitorStatusId) ||
473
+ monitorStatusTimelineChange.monitorStatusId);
474
+ evaluationSummary.events.push({
475
+ type: "monitor-status-changed",
476
+ title: "Monitor status updated",
477
+ message: changedStatusName
478
+ ? `Monitor status changed to "${changedStatusName}" because criteria "${((_p = matchedCriteriaInstance.data) === null || _p === void 0 ? void 0 : _p.name) || "Unnamed criteria"}" was met.`
479
+ : `Monitor status changed because criteria "${((_q = matchedCriteriaInstance.data) === null || _q === void 0 ? void 0 : _q.name) || "Unnamed criteria"}" was met.`,
480
+ relatedCriteriaId: (_r = matchedCriteriaInstance.data) === null || _r === void 0 ? void 0 : _r.id,
481
+ at: OneUptimeDate.getCurrentDate(),
482
+ });
483
+ }
484
+ await MonitorIncident.criteriaMetCreateIncidentsAndUpdateMonitorStatus({
485
+ monitor: monitor,
486
+ rootCause: response.rootCause,
487
+ dataToProcess: dataToProcess,
488
+ autoResolveCriteriaInstanceIdIncidentIdsDictionary,
489
+ criteriaInstance: matchedCriteriaInstance,
490
+ evaluationSummary: evaluationSummary,
491
+ props: {
492
+ telemetryQuery: telemetryQuery,
493
+ },
494
+ matchesPerSeries: response.perSeriesMatches,
495
+ });
496
+ await MonitorAlert.criteriaMetCreateAlertsAndUpdateMonitorStatus({
497
+ monitor: monitor,
498
+ rootCause: response.rootCause,
499
+ dataToProcess: dataToProcess,
500
+ autoResolveCriteriaInstanceIdAlertIdsDictionary,
501
+ criteriaInstance: criteriaInstanceAlertMap[response.criteriaMetId],
502
+ evaluationSummary: evaluationSummary,
503
+ props: {
504
+ telemetryQuery: telemetryQuery,
505
+ },
506
+ matchesPerSeries: response.perSeriesMatches,
507
+ });
534
508
  }
535
- else {
536
- // if no criteria is met then update monitor to default state.
537
- const monitorStatusTimeline = new MonitorStatusTimeline();
538
- monitorStatusTimeline.monitorId = monitor.id;
539
- monitorStatusTimeline.monitorStatusId =
540
- monitorSteps.data.defaultMonitorStatusId;
541
- monitorStatusTimeline.projectId = monitor.projectId;
542
- monitorStatusTimeline.isOwnerNotified = true; // no need to notify owner as this is default status.
543
- monitorStatusTimeline.statusChangeLog = JSON.parse(JSON.stringify(dataToProcess));
544
- monitorStatusTimeline.rootCause =
545
- "No monitoring criteria met. Change to default status. ";
546
- await MonitorStatusTimelineService.create({
547
- data: monitorStatusTimeline,
509
+ else if (!response.criteriaMetId &&
510
+ monitorSteps.data.defaultMonitorStatusId &&
511
+ ((_s = monitor.currentMonitorStatusId) === null || _s === void 0 ? void 0 : _s.toString()) !==
512
+ monitorSteps.data.defaultMonitorStatusId.toString()) {
513
+ logger.debug(`${dataToProcess.monitorId.toString()} - No criteria met. Change to default status.`);
514
+ await MonitorIncident.checkOpenIncidentsAndCloseIfResolved({
515
+ monitorId: monitor.id,
516
+ autoResolveCriteriaInstanceIdIncidentIdsDictionary,
517
+ rootCause: "No monitoring criteria met. Change to default status.",
518
+ criteriaInstance: null, // no criteria met!
519
+ dataToProcess: dataToProcess,
520
+ evaluationSummary: evaluationSummary,
521
+ });
522
+ await MonitorAlert.checkOpenAlertsAndCloseIfResolved({
523
+ monitorId: monitor.id,
524
+ autoResolveCriteriaInstanceIdAlertIdsDictionary,
525
+ rootCause: "No monitoring criteria met. Change to default status.",
526
+ criteriaInstance: null, // no criteria met!
527
+ dataToProcess: dataToProcess,
528
+ evaluationSummary: evaluationSummary,
529
+ });
530
+ // get last monitor status timeline.
531
+ const lastMonitorStatusTimeline = await MonitorStatusTimelineService.findOneBy({
532
+ query: {
533
+ monitorId: monitor.id,
534
+ projectId: monitor.projectId,
535
+ },
536
+ select: {
537
+ _id: true,
538
+ monitorStatusId: true,
539
+ },
540
+ sort: {
541
+ startsAt: SortOrder.Descending,
542
+ },
548
543
  props: {
549
544
  isRoot: true,
550
545
  },
551
546
  });
552
- logger.debug(`${dataToProcess.monitorId.toString()} - Monitor status updated to default.`);
553
- const defaultStatusName = await getMonitorStatusName(monitorSteps.data.defaultMonitorStatusId);
554
- evaluationSummary.events.push({
555
- type: "monitor-status-changed",
556
- title: "Monitor status reverted",
557
- message: defaultStatusName
558
- ? `Monitor status reverted to "${defaultStatusName}" because no monitoring criteria were met.`
559
- : "Monitor status reverted to its default state because no monitoring criteria were met.",
560
- at: OneUptimeDate.getCurrentDate(),
561
- });
547
+ if (lastMonitorStatusTimeline &&
548
+ lastMonitorStatusTimeline.monitorStatusId &&
549
+ lastMonitorStatusTimeline.monitorStatusId.toString() ===
550
+ monitorSteps.data.defaultMonitorStatusId.toString()) {
551
+ /*
552
+ * status is same as last status. do not create new status timeline.
553
+ * do nothing! status is same as last status.
554
+ */
555
+ }
556
+ else {
557
+ // if no criteria is met then update monitor to default state.
558
+ const monitorStatusTimeline = new MonitorStatusTimeline();
559
+ monitorStatusTimeline.monitorId = monitor.id;
560
+ monitorStatusTimeline.monitorStatusId =
561
+ monitorSteps.data.defaultMonitorStatusId;
562
+ monitorStatusTimeline.projectId = monitor.projectId;
563
+ monitorStatusTimeline.isOwnerNotified = true; // no need to notify owner as this is default status.
564
+ monitorStatusTimeline.statusChangeLog = JSON.parse(JSON.stringify(dataToProcess));
565
+ monitorStatusTimeline.rootCause =
566
+ "No monitoring criteria met. Change to default status. ";
567
+ await MonitorStatusTimelineService.create({
568
+ data: monitorStatusTimeline,
569
+ props: {
570
+ isRoot: true,
571
+ },
572
+ });
573
+ logger.debug(`${dataToProcess.monitorId.toString()} - Monitor status updated to default.`);
574
+ const defaultStatusName = await getMonitorStatusName(monitorSteps.data.defaultMonitorStatusId);
575
+ evaluationSummary.events.push({
576
+ type: "monitor-status-changed",
577
+ title: "Monitor status reverted",
578
+ message: defaultStatusName
579
+ ? `Monitor status reverted to "${defaultStatusName}" because no monitoring criteria were met.`
580
+ : "Monitor status reverted to its default state because no monitoring criteria were met.",
581
+ at: OneUptimeDate.getCurrentDate(),
582
+ });
583
+ }
562
584
  }
585
+ await releaseMutex();
586
+ await persistLatestMonitorPayload();
587
+ MonitorLogUtil.saveMonitorLog({
588
+ monitorId: monitor.id,
589
+ projectId: monitor.projectId,
590
+ dataToProcess: dataToProcess,
591
+ });
592
+ return response;
563
593
  }
564
- if (mutex) {
565
- try {
566
- await Semaphore.release(mutex);
567
- }
568
- catch (err) {
569
- logger.error(err);
570
- }
594
+ finally {
595
+ await releaseMutex();
571
596
  }
572
- await persistLatestMonitorPayload();
573
- MonitorLogUtil.saveMonitorLog({
574
- monitorId: monitor.id,
575
- projectId: monitor.projectId,
576
- dataToProcess: dataToProcess,
577
- });
578
- return response;
579
597
  }
580
598
  static async checkProbeAgreement(input) {
581
599
  var _a, _b, _c, _d, _e, _f, _g;