querysub 0.450.0 → 0.451.0
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/package.json
CHANGED
|
@@ -19,13 +19,15 @@ import { safeLoop } from "socket-function/src/batching";
|
|
|
19
19
|
import { errorToUndefined } from "../errors";
|
|
20
20
|
import { shutdown } from "../diagnostics/periodic";
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
// Kept separate from the cache-wrapped `archives` so we can probe the underlying archives
|
|
23
|
+
// directly (without the cache layer in the way) when diagnosing missing-file failures.
|
|
24
|
+
const archivesBase = lazy(() => getArchives("path-values/"));
|
|
25
|
+
export const archives = lazy(() => wrapArchivesWithCache(archivesBase()));
|
|
23
26
|
export const archivesLocks = lazy(() => getArchives("path-values-locks/"));
|
|
24
27
|
export const archivesRecycleBin = lazy(() => wrapArchivesWithCache(getArchives("path-values-recycle-bin/")));
|
|
25
28
|
|
|
26
|
-
//
|
|
27
|
-
//
|
|
28
|
-
// times to confirm it really is gone.
|
|
29
|
+
// If getInfo reports a just-written file as missing, recheck a few times before treating it
|
|
30
|
+
// as a fatal "written too slowly" condition.
|
|
29
31
|
const ARCHIVE_INFO_RECHECK_ATTEMPTS = 5;
|
|
30
32
|
const ARCHIVE_INFO_RECHECK_DELAY = 5000;
|
|
31
33
|
|
|
@@ -224,14 +226,33 @@ export class PathValueArchives {
|
|
|
224
226
|
let fileInfo = await archives().getInfo(fullPath);
|
|
225
227
|
// NOTE: If no fileInfo... then our file was merged? Which... is BAD, as it means we took
|
|
226
228
|
// too long to read it, so we probably took too long to write it too!
|
|
227
|
-
// Backblaze's getInfo can transiently report a just-written file as missing, so recheck
|
|
228
|
-
// a few times before treating a missing file as fatal.
|
|
229
229
|
for (let attempt = 0; !fileInfo && attempt < ARCHIVE_INFO_RECHECK_ATTEMPTS; attempt++) {
|
|
230
230
|
await delay(ARCHIVE_INFO_RECHECK_DELAY);
|
|
231
231
|
fileInfo = await archives().getInfo(fullPath);
|
|
232
232
|
}
|
|
233
233
|
if (!fileInfo || fileInfo.writeTime > slowestFileWriteTime) {
|
|
234
|
-
|
|
234
|
+
// If we're crashing because getInfo reports the file as missing, probe the
|
|
235
|
+
// underlying (non-cached) archives -- both getInfo and an actual get() of the
|
|
236
|
+
// bytes -- and record the results in the crash log, so we can tell whether that
|
|
237
|
+
// info is consistent. We do NOT use this as a fallback; we still crash.
|
|
238
|
+
let missingDiagnostic = "";
|
|
239
|
+
if (!fileInfo) {
|
|
240
|
+
let baseInfoStr: string;
|
|
241
|
+
try {
|
|
242
|
+
baseInfoStr = JSON.stringify(await archivesBase().getInfo(fullPath)) ?? "undefined";
|
|
243
|
+
} catch (e) {
|
|
244
|
+
baseInfoStr = `threw ${(e as Error).stack ?? e}`;
|
|
245
|
+
}
|
|
246
|
+
let baseGetStr: string;
|
|
247
|
+
try {
|
|
248
|
+
let baseData = await archivesBase().get(fullPath);
|
|
249
|
+
baseGetStr = baseData ? `${baseData.byteLength}B` : "undefined";
|
|
250
|
+
} catch (e) {
|
|
251
|
+
baseGetStr = `threw ${(e as Error).stack ?? e}`;
|
|
252
|
+
}
|
|
253
|
+
missingDiagnostic = ` [underlying archives probe: getInfo=${baseInfoStr}, get=${baseGetStr}, expected ${data.byteLength}B]`;
|
|
254
|
+
}
|
|
255
|
+
console.error(red(`File ${fullPath} was written too slowly, ${fileInfo?.writeTime || "undefined"} < ${slowestFileWriteTime}.${missingDiagnostic} This means some values will be rejected by reads. Killing server, our state is irrecoverable. Our watches have invalid data, and we have to stop before we create more invalid dependencies.`));
|
|
235
256
|
await delay(5000);
|
|
236
257
|
try {
|
|
237
258
|
await shutdown();
|
|
@@ -40,6 +40,7 @@ let pages: {
|
|
|
40
40
|
componentName: string;
|
|
41
41
|
title: string;
|
|
42
42
|
}[] = [];
|
|
43
|
+
let headerNotifications: preact.ComponentClass[] = [];
|
|
43
44
|
|
|
44
45
|
let __schema: SchemaObject<unknown, {
|
|
45
46
|
isManagementUser: () => boolean;
|
|
@@ -63,6 +64,9 @@ export async function registerManagementPages2(config: {
|
|
|
63
64
|
controllerName?: string;
|
|
64
65
|
getModule: () => Promise<any>;
|
|
65
66
|
|
|
67
|
+
// If provided, this component (exported from getModule) is rendered in the management header.
|
|
68
|
+
notificationComponentName?: string;
|
|
69
|
+
|
|
66
70
|
title?: string;
|
|
67
71
|
}[];
|
|
68
72
|
}) {
|
|
@@ -240,6 +244,9 @@ export async function registerManagementPages2(config: {
|
|
|
240
244
|
componentName: page.componentName,
|
|
241
245
|
componentClass: createLazyComponent(page.getModule)(page.componentName),
|
|
242
246
|
});
|
|
247
|
+
if (page.notificationComponentName) {
|
|
248
|
+
headerNotifications.push(createLazyComponent(page.getModule)(page.notificationComponentName));
|
|
249
|
+
}
|
|
243
250
|
}
|
|
244
251
|
}
|
|
245
252
|
}
|
|
@@ -355,6 +362,7 @@ class ManagementRoot extends qreact.Component {
|
|
|
355
362
|
</style>
|
|
356
363
|
<div class={css.fillWidth.hbox(20, 4).wrap.hsl(245, 25, 80).pad2(20, 4).pointerEvents("all")}>
|
|
357
364
|
<ErrorWarning />
|
|
365
|
+
{headerNotifications.map(N => <N />)}
|
|
358
366
|
<ATag values={[
|
|
359
367
|
managementPageURL.getOverride("MachinesPage"),
|
|
360
368
|
currentViewParam.getOverride("services"),
|