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 +1 -1
- package/src/-c-identity/IdentityController.ts +10 -7
- package/src/0-path-value-core/PathValueCommitter.ts +13 -1
- package/src/0-path-value-core/ValidStateComputer.ts +12 -1
- package/src/0-path-value-core/pathValueCore.ts +1 -1
- package/src/4-querysub/querysubPrediction.ts +11 -4
- package/src/diagnostics/NodeConnectionsPage.tsx +3 -3
- package/src/diagnostics/logs/errorNotifications2/ErrorWarning.tsx +5 -2
- package/tempnotes.txt +5 -20
- package/test.ts +40 -7
package/package.json
CHANGED
|
@@ -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
|
-
|
|
151
|
-
if (
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
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
|
|
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:
|
|
311
|
-
value:
|
|
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
|
-
<
|
|
60
|
-
|
|
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(
|
|
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={[
|
|
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
|
-
|
|
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
|
-
|
|
4
|
+
-1) Get a test script that doesn't even use the function runner working.
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
-
|
|
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)
|
|
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
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
});
|
|
24
|
-
|
|
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)
|