querysub 0.412.0 → 0.414.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.412.0",
3
+ "version": "0.414.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,6 +19,7 @@ import { waitForFirstTimeSync } from "socket-function/time/trueTimeShim";
19
19
  import { red } from "socket-function/src/formatting/logColors";
20
20
  import { isNode } from "typesafecss";
21
21
  import { areNodeIdsEqual, getOwnNodeId, getOwnThreadId } from "../-f-node-discovery/NodeDiscovery";
22
+ import { timeInMinute } from "socket-function/src/misc";
22
23
 
23
24
  let callerInfo = new Map<CallerContext, {
24
25
  reconnectNodeId: string | undefined;
@@ -235,8 +236,8 @@ const changeIdentityOnce = cacheWeak(async function changeIdentityOnce(connectio
235
236
  };
236
237
  let signature = sign(threadKeyCert, payload);
237
238
  await timeoutToError(
238
- // NOTE: This timeout has to be small as if we try to connect to a node to send it something time sensitive such as a PathValue and it takes too long it might result in us having a PathValue which is expired. The threshold is around 60 seconds and so we want to build a timeout calling a few different nodes before the PathValue expires.
239
- 10 * 1000,
239
+ // NOTE: This used to be small, but we cache this, so it would mean a node on startup would time out, and then we would refuse to talk to it ever again. So... this can't be small
240
+ timeInMinute * 3,
240
241
  IdentityController.nodes[nodeId].changeIdentity(signature, payload),
241
242
  () => new Error(`Timeout calling changeIdentity for ${nodeId}`)
242
243
  );
@@ -21,6 +21,8 @@ import { decodeNodeId } from "../-a-auth/certs";
21
21
  import { decodeParentFilter, encodeParentFilter } from "./hackedPackedPathParentFiltering";
22
22
  import { deepCloneCborx } from "../misc/cloneHelpers";
23
23
  import { removeRange } from "../rangeMath";
24
+ import { registerShutdownHandler } from "../diagnostics/periodic";
25
+ import { getCallFactory } from "socket-function/src/nodeCache";
24
26
  setImmediate(() => import("../1-path-client/RemoteWatcher"));
25
27
  setImmediate(() => import("../4-querysub/Querysub"));
26
28
 
@@ -48,6 +50,7 @@ class PathValueCommitter {
48
50
  void promise.finally(() => this.pendingCommits.delete(promise));
49
51
  }
50
52
  public async waitForValuesToCommit() {
53
+ await this.broadcastValues({ values: new Set() });
51
54
  await Promise.all(this.pendingCommits);
52
55
  // HACK: Wait a bit more, for the websocket to send the values (and for some batching, etc, etc)
53
56
  //await new Promise(resolve => setTimeout(resolve, 100));
@@ -143,6 +146,7 @@ class PathValueCommitter {
143
146
  tryCount?: number;
144
147
  }[]) {
145
148
  let values = new Set(valuesBatched.flatMap(x => Array.from(x.values)));
149
+ if (values.size === 0) return;
146
150
  let tryCountPerValue = new Map<PathValue, number>();
147
151
  for (let list of valuesBatched) {
148
152
  for (let value of list.values) {
@@ -216,20 +220,24 @@ class PathValueCommitter {
216
220
 
217
221
  // Don't send to bad nodes for 60 seconds
218
222
  const nodeIgnoreTime = Date.now() - 1000 * 60;
223
+
219
224
  let promises = Array.from(valuesPerOtherAuthority.entries()).map(async ([otherAuthority, values]) => {
220
225
 
221
226
  let disconnected = SocketFunction.getLastDisconnectTime(otherAuthority);
222
- if (disconnected && disconnected > nodeIgnoreTime) {
227
+ if (!SocketFunction.isNodeConnected(otherAuthority) && disconnected && disconnected > nodeIgnoreTime) {
223
228
  // If it disconnected recently... don't send to it for a little bit, so we don't spend
224
229
  // all of our time spamming disconnected nodes
230
+ console.log(`disconnected at ${disconnected} > ${nodeIgnoreTime}`);
225
231
  return;
226
232
  }
227
233
 
228
234
  let isTrusted = await isTrustedByNode(otherAuthority);
229
235
  if (!isTrusted) {
236
+ console.log(`not trusted`);
230
237
  throw new Error(`Tried to write to paths on authorities not trusted by us. You probably need to call a function instead of directly writing to the server schema. Authority: ${otherAuthority}, Paths: ${values.map(x => getPathFromStr(x.path).join(".")).join(", ")}`);
231
238
  }
232
239
 
240
+
233
241
  // NOTE: We don't retry on failure, because... we broadcasted it anyways, AND this is supposed
234
242
  // to be on a trusted node (which should always be a server), so... either our network went
235
243
  // down, or all nodes went down. Either way, it likely won't fix itself quickly, and so these
@@ -434,3 +442,7 @@ class PathValueCommitter {
434
442
  }
435
443
 
436
444
  export const pathValueCommitter = new PathValueCommitter();
445
+
446
+ registerShutdownHandler(async () => {
447
+ await pathValueCommitter.waitForValuesToCommit();
448
+ });
@@ -27,6 +27,7 @@ class ValidStateComputer {
27
27
  parentSyncs: { parentPath: string; sourceNodeId: string }[];
28
28
  initialTriggers: { values: Set<string>; parentPaths: Set<string> };
29
29
  doNotArchive?: boolean;
30
+
30
31
  }) {
31
32
  let { pathValues, parentSyncs, initialTriggers, doNotArchive } = config;
32
33
 
@@ -93,7 +94,6 @@ class ValidStateComputer {
93
94
  }
94
95
 
95
96
 
96
-
97
97
  let initialPrevValidStates: Map<PathValue, boolean | undefined> | undefined = this.capturePrevValidStates(ourValues);
98
98
  authorityStorage.ingestValues(ourValues, { doNotArchive });
99
99
 
@@ -104,6 +104,17 @@ class ValidStateComputer {
104
104
  let valuesChanged = new Set<PathValue>(pathValues);
105
105
  let dependenciesChanged = new Set<PathValue>(ourValues);
106
106
  let alreadyTriggered = new Set<PathValue>(dependenciesChanged);
107
+ // NOTE: For our own values, we don't care what the input valid state is, compute valid states will calculate if it's changed.
108
+ for (let value of notOurValues) {
109
+ // NOTE: We could be smarter and determine if the value we're receiving has a valid state change. However, it's probably fine.
110
+ // NOTE: Any watchers will get their valid states computed. I think that's really the only reason to watch something is if we own it and want to compute its valid state.
111
+ let watchers = lockWatcher2.getValuePathWatchers(value);
112
+ for (let watcher of watchers) {
113
+ if (alreadyTriggered.has(watcher)) continue;
114
+ dependenciesChanged.add(watcher);
115
+ alreadyTriggered.add(watcher);
116
+ }
117
+ }
107
118
 
108
119
  let loopCount = 0;
109
120
 
@@ -880,7 +880,7 @@ class AuthorityPathValueStorage {
880
880
 
881
881
  let goldenTime = now - MAX_CHANGE_AGE;
882
882
  // NOTE: Values with no locks are implicitly golden, as they can't be rejected
883
- let firstGoldenIndex = values.findIndex(x => x.time.time < goldenTime && x.valid || x.lockCount === 0);
883
+ let firstGoldenIndex = values.findIndex(x => x.valid && (x.time.time < goldenTime || x.lockCount === 0));
884
884
  if (firstGoldenIndex < 0) return;
885
885
 
886
886
  // Special case, everything is golden, and the latest value is undefined, and is behind the GC
@@ -260,6 +260,9 @@ function predictCallBase(config: {
260
260
  }),
261
261
  };
262
262
 
263
+ // Abort, and don't predict
264
+ if (didCancel) return predictions;
265
+
263
266
  for (let value of predictions.writes) {
264
267
  value.source = debugName;
265
268
  }
@@ -291,6 +294,7 @@ function predictCallBase(config: {
291
294
  pathValues: predictions.writes,
292
295
  parentSyncs: [],
293
296
  initialTriggers: { values: new Set(), parentPaths: new Set() },
297
+ doNotArchive: true,
294
298
  });
295
299
 
296
300
  if (Querysub.DEBUG_PREDICTIONS) {
@@ -302,21 +306,24 @@ function predictCallBase(config: {
302
306
 
303
307
  let didCancel = false;
304
308
  function rejectPrediction() {
309
+ predictions;
305
310
  if (didCancel) return;
306
311
  didCancel = true;
312
+ if (!predictions) return;
307
313
  // Reject our predictions, as the call likely never got committed, so it will never be written
308
314
  validStateComputer.ingestValuesAndValidStates({
309
- pathValues: [{
310
- path: pathResultWrite,
311
- value: undefined,
315
+ pathValues: predictions.writes.map(write => ({
316
+ path: write.path,
317
+ value: write.value,
312
318
  locks: [],
313
319
  lockCount: 0,
314
320
  valid: false,
315
321
  time: predictResultWrite.time,
316
322
  isTransparent: false,
317
- }],
323
+ })),
318
324
  parentSyncs: [],
319
325
  initialTriggers: { values: new Set(), parentPaths: new Set() },
326
+ doNotArchive: true,
320
327
  });
321
328
  }
322
329
 
@@ -56,12 +56,12 @@ export class NodeConnectionsPage extends qreact.Component {
56
56
 
57
57
  return (
58
58
  <div class={css.pad2(10).vbox(20).fillWidth}>
59
- <div class={css.vbox(10).fillWidth}>
60
- <h1>Connectable Nodes</h1>
59
+ <h1>Connectable Nodes</h1>
60
+ <div class={css.hbox(20).wrap.fillWidth}>
61
61
  {connectableNodes.map(node => {
62
62
  let color = getColorForNodeId(node.friendlyNodeId);
63
63
  return (
64
- <div class={css.vbox(5).pad2(10).maxHeight(600).overflowAuto.bord(1, { h: 0, s: 0, l: 80 })}>
64
+ <div class={css.vbox(5).pad2(10).maxHeight(400).overflowAuto.bord(1, { h: 0, s: 0, l: 80 })}>
65
65
  <div class={css.vbox(3)}>
66
66
  {node.entryPoint && (
67
67
  <div class={css.fontSize(14).fontWeight("bold")}>
@@ -3,7 +3,7 @@ import { t } from "../../../2-proxy/schema2";
3
3
  import { css } from "typesafecss";
4
4
  import { Querysub } from "../../../4-querysub/QuerysubController";
5
5
  import { ATag } from "../../../library-components/ATag";
6
- import { managementPageURL } from "../../managementPages";
6
+ import { managementPageURL, showingManagementURL } from "../../managementPages";
7
7
  import { LogDatumRenderer } from "./ErrorNotificationPage";
8
8
  import { getErrorNotificationsManager } from "./errorWatcher";
9
9
 
@@ -22,7 +22,10 @@ export class ErrorWarning extends qreact.Component {
22
22
  let firstError = this.manager.state.unmatchedErrors[0];
23
23
 
24
24
  return <div className={css.hbox(4).alignItems("start").hsl(0, 0, 90).bord2(0, 0, 85).pad2(4, 2).hslcolor(0, 0, 0)}>
25
- <ATag className={css.paddingTop(5).flexShrink0} values={[managementPageURL.getOverride("ErrorNotificationPage")]}>
25
+ <ATag className={css.paddingTop(5).flexShrink0} values={[
26
+ showingManagementURL.getOverride(true),
27
+ managementPageURL.getOverride("ErrorNotificationPage"),
28
+ ]}>
26
29
  Suppress
27
30
  </ATag>
28
31
  <div className={css.paddingTop(5)}>|</div>
package/tempnotes.txt CHANGED
@@ -1,32 +1,56 @@
1
1
 
2
- 1) The machines think they're using a local IP, so something's wrong with how they're determining their own IP.
3
- 2) The machines all think their port is the same, but they shouldn't think that.
4
2
 
5
- Oh, we broke our live file at some time. The host of everything is saying it's the same thing. It's just wrong. That's not the host based on the node ID. Where did that even come from?
3
+ Local CYOA + Local FunctionRunner works
6
4
 
7
- Oh, our live hash in the edge node index file is wrong. Let's see if it's wrong in the database or if we're just creating the file wrong.
8
- - As in, it's corrupted, as in it's the string object representation.
9
5
 
10
- 1) Test local querysub server
11
- 1.0) If it doesn't work, try to run the test script with a simple watcher to see if we can sync paths
12
- 1.1) If it does... run a public querysub server locally?
13
6
 
14
- Well, let's run locally and see if our local query subserver works.
15
7
 
16
- It's not working. The server isn't starting up.
8
+ Hmm... we ask to watch it, but the watch doesn't show up on the other side? Fuck...
17
9
 
18
10
 
19
- 3) Deploy EVERYTHING (all services) to remote servers
20
- 3.0) deploy the path value servers shard it like we were testing locally
21
- - 4, for redundancy and sharding
22
- 3.1) Make sure the gc services are deployed too
23
- 5) Deploy disk audit service too
24
- `yarn audit-disk-values` should work
11
+
12
+
13
+ So we have the remote watcher, so it looks like we were watching it, but we don't think it's synced, so we clearly didn't get a value back.
14
+
15
+ WATCHER FAILED TO SYNC findFunctionsToCall DID NOT RECEIVE PATH VALUES. This means PathValueServer is not responding to watches, either to specific paths, or for all paths [
16
+ '.,querysubtest._com.,',
17
+ '.,querysubtest._com.,PathFunctionRunner.,'
18
+ ] [ '.,querysubtest._com.,PathFunctionRunner.,' ] [Function: findFunctionsToCall]
19
+ Node list is missing nodes, resyncing node {
20
+ missingNodes: [ '716181d6570f7b55.a794fbcf7b104c68.querysubtest.com:34689' ],
21
+
22
+
23
+ UGH... poking fixed it. WHICH MEANS, we have to break it again, and get into the bad state again!
24
+ - Ugh...
25
+
26
+ -1) We got into a bad state where The query sub nodes weren't really listening or it's it th they had zombie watchers that hadn't finished synchronizing, but it wasn't complaining about being able to un to find any any paths. There were no unwatched paths
27
+ - It fixed when we poked them.
28
+
29
+ 0) If it fails after redeploying, disable the server and run everything locally again, as we made a lot of changes, and so it might not even work locally.
30
+
31
+ -1) Get a test script that doesn't even use the function runner working.
32
+
33
+ 0) Get our front end working. I guess we need to run the function runner locally, and then if that works, we need to figure out why the remote function runner isn't working.
34
+ - Also, see if we run the local query subserver and local function runner, if those writes can be seen remotely. If it's a remote function runner problem but not a remote path value server problem, we should see the writes.
35
+
36
+ -1) Fix the remote servers not being able to send values to our local path value server. I don't know why it wouldn't work... They should have changed the name knowing our local name.
25
37
 
26
38
  1) Use connection page to verify server can talk to our locally hosted server
27
39
  1.1) Verify by breaking into our local server that we are receiving values written on the remote server in the local server.
28
40
  - This is extremely important. Without this, we can't run a local server. There's all kinds of issues, but our socket function changes should make this so this just works.
29
41
 
42
+ 0) SHARD THE FUNCTION RUNNER!
43
+ - And secondary sharding for backup...
44
+ - First shard locally
45
+ - Test on our sync test page (Otherwise all the writes are going to go to the same function runner anyway, so it's not a good test.
46
+
47
+ -2) Errors are not being automatically grouped.
48
+ - We are getting notifications though, which is weird. The notifications are supposed to be automatically grouping it. We should probably look at its logs and see why.
49
+
50
+ 0) We just are constantly "Node list is missing nodes, resyncing node"
51
+ - We should check the logs. I'm assuming what happened is that at some point some server thought it was the wrong node ID and deleted it?
52
+ - It's generally the same ones, except new ones get added.
53
+
30
54
  I think even if we run into some occasional issues, we should just power through and try to fix them later. Because I'm sick of working on the framework...
31
55
 
32
56
  MONTHLY SUMMARY!
package/test.ts CHANGED
@@ -6,22 +6,55 @@ import { SocketFunction } from "socket-function/SocketFunction";
6
6
  import { getControllerNodeIdList } from "./src/-g-core-values/NodeCapabilities";
7
7
  import { delay } from "socket-function/src/batching";
8
8
  import { green, yellow } from "socket-function/src/formatting/logColors";
9
- import { Querysub } from "./src/4-querysub/Querysub";
9
+ import { Querysub, t } from "./src/4-querysub/Querysub";
10
10
  import { pathValueArchives } from "./src/0-path-value-core/pathValueArchives";
11
11
  import { getAllAuthoritySpec } from "./src/0-path-value-core/PathRouterServerAuthoritySpec";
12
12
  import { deploySchema } from "./src/4-deploy/deploySchema";
13
13
  import { getDomain } from "./src/config";
14
+ import { getProxyPath } from "./src/2-proxy/pathValueProxy";
15
+ import { ClientWatcher } from "./src/1-path-client/pathValueClientWatcher";
16
+ import { RemoteWatcher } from "./src/1-path-client/RemoteWatcher";
17
+ import { PathRouter } from "./src/0-path-value-core/PathRouter";
18
+ import { shutdown } from "./src/diagnostics/periodic";
14
19
 
20
+ let tempTestSchema = Querysub.createSchema({
21
+ value: t.number,
22
+ })({
23
+ domainName: getDomain(),
24
+ moduleId: "tempTest",
25
+ module: module,
26
+ functions: {},
27
+ });
15
28
 
16
29
  async function main() {
17
30
  await Querysub.hostService("test");
18
31
 
19
- let test = await Querysub.commitAsync(() => {
20
- let live = deploySchema()[getDomain()].deploy.live.hash;
21
- console.log({ live });
22
- return String(live);
23
- });
24
- console.log({ test });
32
+ // let path = getProxyPath(() => tempTestSchema.data().value);
33
+ // console.log({ path });
34
+ // let authorities = PathRouter.getAllAuthorities(path);
35
+ // for (let authority of authorities) {
36
+ // console.log({ authority });
37
+ // }
38
+
39
+ // ClientWatcher.DEBUG_READS = true;
40
+ // ClientWatcher.DEBUG_WRITES = true;
41
+ // RemoteWatcher.DEBUG = true;
42
+
43
+ let value = await Querysub.commitAsync(() => tempTestSchema.data().value);
44
+ console.log({ value });
45
+ await Querysub.commitAsync(() => tempTestSchema.data().value++, { doNotStoreWritesAsPredictions: true });
46
+ await delay(3000);
47
+ let value2 = await Querysub.commitAsync(() => tempTestSchema.data().value);
48
+ console.log({ value2 });
49
+
50
+ await shutdown();
51
+
52
+ // let test = await Querysub.commitAsync(() => {
53
+ // let live = deploySchema()[getDomain()].deploy.live.hash;
54
+ // console.log({ live });
55
+ // return String(live);
56
+ // });
57
+ // console.log({ test });
25
58
  }
26
59
 
27
60
  main().catch(console.error)