@oneuptime/common 11.3.5 → 11.3.7

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.
Files changed (65) hide show
  1. package/Server/Infrastructure/Queue.ts +103 -16
  2. package/Server/Services/AlertService.ts +302 -202
  3. package/Server/Services/AnalyticsDatabaseService.ts +9 -5
  4. package/Server/Services/IncidentService.ts +410 -306
  5. package/Server/Services/TeamMemberService.ts +38 -1
  6. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +31 -0
  7. package/Server/Utils/Monitor/IncomingRequestIncidentGrouping.ts +428 -0
  8. package/Server/Utils/Monitor/MonitorAlert.ts +158 -8
  9. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +26 -0
  10. package/Server/Utils/Monitor/MonitorIncident.ts +147 -2
  11. package/Server/Utils/Monitor/MonitorResource.ts +74 -0
  12. package/Tests/Server/Utils/AnalyticsDatabase/ClusterAwareSchema.test.ts +2 -1
  13. package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +32 -0
  14. package/Tests/Server/Utils/Monitor/IncomingRequestIncidentGrouping.test.ts +389 -0
  15. package/Tests/Server/Utils/Monitor/SeriesAbsenceResolutionGuard.test.ts +177 -0
  16. package/Tests/Types/Events/Recurring.test.ts +475 -0
  17. package/Tests/Types/Monitor/CriteriaFilter.test.ts +348 -0
  18. package/Tests/Types/OnCallDutyPolicy/RestrictionTimes.test.ts +331 -0
  19. package/Tests/UI/Components/Alert.test.tsx +23 -15
  20. package/Tests/UI/Components/Breadcrumbs.test.tsx +18 -6
  21. package/Tests/UI/Components/Button.test.tsx +3 -3
  22. package/Tests/UI/Components/DictionaryFilterOperator.test.ts +174 -0
  23. package/Tests/UI/Components/EmptyState/EmptyState.test.tsx +4 -3
  24. package/Tests/UI/Components/Input.test.tsx +3 -2
  25. package/Tests/UI/Components/Pill.test.tsx +4 -2
  26. package/Tests/UI/Components/SideMenuItem.test.tsx +18 -8
  27. package/Tests/UI/Components/TextArea.test.tsx +6 -4
  28. package/Types/Monitor/IncomingMonitor/IncidentGroupingConfig.ts +74 -0
  29. package/Types/Monitor/MonitorCriteriaInstance.ts +22 -0
  30. package/UI/Components/Dictionary/Dictionary.tsx +123 -50
  31. package/UI/Components/Dictionary/DictionaryFilterOperator.ts +96 -1
  32. package/UI/Components/MasterPage/MasterPage.tsx +5 -3
  33. package/build/dist/Server/Infrastructure/Queue.js +83 -15
  34. package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
  35. package/build/dist/Server/Services/AlertService.js +255 -161
  36. package/build/dist/Server/Services/AlertService.js.map +1 -1
  37. package/build/dist/Server/Services/AnalyticsDatabaseService.js +9 -5
  38. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  39. package/build/dist/Server/Services/IncidentService.js +322 -236
  40. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  41. package/build/dist/Server/Services/TeamMemberService.js +37 -1
  42. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  43. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +26 -0
  44. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
  45. package/build/dist/Server/Utils/Monitor/IncomingRequestIncidentGrouping.js +288 -0
  46. package/build/dist/Server/Utils/Monitor/IncomingRequestIncidentGrouping.js.map +1 -0
  47. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +119 -8
  48. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  49. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +24 -0
  50. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  51. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +103 -2
  52. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  53. package/build/dist/Server/Utils/Monitor/MonitorResource.js +62 -0
  54. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  55. package/build/dist/Types/Monitor/IncomingMonitor/IncidentGroupingConfig.js +8 -0
  56. package/build/dist/Types/Monitor/IncomingMonitor/IncidentGroupingConfig.js.map +1 -0
  57. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +10 -0
  58. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  59. package/build/dist/UI/Components/Dictionary/Dictionary.js +60 -9
  60. package/build/dist/UI/Components/Dictionary/Dictionary.js.map +1 -1
  61. package/build/dist/UI/Components/Dictionary/DictionaryFilterOperator.js +69 -0
  62. package/build/dist/UI/Components/Dictionary/DictionaryFilterOperator.js.map +1 -1
  63. package/build/dist/UI/Components/MasterPage/MasterPage.js +5 -3
  64. package/build/dist/UI/Components/MasterPage/MasterPage.js.map +1 -1
  65. package/package.json +1 -1
