querysub 0.143.0 → 0.145.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.143.0",
3
+ "version": "0.145.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",
@@ -24,7 +24,7 @@
24
24
  "node-forge": "https://github.com/sliftist/forge#e618181b469b07bdc70b968b0391beb8ef5fecd6",
25
25
  "pako": "^2.1.0",
26
26
  "preact": "^10.11.3",
27
- "socket-function": "^0.80.0",
27
+ "socket-function": "^0.81.0",
28
28
  "terser": "^5.31.0",
29
29
  "typesafecss": "^0.6.3",
30
30
  "yaml": "^2.5.0",
@@ -11,7 +11,7 @@ import { errorToUndefined, logErrors, timeoutToUndefined } from "../errors";
11
11
  import { getPathFromStr } from "../path";
12
12
  import { nodePathAuthority, pathValueAuthority2 } from "./NodePathAuthorities";
13
13
  import { PathValueController } from "./PathValueController";
14
- import { PathValue, MAX_ACCEPTED_CHANGE_AGE, lockWatcher, authorityStorage, writeValidStorage, WriteState, lockToCallback, pathWatcher, MAX_CHANGE_AGE, debugPathValue, debugPathValuePath, compareTime, epochTime, isOurPrediction } from "./pathValueCore";
14
+ import { PathValue, MAX_ACCEPTED_CHANGE_AGE, lockWatcher, authorityStorage, writeValidStorage, WriteState, lockToCallback, pathWatcher, MAX_CHANGE_AGE, debugPathValue, debugPathValuePath, compareTime, epochTime, isOurPrediction, timeHash } from "./pathValueCore";
15
15
  import debugbreak from "debugbreak";
16
16
  import { red } from "socket-function/src/formatting/logColors";
17
17
  import { registerDynamicResource } from "../diagnostics/trackResources";
@@ -278,6 +278,19 @@ class PathValueCommitter {
278
278
  // missing values (unless we are the authority). Otherwise bad rejected values can stick around,
279
279
  // because they are newer, and our new source doesn't know we have them.
280
280
  measureBlock(function resetOnInitialTrigger() {
281
+ let receivedValues = new Map<string, Set<string>>();
282
+ for (let batch of batched) {
283
+ for (let pathValue of batch.pathValues) {
284
+ let path = pathValue.path;
285
+ let hash = timeHash(pathValue.time);
286
+ let paths = receivedValues.get(path);
287
+ if (!paths) {
288
+ paths = new Set();
289
+ receivedValues.set(path, paths);
290
+ }
291
+ paths.add(hash);
292
+ }
293
+ }
281
294
  for (let batch of batched) {
282
295
  if (!batch.initialTrigger) continue;
283
296
  for (let pathValue of batch.pathValues) {
@@ -292,12 +305,24 @@ class PathValueCommitter {
292
305
  // Only reset values after us, otherwise we might clear valid history that
293
306
  // we JUST received.
294
307
  if (compareTime(value.time, pathValue.time) <= 0) continue;
308
+
309
+ // If we just received it, accept it.
310
+ // - Otherwise we might reject a valid older value, and then when the newer value is rejected
311
+ // the server won't tell us about the older value again, and so we will think no values
312
+ // are accepted, when the older value IS accepted.
313
+ // BUG: In theory couldn't we receive an outdated value, then reconnect, then receive the correct
314
+ // value, and have them all batched together? In which case, we shouldn't accept the value.
315
+ // I'm not really sure how to fix this though. We kind of need to just handle adding the values
316
+ // and the rejections in order, instead of batching them, but that is much slower, and the race
317
+ // condition will likely be extremely rare (and only happen on disconnections anyways).
318
+ if (receivedValues.get(pathValue.path)?.has(timeHash(pathValue.time))) continue;
319
+
295
320
  specialInitialParentCausedRejections.push({
296
321
  path: value.path,
297
322
  time: value.time,
298
323
  isValid: false,
299
324
  isTransparent: !!value.isTransparent,
300
- reason: "future missing (initial trigger)",
325
+ reason: "future missing on resync (initial trigger)",
301
326
  });
302
327
  }
303
328
  }
@@ -431,6 +456,13 @@ class PathValueCommitter {
431
456
 
432
457
  let now = Date.now();
433
458
 
459
+ // NOTE: specialInitialParentCausedRejections is a bit dangerous, because it can reject golden values.
460
+ // This means we have to ingest it immediately, otherwise authorityStorage might gc path values
461
+ // (that are before a golden value), when rejections might remove that golden value.
462
+ if (specialInitialParentCausedRejections) {
463
+ this.ingestValidStates(specialInitialParentCausedRejections);
464
+ }
465
+
434
466
  authorityStorage.ingestValues(pathValues, parentsSynced, parentSyncedSources);
435
467
 
436
468
  // NOTE: This doesn't seem to be where we lag, so we aren't tracking it anymore.
@@ -440,7 +472,7 @@ class PathValueCommitter {
440
472
  }
441
473
 
442
474
  // NOTE: Also triggers rejections of watched paths
443
- this.ingestValidStates(specialInitialParentCausedRejections ?? [], parentsSynced, undefined, pathValues, initialTriggers);
475
+ this.ingestValidStates([], parentsSynced, undefined, pathValues, initialTriggers);
444
476
  }
445
477
 
446
478
  // NOTE: Technically, because we trigger all remotes any time a value changes, even
@@ -945,9 +945,10 @@ class AuthorityPathValueStorage {
945
945
  // some values in memory...
946
946
 
947
947
  let goldenTime = now - MAX_CHANGE_AGE;
948
- // NOTE: Values with no locks are implicitly golden, as they can't be rejected.
949
- let firstGoldenIndex = values.findIndex(x => x.time.time < goldenTime || x.lockCount === 0);
948
+ // NOTE: Values with no locks are implicitly golden, as they can't be rejected
949
+ let firstGoldenIndex = values.findIndex(x => x.time.time < goldenTime && x.valid || x.lockCount === 0);
950
950
  if (firstGoldenIndex < 0) return;
951
+
951
952
  // Special case, everything is golden, and the latest value is undefined, and is behind the GC
952
953
  // point, then delete everything, so constantly changing keys doesn't result in us leaking memory forever.
953
954
  if (
@@ -987,9 +988,8 @@ class AuthorityPathValueStorage {
987
988
  }
988
989
  } else {
989
990
  // Delete everything that is golden, except for the most recent VALID value.
990
- let validGoldenIndex = values.findIndex(x => x.valid && x.time.time < goldenTime);
991
- if (validGoldenIndex >= 0 && values.length > validGoldenIndex) {
992
- for (let i = values.length - 1; i > validGoldenIndex; i--) {
991
+ if (firstGoldenIndex >= 0 && values.length > firstGoldenIndex) {
992
+ for (let i = values.length - 1; i > firstGoldenIndex; i--) {
993
993
  let gced = values.pop();
994
994
  if (!isCoreQuiet && gced) {
995
995
  console.log(`GCing ${debugPathValuePath(gced)}`);