querysub 0.449.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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "querysub",
3
- "version": "0.449.0",
3
+ "version": "0.451.0",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "note1": "note on node-forge fork, see https://github.com/digitalbazaar/forge/issues/744 for details",
@@ -19,10 +19,18 @@ import { safeLoop } from "socket-function/src/batching";
19
19
  import { errorToUndefined } from "../errors";
20
20
  import { shutdown } from "../diagnostics/periodic";
21
21
 
22
- export const archives = lazy(() => wrapArchivesWithCache(getArchives("path-values/")));
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
 
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.
31
+ const ARCHIVE_INFO_RECHECK_ATTEMPTS = 5;
32
+ const ARCHIVE_INFO_RECHECK_DELAY = 5000;
33
+
26
34
  export type RawValuePaths = {
27
35
  dataPath: string;
28
36
  values: PathValue[];
@@ -218,8 +226,33 @@ export class PathValueArchives {
218
226
  let fileInfo = await archives().getInfo(fullPath);
219
227
  // NOTE: If no fileInfo... then our file was merged? Which... is BAD, as it means we took
220
228
  // too long to read it, so we probably took too long to write it too!
229
+ for (let attempt = 0; !fileInfo && attempt < ARCHIVE_INFO_RECHECK_ATTEMPTS; attempt++) {
230
+ await delay(ARCHIVE_INFO_RECHECK_DELAY);
231
+ fileInfo = await archives().getInfo(fullPath);
232
+ }
221
233
  if (!fileInfo || fileInfo.writeTime > slowestFileWriteTime) {
222
- console.error(red(`File ${fullPath} was written too slowly, ${fileInfo?.writeTime || "undefined"} < ${slowestFileWriteTime}. 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.`));
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.`));
223
256
  await delay(5000);
224
257
  try {
225
258
  await shutdown();
@@ -24,7 +24,7 @@ import { atomic } from "../../../2-proxy/PathValueProxyWatcher";
24
24
  import { magenta } from "socket-function/src/formatting/logColors";
25
25
 
26
26
 
27
- type SuppressionEntry = {
27
+ export type SuppressionEntry = {
28
28
  id: string;
29
29
  notes?: string;
30
30
  pattern: string;
@@ -35,7 +35,7 @@ type SuppressionEntry = {
35
35
  createdTime: number;
36
36
  lastUpdatedTime: number;
37
37
  };
38
- const suppression = archiveJSONT<SuppressionEntry>(() => nestArchives("logs/error-suppression/", getArchivesBackblaze(getDomain())));
38
+ export const suppression = archiveJSONT<SuppressionEntry>(() => nestArchives("logs/error-suppression/", getArchivesBackblaze(getDomain())));
39
39
  let suppressionCache: SuppressionEntry[] = [];
40
40
 
41
41
  // In-memory Discord notification throttling
@@ -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"),