@@ -88,8 +88,8 @@ export default class Queue {
88
88
  // Keep BullMQ data under control to avoid Redis bloat
89
89
  defaultJobOptions: {
90
90
  // keep only recent completed/failed jobs
91
- removeOnComplete: { count: 500 }, // keep last 1000 completed jobs
92
- removeOnFail: { count: 100 }, // keep last 500 failed jobs
91
+ removeOnComplete: { count: 500 }, // keep last 500 completed jobs
92
+ removeOnFail: { count: 100 }, // keep last 100 failed jobs
93
93
  },
94
94
  /*
95
95
  * Optionally cap the event stream length (supported in BullMQ >= v5)
@@ -112,24 +112,111 @@ export default class Queue {
112
112
  logger.error(err);
113
113
  });
114
114
 
115
- // Fire-and-forget initial cleanup for legacy/old data if not done before
116
- if (!this.cleanedQueueNames.has(queueName)) {
117
- this.cleanedQueueNames.add(queueName);
118
- // Clean jobs older than 1 days to reclaim memory from historic runs
119
- const oneDaysMs: number = 1 * 24 * 60 * 60 * 1000;
120
- void (async () => {
121
- try {
122
- await queue.clean(oneDaysMs, 1000, "completed");
123
- await queue.clean(oneDaysMs, 1000, "failed");
124
- } catch {
125
- // ignore cleanup errors to not impact normal flow
126
- }
127
- })();
128
- }
115
+ /*
116
+ * Lazy fallback for the startup sweep: the first time a queue is created in
117
+ * this process, kick off its once-per-process terminal-job cleanup. The
118
+ * eager path (cleanAllQueuesOnStartup, called at service boot) normally
119
+ * wins; whichever runs first is the only one to sweep, via the gate inside
120
+ * cleanQueueOnStartup(). Fire and forget so it never blocks enqueue.
121
+ */
122
+ void this.cleanQueueOnStartup(queueName);
129
123
 
130
124
  return queue;
131
125
  }
132
126
 
127
+ /**
128
+ * Eagerly sweeps every queue's terminal (completed/failed) jobs at process
129
+ * startup, so the Completed/Failed counts on the admin Health page reset on a
130
+ * pod (re)start regardless of when — or whether — each queue next produces a
131
+ * job. Call once during service boot (see App/Index.ts). Each queue is swept
132
+ * at most once per process via the cleanedQueueNames gate, so this is safe to
133
+ * call alongside the lazy getQueue() path and from any service role.
134
+ */
135
+ @CaptureSpan()
136
+ public static async cleanAllQueuesOnStartup(): Promise<void> {
137
+ await Promise.all(
138
+ Object.values(QueueName).map((queueName: QueueName) => {
139
+ return this.cleanQueueOnStartup(queueName);
140
+ }),
141
+ );
142
+ }
143
+
144
+ /*
145
+ * How old (in ms) a completed/failed job must be before the startup sweep
146
+ * removes it. 0 means "remove all terminal jobs on every restart", which
147
+ * resets the Completed/Failed counts on the admin Health page so operators
148
+ * stop seeing stale failures after a deploy/restart. Raise this (e.g. to
149
+ * 24 * 60 * 60 * 1000) if you'd rather keep recent failures around for
150
+ * crash-loop debugging.
151
+ */
152
+ private static readonly STARTUP_CLEANUP_GRACE_MS: number = 0;
153
+ // Number of jobs removed per clean() call; we loop until the state is drained.
154
+ private static readonly STARTUP_CLEANUP_BATCH_SIZE: number = 1000;
155
+ // Safety cap so a pathologically large backlog can't loop forever.
156
+ private static readonly STARTUP_CLEANUP_MAX_BATCHES: number = 1000;
157
+
158
+ /**
159
+ * Removes terminal (completed/failed) jobs for a single queue, at most once
160
+ * per process (gated by cleanedQueueNames). Drains in batches because
161
+ * BullMQ's clean() caps each call at `limit` removals. Leaves live states
162
+ * (waiting/active/delayed) untouched. Best-effort: any error is logged and
163
+ * swallowed so a transient Redis hiccup at boot never breaks queue setup.
164
+ */
165
+ private static async cleanQueueOnStartup(
166
+ queueName: QueueName,
167
+ ): Promise<void> {
168
+ /*
169
+ * Gate synchronously, before any await, so the lazy getQueue() trigger and
170
+ * the eager cleanAllQueuesOnStartup() call can't both sweep the same queue.
171
+ */
172
+ if (this.cleanedQueueNames.has(queueName)) {
173
+ return;
174
+ }
175
+ this.cleanedQueueNames.add(queueName);
176
+
177
+ const queue: BullQueue = this.getQueue(queueName);
178
+
179
+ const terminalStates: Array<"completed" | "failed"> = [
180
+ "completed",
181
+ "failed",
182
+ ];
183
+
184
+ for (const state of terminalStates) {
185
+ try {
186
+ let removedTotal: number = 0;
187
+
188
+ for (
189
+ let batch: number = 0;
190
+ batch < this.STARTUP_CLEANUP_MAX_BATCHES;
191
+ batch++
192
+ ) {
193
+ const removed: string[] = await queue.clean(
194
+ this.STARTUP_CLEANUP_GRACE_MS,
195
+ this.STARTUP_CLEANUP_BATCH_SIZE,
196
+ state,
197
+ );
198
+
199
+ removedTotal += removed.length;
200
+
201
+ // A short batch means the state is drained; stop looping.
202
+ if (removed.length < this.STARTUP_CLEANUP_BATCH_SIZE) {
203
+ break;
204
+ }
205
+ }
206
+
207
+ if (removedTotal > 0) {
208
+ logger.debug(
209
+ `Queue ${queueName}: removed ${removedTotal} ${state} job(s) on startup`,
210
+ );
211
+ }
212
+ } catch (err) {
213
+ // Ignore cleanup errors to not impact normal flow.
214
+ logger.debug(`Queue ${queueName}: startup ${state} cleanup failed`);
215
+ logger.debug(err);
216
+ }
217
+ }
218
+ }
219
+
133
220
  @CaptureSpan()
134
221
  public static async removeJob(
135
222
  queueName: QueueName,