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 +1 -1
- package/src/-c-identity/IdentityController.ts +3 -2
- 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 +40 -16
- package/test.ts +40 -7
package/package.json
CHANGED
|
@@ -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
|
|
239
|
-
|
|
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
|
|
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,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
|
-
|
|
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
|
-
|
|
8
|
+
Hmm... we ask to watch it, but the watch doesn't show up on the other side? Fuck...
|
|
17
9
|
|
|
18
10
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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
|
|
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)
|