@oneuptime/common 11.0.4 → 11.0.5
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.
- package/Server/Infrastructure/GlobalCache.ts +11 -0
- package/Server/Services/CephClusterService.ts +22 -10
- package/Server/Services/CloudResourceService.ts +22 -10
- package/Server/Services/DockerHostService.ts +22 -10
- package/Server/Services/DockerSwarmClusterService.ts +22 -10
- package/Server/Services/HostService.ts +22 -10
- package/Server/Services/KubernetesClusterService.ts +22 -10
- package/Server/Services/PodmanHostService.ts +22 -10
- package/Server/Services/ProxmoxClusterService.ts +22 -10
- package/Server/Services/RumApplicationService.ts +22 -10
- package/Server/Services/ServerlessFunctionService.ts +22 -10
- package/Tests/Server/Infrastructure/GlobalCache.test.ts +43 -0
- package/UI/Components/Charts/Area/AreaChart.tsx +4 -8
- package/UI/Components/Charts/Bar/BarChart.tsx +4 -8
- package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +4 -8
- package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +1 -1
- package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +1 -1
- package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +1 -1
- package/UI/Components/Charts/Line/LineChart.tsx +4 -8
- package/UI/Components/EditionLabel/EditionLabel.tsx +68 -16
- package/UI/Components/Modal/Modal.tsx +4 -1
- package/build/dist/Server/Infrastructure/GlobalCache.js +13 -0
- package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
- package/build/dist/Server/Services/CephClusterService.js +19 -2
- package/build/dist/Server/Services/CephClusterService.js.map +1 -1
- package/build/dist/Server/Services/CloudResourceService.js +19 -2
- package/build/dist/Server/Services/CloudResourceService.js.map +1 -1
- package/build/dist/Server/Services/DockerHostService.js +19 -2
- package/build/dist/Server/Services/DockerHostService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmClusterService.js +19 -2
- package/build/dist/Server/Services/DockerSwarmClusterService.js.map +1 -1
- package/build/dist/Server/Services/HostService.js +19 -2
- package/build/dist/Server/Services/HostService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterService.js +19 -2
- package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
- package/build/dist/Server/Services/PodmanHostService.js +19 -2
- package/build/dist/Server/Services/PodmanHostService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxClusterService.js +19 -2
- package/build/dist/Server/Services/ProxmoxClusterService.js.map +1 -1
- package/build/dist/Server/Services/RumApplicationService.js +19 -2
- package/build/dist/Server/Services/RumApplicationService.js.map +1 -1
- package/build/dist/Server/Services/ServerlessFunctionService.js +19 -2
- package/build/dist/Server/Services/ServerlessFunctionService.js.map +1 -1
- package/build/dist/UI/Components/Charts/Area/AreaChart.js +2 -6
- package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js +2 -6
- package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +8 -8
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js +2 -6
- package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +31 -5
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/UI/Components/Modal/Modal.js +1 -1
- package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
- package/package.json +1 -1
|
@@ -175,4 +175,15 @@ export default abstract class GlobalCache {
|
|
|
175
175
|
*/
|
|
176
176
|
await client.set(`${namespace}-${key}`, value, "EX", expiresInSeconds);
|
|
177
177
|
}
|
|
178
|
+
|
|
179
|
+
@CaptureSpan()
|
|
180
|
+
public static async deleteKey(namespace: string, key: string): Promise<void> {
|
|
181
|
+
const client: ClientType | null = Redis.getClient();
|
|
182
|
+
|
|
183
|
+
if (!client || !Redis.isConnected()) {
|
|
184
|
+
throw new DatabaseNotConnectedException("Cache is not connected");
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
await client.del(`${namespace}-${key}`);
|
|
188
|
+
}
|
|
178
189
|
}
|
|
@@ -200,21 +200,33 @@ export class Service extends DatabaseService<Model> {
|
|
|
200
200
|
)
|
|
201
201
|
.digest("hex");
|
|
202
202
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
cacheKey
|
|
206
|
-
|
|
203
|
+
let cached: string | null = null;
|
|
204
|
+
try {
|
|
205
|
+
cached = await GlobalCache.getString(LAST_SEEN_CACHE_NAMESPACE, cacheKey);
|
|
206
|
+
} catch {
|
|
207
|
+
/*
|
|
208
|
+
* Cache unavailable — fail open and refresh lastSeenAt anyway. A
|
|
209
|
+
* cache error must never skip the DB write below, otherwise the
|
|
210
|
+
* resource is wrongly marked "disconnected" while telemetry is
|
|
211
|
+
* still flowing. Mirrors shouldRunMaintenance's fail-open stance.
|
|
212
|
+
*/
|
|
213
|
+
cached = null;
|
|
214
|
+
}
|
|
207
215
|
|
|
208
216
|
if (cached === extrasFingerprint) {
|
|
209
217
|
return; // same data was written recently
|
|
210
218
|
}
|
|
211
219
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
220
|
+
try {
|
|
221
|
+
await GlobalCache.setString(
|
|
222
|
+
LAST_SEEN_CACHE_NAMESPACE,
|
|
223
|
+
cacheKey,
|
|
224
|
+
extrasFingerprint,
|
|
225
|
+
{ expiresInSeconds: LAST_SEEN_THROTTLE_SECONDS },
|
|
226
|
+
);
|
|
227
|
+
} catch {
|
|
228
|
+
// Best-effort throttle write; proceed with the DB update regardless.
|
|
229
|
+
}
|
|
218
230
|
|
|
219
231
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
220
232
|
const data: any = {
|
|
@@ -170,21 +170,33 @@ export class Service extends DatabaseService<Model> {
|
|
|
170
170
|
)
|
|
171
171
|
.digest("hex");
|
|
172
172
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
cacheKey
|
|
176
|
-
|
|
173
|
+
let cached: string | null = null;
|
|
174
|
+
try {
|
|
175
|
+
cached = await GlobalCache.getString(LAST_SEEN_CACHE_NAMESPACE, cacheKey);
|
|
176
|
+
} catch {
|
|
177
|
+
/*
|
|
178
|
+
* Cache unavailable — fail open and refresh lastSeenAt anyway. A
|
|
179
|
+
* cache error must never skip the DB write below, otherwise the
|
|
180
|
+
* resource is wrongly marked "disconnected" while telemetry is
|
|
181
|
+
* still flowing. Mirrors shouldRunMaintenance's fail-open stance.
|
|
182
|
+
*/
|
|
183
|
+
cached = null;
|
|
184
|
+
}
|
|
177
185
|
|
|
178
186
|
if (cached === extrasFingerprint) {
|
|
179
187
|
return; // same data was written recently
|
|
180
188
|
}
|
|
181
189
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
190
|
+
try {
|
|
191
|
+
await GlobalCache.setString(
|
|
192
|
+
LAST_SEEN_CACHE_NAMESPACE,
|
|
193
|
+
cacheKey,
|
|
194
|
+
extrasFingerprint,
|
|
195
|
+
{ expiresInSeconds: LAST_SEEN_THROTTLE_SECONDS },
|
|
196
|
+
);
|
|
197
|
+
} catch {
|
|
198
|
+
// Best-effort throttle write; proceed with the DB update regardless.
|
|
199
|
+
}
|
|
188
200
|
|
|
189
201
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
190
202
|
const data: any = {
|
|
@@ -192,21 +192,33 @@ export class Service extends DatabaseService<Model> {
|
|
|
192
192
|
)
|
|
193
193
|
.digest("hex");
|
|
194
194
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
cacheKey
|
|
198
|
-
|
|
195
|
+
let cached: string | null = null;
|
|
196
|
+
try {
|
|
197
|
+
cached = await GlobalCache.getString(LAST_SEEN_CACHE_NAMESPACE, cacheKey);
|
|
198
|
+
} catch {
|
|
199
|
+
/*
|
|
200
|
+
* Cache unavailable — fail open and refresh lastSeenAt anyway. A
|
|
201
|
+
* cache error must never skip the DB write below, otherwise the
|
|
202
|
+
* resource is wrongly marked "disconnected" while telemetry is
|
|
203
|
+
* still flowing. Mirrors shouldRunMaintenance's fail-open stance.
|
|
204
|
+
*/
|
|
205
|
+
cached = null;
|
|
206
|
+
}
|
|
199
207
|
|
|
200
208
|
if (cached === extrasFingerprint) {
|
|
201
209
|
return; // same data was written recently
|
|
202
210
|
}
|
|
203
211
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
212
|
+
try {
|
|
213
|
+
await GlobalCache.setString(
|
|
214
|
+
LAST_SEEN_CACHE_NAMESPACE,
|
|
215
|
+
cacheKey,
|
|
216
|
+
extrasFingerprint,
|
|
217
|
+
{ expiresInSeconds: LAST_SEEN_THROTTLE_SECONDS },
|
|
218
|
+
);
|
|
219
|
+
} catch {
|
|
220
|
+
// Best-effort throttle write; proceed with the DB update regardless.
|
|
221
|
+
}
|
|
210
222
|
|
|
211
223
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
212
224
|
const data: any = {
|
|
@@ -202,21 +202,33 @@ export class Service extends DatabaseService<Model> {
|
|
|
202
202
|
)
|
|
203
203
|
.digest("hex");
|
|
204
204
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
cacheKey
|
|
208
|
-
|
|
205
|
+
let cached: string | null = null;
|
|
206
|
+
try {
|
|
207
|
+
cached = await GlobalCache.getString(LAST_SEEN_CACHE_NAMESPACE, cacheKey);
|
|
208
|
+
} catch {
|
|
209
|
+
/*
|
|
210
|
+
* Cache unavailable — fail open and refresh lastSeenAt anyway. A
|
|
211
|
+
* cache error must never skip the DB write below, otherwise the
|
|
212
|
+
* resource is wrongly marked "disconnected" while telemetry is
|
|
213
|
+
* still flowing. Mirrors shouldRunMaintenance's fail-open stance.
|
|
214
|
+
*/
|
|
215
|
+
cached = null;
|
|
216
|
+
}
|
|
209
217
|
|
|
210
218
|
if (cached === extrasFingerprint) {
|
|
211
219
|
return; // same data was written recently
|
|
212
220
|
}
|
|
213
221
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
222
|
+
try {
|
|
223
|
+
await GlobalCache.setString(
|
|
224
|
+
LAST_SEEN_CACHE_NAMESPACE,
|
|
225
|
+
cacheKey,
|
|
226
|
+
extrasFingerprint,
|
|
227
|
+
{ expiresInSeconds: LAST_SEEN_THROTTLE_SECONDS },
|
|
228
|
+
);
|
|
229
|
+
} catch {
|
|
230
|
+
// Best-effort throttle write; proceed with the DB update regardless.
|
|
231
|
+
}
|
|
220
232
|
|
|
221
233
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
222
234
|
const data: any = {
|
|
@@ -214,21 +214,33 @@ export class Service extends DatabaseService<Model> {
|
|
|
214
214
|
*/
|
|
215
215
|
const cacheKey: string = hostId.toString();
|
|
216
216
|
const extrasFingerprint: string = this.fingerprintExtras(extra);
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
cacheKey
|
|
220
|
-
|
|
217
|
+
let cached: string | null = null;
|
|
218
|
+
try {
|
|
219
|
+
cached = await GlobalCache.getString(LAST_SEEN_CACHE_NAMESPACE, cacheKey);
|
|
220
|
+
} catch {
|
|
221
|
+
/*
|
|
222
|
+
* Cache unavailable — fail open and refresh lastSeenAt anyway. A
|
|
223
|
+
* cache error must never skip the DB write below, otherwise the
|
|
224
|
+
* resource is wrongly marked "disconnected" while telemetry is
|
|
225
|
+
* still flowing. Mirrors shouldRunMaintenance's fail-open stance.
|
|
226
|
+
*/
|
|
227
|
+
cached = null;
|
|
228
|
+
}
|
|
221
229
|
|
|
222
230
|
if (cached === extrasFingerprint) {
|
|
223
231
|
return; // same data was written recently
|
|
224
232
|
}
|
|
225
233
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
234
|
+
try {
|
|
235
|
+
await GlobalCache.setString(
|
|
236
|
+
LAST_SEEN_CACHE_NAMESPACE,
|
|
237
|
+
cacheKey,
|
|
238
|
+
extrasFingerprint,
|
|
239
|
+
{ expiresInSeconds: LAST_SEEN_THROTTLE_SECONDS },
|
|
240
|
+
);
|
|
241
|
+
} catch {
|
|
242
|
+
// Best-effort throttle write; proceed with the DB update regardless.
|
|
243
|
+
}
|
|
232
244
|
|
|
233
245
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
234
246
|
const data: any = {
|
|
@@ -155,21 +155,33 @@ export class Service extends DatabaseService<Model> {
|
|
|
155
155
|
)
|
|
156
156
|
.digest("hex");
|
|
157
157
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
cacheKey
|
|
161
|
-
|
|
158
|
+
let cached: string | null = null;
|
|
159
|
+
try {
|
|
160
|
+
cached = await GlobalCache.getString(LAST_SEEN_CACHE_NAMESPACE, cacheKey);
|
|
161
|
+
} catch {
|
|
162
|
+
/*
|
|
163
|
+
* Cache unavailable — fail open and refresh lastSeenAt anyway. A
|
|
164
|
+
* cache error must never skip the DB write below, otherwise the
|
|
165
|
+
* resource is wrongly marked "disconnected" while telemetry is
|
|
166
|
+
* still flowing. Mirrors shouldRunMaintenance's fail-open stance.
|
|
167
|
+
*/
|
|
168
|
+
cached = null;
|
|
169
|
+
}
|
|
162
170
|
|
|
163
171
|
if (cached === extrasFingerprint) {
|
|
164
172
|
return; // same data was written recently
|
|
165
173
|
}
|
|
166
174
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
175
|
+
try {
|
|
176
|
+
await GlobalCache.setString(
|
|
177
|
+
LAST_SEEN_CACHE_NAMESPACE,
|
|
178
|
+
cacheKey,
|
|
179
|
+
extrasFingerprint,
|
|
180
|
+
{ expiresInSeconds: LAST_SEEN_THROTTLE_SECONDS },
|
|
181
|
+
);
|
|
182
|
+
} catch {
|
|
183
|
+
// Best-effort throttle write; proceed with the DB update regardless.
|
|
184
|
+
}
|
|
173
185
|
|
|
174
186
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
175
187
|
const data: any = {
|
|
@@ -192,21 +192,33 @@ export class Service extends DatabaseService<Model> {
|
|
|
192
192
|
)
|
|
193
193
|
.digest("hex");
|
|
194
194
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
cacheKey
|
|
198
|
-
|
|
195
|
+
let cached: string | null = null;
|
|
196
|
+
try {
|
|
197
|
+
cached = await GlobalCache.getString(LAST_SEEN_CACHE_NAMESPACE, cacheKey);
|
|
198
|
+
} catch {
|
|
199
|
+
/*
|
|
200
|
+
* Cache unavailable — fail open and refresh lastSeenAt anyway. A
|
|
201
|
+
* cache error must never skip the DB write below, otherwise the
|
|
202
|
+
* resource is wrongly marked "disconnected" while telemetry is
|
|
203
|
+
* still flowing. Mirrors shouldRunMaintenance's fail-open stance.
|
|
204
|
+
*/
|
|
205
|
+
cached = null;
|
|
206
|
+
}
|
|
199
207
|
|
|
200
208
|
if (cached === extrasFingerprint) {
|
|
201
209
|
return; // same data was written recently
|
|
202
210
|
}
|
|
203
211
|
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
212
|
+
try {
|
|
213
|
+
await GlobalCache.setString(
|
|
214
|
+
LAST_SEEN_CACHE_NAMESPACE,
|
|
215
|
+
cacheKey,
|
|
216
|
+
extrasFingerprint,
|
|
217
|
+
{ expiresInSeconds: LAST_SEEN_THROTTLE_SECONDS },
|
|
218
|
+
);
|
|
219
|
+
} catch {
|
|
220
|
+
// Best-effort throttle write; proceed with the DB update regardless.
|
|
221
|
+
}
|
|
210
222
|
|
|
211
223
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
212
224
|
const data: any = {
|
|
@@ -193,21 +193,33 @@ export class Service extends DatabaseService<Model> {
|
|
|
193
193
|
)
|
|
194
194
|
.digest("hex");
|
|
195
195
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
cacheKey
|
|
199
|
-
|
|
196
|
+
let cached: string | null = null;
|
|
197
|
+
try {
|
|
198
|
+
cached = await GlobalCache.getString(LAST_SEEN_CACHE_NAMESPACE, cacheKey);
|
|
199
|
+
} catch {
|
|
200
|
+
/*
|
|
201
|
+
* Cache unavailable — fail open and refresh lastSeenAt anyway. A
|
|
202
|
+
* cache error must never skip the DB write below, otherwise the
|
|
203
|
+
* resource is wrongly marked "disconnected" while telemetry is
|
|
204
|
+
* still flowing. Mirrors shouldRunMaintenance's fail-open stance.
|
|
205
|
+
*/
|
|
206
|
+
cached = null;
|
|
207
|
+
}
|
|
200
208
|
|
|
201
209
|
if (cached === extrasFingerprint) {
|
|
202
210
|
return; // same data was written recently
|
|
203
211
|
}
|
|
204
212
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
213
|
+
try {
|
|
214
|
+
await GlobalCache.setString(
|
|
215
|
+
LAST_SEEN_CACHE_NAMESPACE,
|
|
216
|
+
cacheKey,
|
|
217
|
+
extrasFingerprint,
|
|
218
|
+
{ expiresInSeconds: LAST_SEEN_THROTTLE_SECONDS },
|
|
219
|
+
);
|
|
220
|
+
} catch {
|
|
221
|
+
// Best-effort throttle write; proceed with the DB update regardless.
|
|
222
|
+
}
|
|
211
223
|
|
|
212
224
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
213
225
|
const data: any = {
|
|
@@ -141,21 +141,33 @@ export class Service extends DatabaseService<Model> {
|
|
|
141
141
|
)
|
|
142
142
|
.digest("hex");
|
|
143
143
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
cacheKey
|
|
147
|
-
|
|
144
|
+
let cached: string | null = null;
|
|
145
|
+
try {
|
|
146
|
+
cached = await GlobalCache.getString(LAST_SEEN_CACHE_NAMESPACE, cacheKey);
|
|
147
|
+
} catch {
|
|
148
|
+
/*
|
|
149
|
+
* Cache unavailable — fail open and refresh lastSeenAt anyway. A
|
|
150
|
+
* cache error must never skip the DB write below, otherwise the
|
|
151
|
+
* resource is wrongly marked "disconnected" while telemetry is
|
|
152
|
+
* still flowing. Mirrors shouldRunMaintenance's fail-open stance.
|
|
153
|
+
*/
|
|
154
|
+
cached = null;
|
|
155
|
+
}
|
|
148
156
|
|
|
149
157
|
if (cached === extrasFingerprint) {
|
|
150
158
|
return; // same data was written recently
|
|
151
159
|
}
|
|
152
160
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
161
|
+
try {
|
|
162
|
+
await GlobalCache.setString(
|
|
163
|
+
LAST_SEEN_CACHE_NAMESPACE,
|
|
164
|
+
cacheKey,
|
|
165
|
+
extrasFingerprint,
|
|
166
|
+
{ expiresInSeconds: LAST_SEEN_THROTTLE_SECONDS },
|
|
167
|
+
);
|
|
168
|
+
} catch {
|
|
169
|
+
// Best-effort throttle write; proceed with the DB update regardless.
|
|
170
|
+
}
|
|
159
171
|
|
|
160
172
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
161
173
|
const data: any = {
|
|
@@ -170,21 +170,33 @@ export class Service extends DatabaseService<Model> {
|
|
|
170
170
|
)
|
|
171
171
|
.digest("hex");
|
|
172
172
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
cacheKey
|
|
176
|
-
|
|
173
|
+
let cached: string | null = null;
|
|
174
|
+
try {
|
|
175
|
+
cached = await GlobalCache.getString(LAST_SEEN_CACHE_NAMESPACE, cacheKey);
|
|
176
|
+
} catch {
|
|
177
|
+
/*
|
|
178
|
+
* Cache unavailable — fail open and refresh lastSeenAt anyway. A
|
|
179
|
+
* cache error must never skip the DB write below, otherwise the
|
|
180
|
+
* resource is wrongly marked "disconnected" while telemetry is
|
|
181
|
+
* still flowing. Mirrors shouldRunMaintenance's fail-open stance.
|
|
182
|
+
*/
|
|
183
|
+
cached = null;
|
|
184
|
+
}
|
|
177
185
|
|
|
178
186
|
if (cached === extrasFingerprint) {
|
|
179
187
|
return; // same data was written recently
|
|
180
188
|
}
|
|
181
189
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
190
|
+
try {
|
|
191
|
+
await GlobalCache.setString(
|
|
192
|
+
LAST_SEEN_CACHE_NAMESPACE,
|
|
193
|
+
cacheKey,
|
|
194
|
+
extrasFingerprint,
|
|
195
|
+
{ expiresInSeconds: LAST_SEEN_THROTTLE_SECONDS },
|
|
196
|
+
);
|
|
197
|
+
} catch {
|
|
198
|
+
// Best-effort throttle write; proceed with the DB update regardless.
|
|
199
|
+
}
|
|
188
200
|
|
|
189
201
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
190
202
|
const data: any = {
|
|
@@ -17,6 +17,7 @@ type MockClient = {
|
|
|
17
17
|
set: jest.Mock;
|
|
18
18
|
expire: jest.Mock;
|
|
19
19
|
get: jest.Mock;
|
|
20
|
+
del: jest.Mock;
|
|
20
21
|
};
|
|
21
22
|
|
|
22
23
|
describe("GlobalCache.setString", () => {
|
|
@@ -27,6 +28,7 @@ describe("GlobalCache.setString", () => {
|
|
|
27
28
|
set: jest.fn().mockResolvedValue("OK"),
|
|
28
29
|
expire: jest.fn().mockResolvedValue(1),
|
|
29
30
|
get: jest.fn(),
|
|
31
|
+
del: jest.fn().mockResolvedValue(1),
|
|
30
32
|
};
|
|
31
33
|
(Redis.getClient as jest.Mock).mockReturnValue(client);
|
|
32
34
|
(Redis.isConnected as jest.Mock).mockReturnValue(true);
|
|
@@ -98,3 +100,44 @@ describe("GlobalCache.setString", () => {
|
|
|
98
100
|
expect(client.expire).not.toHaveBeenCalled();
|
|
99
101
|
});
|
|
100
102
|
});
|
|
103
|
+
|
|
104
|
+
describe("GlobalCache.deleteKey", () => {
|
|
105
|
+
let client: MockClient;
|
|
106
|
+
|
|
107
|
+
beforeEach(() => {
|
|
108
|
+
client = {
|
|
109
|
+
set: jest.fn().mockResolvedValue("OK"),
|
|
110
|
+
expire: jest.fn().mockResolvedValue(1),
|
|
111
|
+
get: jest.fn(),
|
|
112
|
+
del: jest.fn().mockResolvedValue(1),
|
|
113
|
+
};
|
|
114
|
+
(Redis.getClient as jest.Mock).mockReturnValue(client);
|
|
115
|
+
(Redis.isConnected as jest.Mock).mockReturnValue(true);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
afterEach(() => {
|
|
119
|
+
jest.clearAllMocks();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
/*
|
|
123
|
+
* deleteKey backs clearMaintenanceFence (OtelIngestBaseService): when
|
|
124
|
+
* the fenced maintenance work (updateLastSeen) fails, the fence is
|
|
125
|
+
* released so the next ingest batch retries instead of leaving the
|
|
126
|
+
* resource stranded as "disconnected" for the whole TTL window.
|
|
127
|
+
*/
|
|
128
|
+
test("deletes the namespaced key", async () => {
|
|
129
|
+
await GlobalCache.deleteKey("ns", "key");
|
|
130
|
+
|
|
131
|
+
expect(client.del).toHaveBeenCalledTimes(1);
|
|
132
|
+
expect(client.del).toHaveBeenCalledWith("ns-key");
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("throws when the cache is not connected", async () => {
|
|
136
|
+
(Redis.isConnected as jest.Mock).mockReturnValue(false);
|
|
137
|
+
|
|
138
|
+
await expect(GlobalCache.deleteKey("ns", "key")).rejects.toThrow(
|
|
139
|
+
DatabaseNotConnectedException,
|
|
140
|
+
);
|
|
141
|
+
expect(client.del).not.toHaveBeenCalled();
|
|
142
|
+
});
|
|
143
|
+
});
|
|
@@ -108,11 +108,6 @@ const AreaChartElement: FunctionComponent<AreaInternalProps> = (
|
|
|
108
108
|
});
|
|
109
109
|
}, [props.exemplarPoints, props.xAxis]);
|
|
110
110
|
|
|
111
|
-
const className: string = props.heightInPx ? `` : "h-80";
|
|
112
|
-
const style: React.CSSProperties = props.heightInPx
|
|
113
|
-
? { height: `${props.heightInPx}px` }
|
|
114
|
-
: {};
|
|
115
|
-
|
|
116
111
|
const hasNoData: boolean =
|
|
117
112
|
!props.data ||
|
|
118
113
|
props.data.length === 0 ||
|
|
@@ -134,10 +129,11 @@ const AreaChartElement: FunctionComponent<AreaInternalProps> = (
|
|
|
134
129
|
typeof yAxisMaxOption === "number" ? { maxValue: yAxisMaxOption } : {};
|
|
135
130
|
|
|
136
131
|
return (
|
|
137
|
-
<div
|
|
132
|
+
<div
|
|
133
|
+
className="relative flex flex-1"
|
|
134
|
+
style={props.heightInPx ? { height: `${props.heightInPx}px` } : undefined}
|
|
135
|
+
>
|
|
138
136
|
<AreaChart
|
|
139
|
-
className={className}
|
|
140
|
-
style={style}
|
|
141
137
|
data={records}
|
|
142
138
|
tickGap={30}
|
|
143
139
|
index={"Time"}
|
|
@@ -54,11 +54,6 @@ const BarChartElement: FunctionComponent<BarInternalProps> = (
|
|
|
54
54
|
setRecords(records);
|
|
55
55
|
}, [props.data]);
|
|
56
56
|
|
|
57
|
-
const className: string = props.heightInPx ? `` : "h-80";
|
|
58
|
-
const style: React.CSSProperties = props.heightInPx
|
|
59
|
-
? { height: `${props.heightInPx}px` }
|
|
60
|
-
: {};
|
|
61
|
-
|
|
62
57
|
const hasNoData: boolean =
|
|
63
58
|
!props.data ||
|
|
64
59
|
props.data.length === 0 ||
|
|
@@ -67,10 +62,11 @@ const BarChartElement: FunctionComponent<BarInternalProps> = (
|
|
|
67
62
|
});
|
|
68
63
|
|
|
69
64
|
return (
|
|
70
|
-
<div
|
|
65
|
+
<div
|
|
66
|
+
className="relative flex flex-1"
|
|
67
|
+
style={props.heightInPx ? { height: `${props.heightInPx}px` } : undefined}
|
|
68
|
+
>
|
|
71
69
|
<BarChart
|
|
72
|
-
className={className}
|
|
73
|
-
style={style}
|
|
74
70
|
data={records}
|
|
75
71
|
tickGap={30}
|
|
76
72
|
index={"Time"}
|
|
@@ -49,7 +49,6 @@ export interface Chart {
|
|
|
49
49
|
export interface ComponentProps {
|
|
50
50
|
charts: Array<Chart>;
|
|
51
51
|
hideCard?: boolean | undefined;
|
|
52
|
-
heightInPx?: number | undefined;
|
|
53
52
|
chartCssClass?: string | undefined;
|
|
54
53
|
}
|
|
55
54
|
|
|
@@ -80,7 +79,6 @@ const ChartGroup: FunctionComponent<ComponentProps> = (
|
|
|
80
79
|
key={index}
|
|
81
80
|
{...(chart.props as LineChartProps)}
|
|
82
81
|
syncid={syncId}
|
|
83
|
-
heightInPx={props.heightInPx}
|
|
84
82
|
exemplarPoints={chart.exemplarPoints}
|
|
85
83
|
onExemplarClick={chart.onExemplarClick}
|
|
86
84
|
showLegend={showLegend}
|
|
@@ -92,7 +90,6 @@ const ChartGroup: FunctionComponent<ComponentProps> = (
|
|
|
92
90
|
key={index}
|
|
93
91
|
{...(chart.props as BarChartProps)}
|
|
94
92
|
syncid={syncId}
|
|
95
|
-
heightInPx={props.heightInPx}
|
|
96
93
|
showLegend={showLegend}
|
|
97
94
|
/>
|
|
98
95
|
);
|
|
@@ -102,7 +99,6 @@ const ChartGroup: FunctionComponent<ComponentProps> = (
|
|
|
102
99
|
key={index}
|
|
103
100
|
{...(chart.props as AreaChartProps)}
|
|
104
101
|
syncid={syncId}
|
|
105
|
-
heightInPx={props.heightInPx}
|
|
106
102
|
exemplarPoints={chart.exemplarPoints}
|
|
107
103
|
onExemplarClick={chart.onExemplarClick}
|
|
108
104
|
showLegend={showLegend}
|
|
@@ -244,14 +240,14 @@ const ChartGroup: FunctionComponent<ComponentProps> = (
|
|
|
244
240
|
return (
|
|
245
241
|
<>
|
|
246
242
|
{renderMetricInfoModal()}
|
|
247
|
-
<div className="space-y-3">
|
|
243
|
+
<div className="space-y-3 flex-col flex flex-1 w-full">
|
|
248
244
|
{props.charts.map((chart: Chart, index: number) => {
|
|
249
245
|
return (
|
|
250
246
|
<div
|
|
251
247
|
key={index}
|
|
252
|
-
className={`bg-white ${props.chartCssClass || ""}`}
|
|
248
|
+
className={`bg-white ${props.chartCssClass || ""} flex-1 flex-col flex w-full`}
|
|
253
249
|
>
|
|
254
|
-
<div className="px-5 pt-4 pb-4">
|
|
250
|
+
<div className="px-5 pt-4 pb-4 flex flex-col flex-1">
|
|
255
251
|
<div className="mb-3 pb-3 border-b border-gray-100">
|
|
256
252
|
<div className="flex items-center">
|
|
257
253
|
<h3 className="text-sm font-semibold text-gray-800 tracking-tight">
|
|
@@ -265,10 +261,10 @@ const ChartGroup: FunctionComponent<ComponentProps> = (
|
|
|
265
261
|
</p>
|
|
266
262
|
)}
|
|
267
263
|
</div>
|
|
264
|
+
{getChartContent(chart, index)}
|
|
268
265
|
{chart.seriesControls ? (
|
|
269
266
|
<div className="mb-3">{chart.seriesControls}</div>
|
|
270
267
|
) : null}
|
|
271
|
-
{getChartContent(chart, index)}
|
|
272
268
|
</div>
|
|
273
269
|
</div>
|
|
274
270
|
);
|
|
@@ -689,7 +689,7 @@ const AreaChart: React.ForwardRefExoticComponent<
|
|
|
689
689
|
}
|
|
690
690
|
|
|
691
691
|
return (
|
|
692
|
-
<div ref={ref} className={cx("
|
|
692
|
+
<div ref={ref} className={cx("flex-1 w-full", className)} {...other}>
|
|
693
693
|
<ResponsiveContainer>
|
|
694
694
|
<RechartsAreaChart
|
|
695
695
|
data={data}
|
|
@@ -695,7 +695,7 @@ const LineChart: React.ForwardRefExoticComponent<
|
|
|
695
695
|
}
|
|
696
696
|
|
|
697
697
|
return (
|
|
698
|
-
<div ref={ref} className={cx("
|
|
698
|
+
<div ref={ref} className={cx("flex-1 w-full", className)} {...other}>
|
|
699
699
|
<ResponsiveContainer>
|
|
700
700
|
<RechartsLineChart
|
|
701
701
|
data={data}
|