querysub 0.411.0 → 0.413.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.411.0",
3
+ "version": "0.413.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",
@@ -147,13 +147,16 @@ class IdentityControllerBase {
147
147
  if (!areNodeIdsEqual(payload.serverId, localNodeId)) {
148
148
  throw new Error(`Identity is for another server! The connection is calling us ${localNodeId}, but signature is for ${payload.serverId}`);
149
149
  }
150
- let calledMachineId = getMachineId(payload.serverId);
151
- if (calledMachineId !== "127-0-0-1" && calledMachineId !== getOwnMachineId()) {
152
- throw new Error(`Tried to call a different machine. We are ${getOwnMachineId()}, they called ${calledMachineId}`);
153
- }
154
- let calledThreadId = decodeNodeId(payload.serverId)?.threadId;
155
- if (calledThreadId && calledThreadId !== "127-0-0-1" && calledThreadId !== getOwnThreadId()) {
156
- throw new Error(`Tried to call a different thread. We are ${getOwnThreadId()}, they called ${calledThreadId}`);
150
+ // If they're calling from the browser, then they're not going to be able to use our machine ID, etc. However, they should be calling an actual https node, so it should still be secure for them.
151
+ if (payload.clientIsNode) {
152
+ let calledMachineId = getMachineId(payload.serverId);
153
+ if (calledMachineId !== "127-0-0-1" && calledMachineId !== getOwnMachineId()) {
154
+ throw new Error(`Tried to call a different machine. We are ${getOwnMachineId()}, they called ${calledMachineId}`);
155
+ }
156
+ let calledThreadId = decodeNodeId(payload.serverId)?.threadId;
157
+ if (calledThreadId && calledThreadId !== "127-0-0-1" && calledThreadId !== getOwnThreadId()) {
158
+ throw new Error(`Tried to call a different thread. We are ${getOwnThreadId()}, they called ${calledThreadId}`);
159
+ }
157
160
  }
158
161
 
159
162
 
@@ -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,27 +1,12 @@
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.
2
+ Alright, well, looks like the writes aren't working. It's not sending it to the server at least. Or at least the server isn't receiving it.
4
3
 
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?
4
+ -1) Get a test script that doesn't even use the function runner working.
6
5
 
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.
6
+ 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.
7
+ - 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.
9
8
 
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
-
14
- Well, let's run locally and see if our local query subserver works.
15
-
16
- It's not working. The server isn't starting up.
17
-
18
-
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
9
+ -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
10
 
26
11
  1) Use connection page to verify server can talk to our locally hosted server
27
12
  1.1) Verify by breaking into our local server that we are receiving values written on the remote server in the local server.
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)