querysub 0.144.0 → 0.146.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.144.0",
3
+ "version": "0.146.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.82.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,23 @@ 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
+ }
294
+
295
+ // TODO: Ugh... rejections on initialSync might the wrong solution. I think we have to just remove the values,
296
+ // even though this makes debugging harder, in order to prevent the rejections from sticking around.
297
+
281
298
  for (let batch of batched) {
282
299
  if (!batch.initialTrigger) continue;
283
300
  for (let pathValue of batch.pathValues) {
@@ -292,12 +309,24 @@ class PathValueCommitter {
292
309
  // Only reset values after us, otherwise we might clear valid history that
293
310
  // we JUST received.
294
311
  if (compareTime(value.time, pathValue.time) <= 0) continue;
312
+
313
+ // If we just received it, accept it.
314
+ // - Otherwise we might reject a valid older value, and then when the newer value is rejected
315
+ // the server won't tell us about the older value again, and so we will think no values
316
+ // are accepted, when the older value IS accepted.
317
+ // BUG: In theory couldn't we receive an outdated value, then reconnect, then receive the correct
318
+ // value, and have them all batched together? In which case, we shouldn't accept the value.
319
+ // I'm not really sure how to fix this though. We kind of need to just handle adding the values
320
+ // and the rejections in order, instead of batching them, but that is much slower, and the race
321
+ // condition will likely be extremely rare (and only happen on disconnections anyways).
322
+ if (receivedValues.get(pathValue.path)?.has(timeHash(pathValue.time))) continue;
323
+
295
324
  specialInitialParentCausedRejections.push({
296
325
  path: value.path,
297
326
  time: value.time,
298
327
  isValid: false,
299
328
  isTransparent: !!value.isTransparent,
300
- reason: "future missing (initial trigger)",
329
+ reason: "future missing on resync (initial trigger)",
301
330
  });
302
331
  }
303
332
  }
@@ -431,6 +460,13 @@ class PathValueCommitter {
431
460
 
432
461
  let now = Date.now();
433
462
 
463
+ // NOTE: specialInitialParentCausedRejections is a bit dangerous, because it can reject golden values.
464
+ // This means we have to ingest it immediately, otherwise authorityStorage might gc path values
465
+ // (that are before a golden value), when rejections might remove that golden value.
466
+ if (specialInitialParentCausedRejections) {
467
+ this.ingestValidStates(specialInitialParentCausedRejections);
468
+ }
469
+
434
470
  authorityStorage.ingestValues(pathValues, parentsSynced, parentSyncedSources);
435
471
 
436
472
  // NOTE: This doesn't seem to be where we lag, so we aren't tracking it anymore.
@@ -440,7 +476,7 @@ class PathValueCommitter {
440
476
  }
441
477
 
442
478
  // NOTE: Also triggers rejections of watched paths
443
- this.ingestValidStates(specialInitialParentCausedRejections ?? [], parentsSynced, undefined, pathValues, initialTriggers);
479
+ this.ingestValidStates([], parentsSynced, undefined, pathValues, initialTriggers);
444
480
  }
445
481
 
446
482
  // 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)}`);
@@ -33,24 +33,30 @@ export class ActionsHistory {
33
33
  }
34
34
  public static OnRead(values: PathValue[]) {
35
35
  if (ClientWatcher.DEBUG_READS && values.length > 0) {
36
- let now = Date.now();
37
- let oldest = Math.min(...values.map(x => x.time.time).filter(x => x), now);
38
- group(ClientWatcher.DEBUG_READS_EXPANDED)(`(${now - epoch}ms, ${now}) Read count ${values.length}, ${(Date.now() - oldest).toFixed()}ms old`);
39
- for (let value of values) {
40
- console.log(blue(debugPathValuePath(value)), "===", pathValueSerializer.getPathValue(value), `(${value.valid ? "valid" : "invalid"})`);
36
+ values = values.filter(x => !x.path.startsWith(LOCAL_DOMAIN_PATH));
37
+ if (values.length > 0) {
38
+ let now = Date.now();
39
+ let oldest = Math.min(...values.map(x => x.time.time).filter(x => x), now);
40
+ group(ClientWatcher.DEBUG_READS_EXPANDED)(`(${now - epoch}ms, ${now}) Read count ${values.length}, ${(Date.now() - oldest).toFixed()}ms old`);
41
+ for (let value of values) {
42
+ console.log(blue(debugPathValuePath(value)), "===", pathValueSerializer.getPathValue(value), `(${value.valid ? "valid" : "invalid"})`);
43
+ }
44
+ console.groupEnd();
41
45
  }
42
- console.groupEnd();
43
46
  }
44
47
  if (!ActionsHistory.LOG_ACTION_HISTORY) return;
45
48
  }
46
49
  public static OnWrite(values: PathValue[]) {
47
50
  if (ClientWatcher.DEBUG_WRITES) {
48
- let now = Date.now();
49
- group(ClientWatcher.DEBUG_WRITES_EXPANDED)(green(`(${now - epoch}ms, ${now}) Committing writes ${values.length}`));
50
- for (let value of values) {
51
- console.log(green(debugPathValuePath(value)), "=", pathValueSerializer.getPathValue(value), `(${value.valid ? "valid" : "invalid"})`);
51
+ values = values.filter(x => !x.path.startsWith(LOCAL_DOMAIN_PATH));
52
+ if (values.length > 0) {
53
+ let now = Date.now();
54
+ group(ClientWatcher.DEBUG_WRITES_EXPANDED)(green(`(${now - epoch}ms, ${now}) Committing writes ${values.length}`));
55
+ for (let value of values) {
56
+ console.log(green(debugPathValuePath(value)), "=", pathValueSerializer.getPathValue(value), `(${value.valid ? "valid" : "invalid"})`);
57
+ }
58
+ console.groupEnd();
52
59
  }
53
- console.groupEnd();
54
60
  }
55
61
  if (!ActionsHistory.LOG_ACTION_HISTORY) return;
56
62
  }