querysub 0.391.0 → 0.392.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 +2 -2
- package/src/0-path-value-core/NodePathAuthorities.ts +21 -6
- package/src/deployManager/components/ServiceDetailPage.tsx +4 -2
- package/src/deployManager/machineSchema.ts +2 -0
- package/src/diagnostics/logs/lifeCycleAnalysis/lifeCycles.tsx +11 -52
- package/src/storage/LZ4.ts +2 -32
- package/test.ts +30 -3
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "querysub",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.392.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",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"pako": "^2.1.0",
|
|
58
58
|
"peggy": "^5.0.6",
|
|
59
59
|
"querysub": "^0.357.0",
|
|
60
|
-
"socket-function": "^1.1.
|
|
60
|
+
"socket-function": "^1.1.4",
|
|
61
61
|
"terser": "^5.31.0",
|
|
62
62
|
"typesafecss": "^0.28.0",
|
|
63
63
|
"yaml": "^2.5.0",
|
|
@@ -2,12 +2,12 @@ import { SocketFunction } from "socket-function/SocketFunction";
|
|
|
2
2
|
import { measureBlock, measureFnc, measureWrap } from "socket-function/src/profiling/measure";
|
|
3
3
|
import { errorToUndefined, errorToUndefinedSilent, ignoreErrors, logErrors, timeoutToUndefined, timeoutToUndefinedSilent } from "../errors";
|
|
4
4
|
import { PromiseObj } from "../promise";
|
|
5
|
-
import { getAllNodeIds, getBrowserUrlNode, getOwnNodeId, isNodeDiscoveryLogging, isOwnNodeId, onNodeDiscoveryReady, triggerNodeChange, watchDeltaNodeIds, watchNodeIds } from "../-f-node-discovery/NodeDiscovery";
|
|
5
|
+
import { getAllNodeIds, getBrowserUrlNode, getOwnNodeId, isNodeDiscoveryLogging, isNodeIdLocal, isOwnNodeId, onNodeDiscoveryReady, triggerNodeChange, watchDeltaNodeIds, watchNodeIds } from "../-f-node-discovery/NodeDiscovery";
|
|
6
6
|
import { PathValueController } from "./PathValueController";
|
|
7
7
|
import { MAX_ACCEPTED_AUTHORITY_STARTUP_TIME, PathValueSnapshot, STARTUP_CUTOFF_TIME, authorityStorage, matchesParentRangeFilterPart } from "./pathValueCore";
|
|
8
8
|
import { pathValueArchives } from "./pathValueArchives";
|
|
9
9
|
import { deepCloneJSON, isNode, sha256Hash, sort, timeInMinute, timeInSecond } from "socket-function/src/misc";
|
|
10
|
-
import { delay, runInfinitePoll } from "socket-function/src/batching";
|
|
10
|
+
import { delay, runInSerial, runInfinitePoll } from "socket-function/src/batching";
|
|
11
11
|
import { blue, green, magenta, red, yellow } from "socket-function/src/formatting/logColors";
|
|
12
12
|
import debugbreak from "debugbreak";
|
|
13
13
|
import { getNodeIdFromLocation, getNodeIdIP, getNodeIdLocation } from "socket-function/src/nodeCache";
|
|
@@ -22,6 +22,7 @@ import { IdentityController_getCurrentReconnectNodeIdAssert, IdentityController_
|
|
|
22
22
|
import { getBufferFraction, getBufferInt, getShortNumber } from "../bits";
|
|
23
23
|
import { devDebugbreak, getDomain, isDevDebugbreak } from "../config";
|
|
24
24
|
import { waitForFirstTimeSync } from "socket-function/time/trueTimeShim";
|
|
25
|
+
import { testTCPIsListening } from "socket-function/src/networking";
|
|
25
26
|
|
|
26
27
|
export const LOCAL_DOMAIN = "LOCAL";
|
|
27
28
|
export const LOCAL_DOMAIN_PATH = getPathStr1(LOCAL_DOMAIN);
|
|
@@ -36,6 +37,7 @@ const MAX_RECONNECT_TIME = timeInMinute * 15;
|
|
|
36
37
|
const RECONNECT_POLL_INTERVAL = 10000;
|
|
37
38
|
|
|
38
39
|
// Poll nodes that appear dead. Without this, if the internet goes down, we might forever ignore nodes.
|
|
40
|
+
// NOTE: During development this might seem excessive, however, after about an hour this should ramp up to the full poll interval of a minute, which should be fairly reasonable.
|
|
39
41
|
const INITIAL_RECOVERY_POLL_INTERVAL = timeInSecond * 5;
|
|
40
42
|
const INITIAL_RECOVERY_RUNS = 100;
|
|
41
43
|
const RECOVERY_POLL_INTERVAL = timeInMinute;
|
|
@@ -213,7 +215,7 @@ class NodePathAuthorities {
|
|
|
213
215
|
let obj = this.authorities.get(nodeId);
|
|
214
216
|
if (!obj) {
|
|
215
217
|
// Might as well use this to add the node, if we don't know about it yet.
|
|
216
|
-
ingestNewNodeIds([nodeId], []);
|
|
218
|
+
void ingestNewNodeIds([nodeId], []);
|
|
217
219
|
return;
|
|
218
220
|
}
|
|
219
221
|
obj.authorityPaths = await PathController.nodes[nodeId].getAuthorityPaths();
|
|
@@ -225,7 +227,7 @@ class NodePathAuthorities {
|
|
|
225
227
|
let firstPromise = new PromiseObj<unknown>();
|
|
226
228
|
logErrors(firstPromise.promise);
|
|
227
229
|
|
|
228
|
-
const ingestNewNodeIds = (newNodeIds: string[], removedNodeIds: string[]) => {
|
|
230
|
+
const ingestNewNodeIds = runInSerial(async (newNodeIds: string[], removedNodeIds: string[]) => {
|
|
229
231
|
for (let nodeId of removedNodeIds) {
|
|
230
232
|
this.authorities.delete(nodeId);
|
|
231
233
|
}
|
|
@@ -249,6 +251,18 @@ class NodePathAuthorities {
|
|
|
249
251
|
return;
|
|
250
252
|
}
|
|
251
253
|
|
|
254
|
+
// Do an initial test by just opening a TCP connection. This will help filter out a lot of dead nodes immediately. Which, in turn, avoids having to create our initial connection signature, which saves a lot of time.
|
|
255
|
+
if (isNode()) {
|
|
256
|
+
let nodeIdObj = getNodeIdLocation(nodeId);
|
|
257
|
+
if (!nodeIdObj) {
|
|
258
|
+
console.error(`Bad nodeId ${nodeId}`);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
let isListening = await testTCPIsListening(nodeIdObj.address, nodeIdObj.port);
|
|
262
|
+
if (!isListening) return;
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
console.log(blue(`Checking for status of node ${nodeId}`));
|
|
252
266
|
let time = Date.now();
|
|
253
267
|
let createTime = await timeoutToUndefinedSilent(POLL_RATE, PathController.nodes[nodeId].getCreateTime());
|
|
254
268
|
if (createTime === undefined) {
|
|
@@ -258,6 +272,7 @@ class NodePathAuthorities {
|
|
|
258
272
|
console.log(yellow(`Node didn't respond to getCreateTime`), { nodeId });
|
|
259
273
|
}
|
|
260
274
|
}
|
|
275
|
+
console.log(yellow(`Node ${nodeId} is not available`));
|
|
261
276
|
this.previouslyNotAvailableNodes.add(nodeId);
|
|
262
277
|
return;
|
|
263
278
|
}
|
|
@@ -311,7 +326,7 @@ class NodePathAuthorities {
|
|
|
311
326
|
if (isCurrentFirst) {
|
|
312
327
|
firstPromise.resolve(promise);
|
|
313
328
|
}
|
|
314
|
-
};
|
|
329
|
+
});
|
|
315
330
|
|
|
316
331
|
let time = Date.now();
|
|
317
332
|
watchDeltaNodeIds(obj => ingestNewNodeIds(obj.newNodeIds, obj.removedNodeIds));
|
|
@@ -340,7 +355,7 @@ class NodePathAuthorities {
|
|
|
340
355
|
nonExistentNodes.push(node);
|
|
341
356
|
}
|
|
342
357
|
// Pretend all dead nodes are new
|
|
343
|
-
ingestNewNodeIds(nonExistentNodes, []);
|
|
358
|
+
await ingestNewNodeIds(nonExistentNodes, []);
|
|
344
359
|
});
|
|
345
360
|
|
|
346
361
|
let readyCount = 0;
|
|
@@ -23,6 +23,8 @@ import { TypedConfigEditor } from "../../library-components/TypedConfigEditor";
|
|
|
23
23
|
import { managementPageURL } from "../../diagnostics/managementPages";
|
|
24
24
|
import { getLogViewerParams } from "../../diagnostics/logs/IndexedLogs/LogViewerParams";
|
|
25
25
|
import { getScreenName } from "../machineApplyMainCode";
|
|
26
|
+
import { getOwnThreadId } from "../../-f-node-discovery/NodeDiscovery";
|
|
27
|
+
import { decodeNodeId } from "../../-a-auth/certs";
|
|
26
28
|
|
|
27
29
|
|
|
28
30
|
|
|
@@ -338,12 +340,12 @@ export class ServiceDetailPage extends qreact.Component {
|
|
|
338
340
|
<div
|
|
339
341
|
className={css.hbox(12)}
|
|
340
342
|
>
|
|
341
|
-
<div className={css.
|
|
343
|
+
<div className={css.hbox(5)}>
|
|
342
344
|
<div>
|
|
343
345
|
{screenName}
|
|
344
346
|
</div>
|
|
345
347
|
<div>
|
|
346
|
-
{machineId} ({machineInfo.info["getExternalIP"]})
|
|
348
|
+
{serviceInfo?.nodeId || machineId} ({machineInfo.info["getExternalIP"]})
|
|
347
349
|
</div>
|
|
348
350
|
</div>
|
|
349
351
|
{isDisabled && (
|
|
@@ -59,6 +59,8 @@ export type MachineInfo = {
|
|
|
59
59
|
// Only times launched for the current applyNodeId, but... still very useful.
|
|
60
60
|
totalTimesLaunched: number;
|
|
61
61
|
// Might take a while to set (15 minutes or more). It's better to look in the nodes list and find the one that seems to match (maybe looking at the startup path to find it?)
|
|
62
|
+
// TODO: Make this work correctly when we deploy multiple instances (the node needs to know which index it is so it can write to a unique file).
|
|
63
|
+
// - We could probably set environment variables in the screen?
|
|
62
64
|
nodeId: string;
|
|
63
65
|
}>;
|
|
64
66
|
};
|
|
@@ -1,64 +1,20 @@
|
|
|
1
1
|
/*
|
|
2
2
|
todonext
|
|
3
3
|
|
|
4
|
-
OKAY! CORE CONCEPTS
|
|
5
|
-
- Index needs to be fast searched, so it's uncompressed form has to be small
|
|
6
|
-
- Nesting compression is fine, as long as it's small enough that each search decompresses very little
|
|
7
|
-
- The enemy is storing every single position, we get around this by:
|
|
8
|
-
- In-exact matches, only storing block indexes.
|
|
9
|
-
- Requires full search to verify, but... in practice has low false hit rate
|
|
10
|
-
- Input key (unit) is only inexact matched.
|
|
11
|
-
- Input query almost always has more than 1 byte of uniqueness. This means if even part is only 50% accurate, we can still get very high hit rates (8 bytes means 255/256 hit rate!)
|
|
12
4
|
|
|
13
5
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
IMPORTANT! Now I am properly calling shutdown, so none of the streamed logs should ever break. The code should be waiting until everything's fully flushed before it allows the shutdown handler to finish running. If we see any more errors, we need to investigate them.
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
//todonext
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
3) "guessed node id" is never working? At least, on the machine's page it is ALWAYS empty, even though it should be almost always detected...
|
|
28
|
-
|
|
29
|
-
4) Verify it works again, when we have multiple servers
|
|
30
|
-
|
|
31
|
-
Invalid addition, you tried to add 1 + Infinity, operands must be valid numbers
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
0) Add LZ4 compression to socket-function by default
|
|
36
|
-
- Setup local tester for it to start
|
|
37
|
-
- with non-synced endpoint, just surely socket-function
|
|
38
|
-
- purely nodejs, just talking to itself
|
|
39
|
-
- Allow setting "compress" to "none" or "lz4" or "zip" or "zip0" or "zip3", etc, for levels.
|
|
40
|
-
- default is "lz4"
|
|
41
|
-
- REQUIRES feature checking the remote, to make sure it is new enough to accept this.
|
|
42
|
-
- A generic thing which gets the version is probably fine.
|
|
43
|
-
- When decoding, I think we should just check the first byte. It should be a magic number which tells us which compression it's using.
|
|
44
|
-
- LZ4 compression is fast enough that this should cause basically no overhead, and in many cases greatly reduce the bandwidth (which will increase the speed).
|
|
45
|
-
- We're gonna have to investigate how we're sending buffers anyway. I think this should be easy, but we
|
|
46
|
-
0.1) Verify the size difference with some local testing
|
|
47
|
-
- ALSO, verify the processing overhead is acceptable.
|
|
48
|
-
1) Deploy, which SHOULD be backwards compatible with everything?
|
|
6
|
+
2) Publish socket-function and update to use the latest
|
|
7
|
+
1) Deploy everything and make sure the servers run
|
|
49
8
|
|
|
50
9
|
|
|
51
10
|
-1) Change PathValueSerialize to use LZ4
|
|
52
|
-
- I think we already have a compression flag here, so it should be easy enough to just
|
|
53
|
-
|
|
54
|
-
-
|
|
55
|
-
|
|
11
|
+
- I think we already have a compression flag here, so it should be easy enough to just change it to support and use LZ4 by default
|
|
12
|
+
-3) Send progress/timing when sending very large messages?
|
|
13
|
+
- Maybe related to the remote => local synchronization. Maybe we're doing multi- many calls, in which case we can't detect it. But if we're doing just one call... Then the socket function system should realize.
|
|
14
|
+
- And even if it's the synchronization code which is doing many calls, then it should probably log progress and timing (on both ends)
|
|
15
|
+
-2) Fix synchronization speed from remote to local.
|
|
16
|
+
- Last time it seemed very slow
|
|
56
17
|
- We might need to jump into the server and look at its profiles there by forcing them to omit, which isn't too hard, but it is kind of annoying.
|
|
57
|
-
|
|
58
|
-
-3) During server startup (when there is a remote server), we spend a lot of time in identityHook.
|
|
59
|
-
- BUT... Our check to see if the async call that we're doing underneath is taking more than 200 milliseconds, is never firing. Even though apparently plenty of calls are taking over a second in total, so what why is it slow? Are some of the synchronous functions we're calling slow?
|
|
60
|
-
- Basically, there's nowhere that could be slow, and we're timing it, and it times as being fast. However, in the profiles, it shows up as being slow.
|
|
61
|
-
- It could also be an asynchronous issue, so some other timing is getting alias to it. That is feasible...
|
|
62
18
|
*/
|
|
63
19
|
|
|
64
20
|
|
|
@@ -73,6 +29,9 @@ Invalid addition, you tried to add 1 + Infinity, operands must be valid numbers
|
|
|
73
29
|
// - I mean, we have to implement it without caching anyways to start, so we can just do that, and then cache it later if we see the need...
|
|
74
30
|
// - WELL, we need SOME kind of caching... Maybe... we DO use a strict time range, and then... if we've scanned a pending file in the past, we can cache that, because we know we've got all of it's values. Hmm...
|
|
75
31
|
// - Use lifecycles to debug rejections. To a point, until it is likely we are out of sync, then we should write the sync verification code.
|
|
32
|
+
// - Maybe it isn't necessarily rejections, but definitely if we're running stuff locally and remotely it breaks. Could it be the case that we can't run the path value server both locally and remotely? And if so, we really should prevent this in some way.
|
|
33
|
+
// - Annoyingly enough, we're actually going to have to add a case where we can scan the local logs as well as the remote logs to debug running it locally...
|
|
34
|
+
// - ALTHOUGH, It does fail just if we have the local running server, But we're on the remote site. So we might be able to just debug it from that life cycle, from the remote life cycles.
|
|
76
35
|
|
|
77
36
|
|
|
78
37
|
// todonext;
|
package/src/storage/LZ4.ts
CHANGED
|
@@ -1,32 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import { measureFnc } from "socket-function/src/profiling/measure";
|
|
4
|
-
export class LZ4 {
|
|
5
|
-
@measureFnc
|
|
6
|
-
static compress(data: Buffer): Buffer {
|
|
7
|
-
return this.compressUntracked(data);
|
|
8
|
-
}
|
|
9
|
-
static compressUntracked(data: Buffer): Buffer {
|
|
10
|
-
try {
|
|
11
|
-
return Buffer.from(lz4_stream.compress(data));
|
|
12
|
-
} catch (e) {
|
|
13
|
-
// Rethrow non errors as properly wrapped errors
|
|
14
|
-
if (!(e && e instanceof Error)) {
|
|
15
|
-
throw new Error(`Error compressing LZ4: ${e}`);
|
|
16
|
-
}
|
|
17
|
-
throw e;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
@measureFnc
|
|
21
|
-
static decompress(data: Buffer): Buffer {
|
|
22
|
-
try {
|
|
23
|
-
return Buffer.from(lz4_stream.decompress(data));
|
|
24
|
-
} catch (e) {
|
|
25
|
-
// Rethrow non errors as properly wrapped errors
|
|
26
|
-
if (!(e && e instanceof Error)) {
|
|
27
|
-
throw new Error(`Error decompressing LZ4: ${e}`);
|
|
28
|
-
}
|
|
29
|
-
throw e;
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
}
|
|
1
|
+
import { LZ4 } from "socket-function/src/lz4/LZ4";
|
|
2
|
+
export { LZ4 };
|
package/test.ts
CHANGED
|
@@ -6,12 +6,39 @@ import "./inject";
|
|
|
6
6
|
import { Querysub } from "./src/4-querysub/QuerysubController";
|
|
7
7
|
import { getErrorLogs, getLoggers2Async } from "./src/diagnostics/logs/diskLogger";
|
|
8
8
|
import { watchAllValues } from "./src/diagnostics/logs/errorNotifications2/logWatcher";
|
|
9
|
+
import { SocketFunction } from "socket-function/SocketFunction";
|
|
10
|
+
import fs from "fs";
|
|
11
|
+
import { getControllerNodeId, getControllerNodeIdList } from "./src/-g-core-values/NodeCapabilities";
|
|
12
|
+
import { delay } from "socket-function/src/batching";
|
|
13
|
+
import { timeInSecond } from "socket-function/src/misc";
|
|
9
14
|
|
|
15
|
+
const path = "D:/repos/qs-cyoa/database-storage/disklogs/1757030400000-1757116800000.log";
|
|
16
|
+
class TestControllerBase {
|
|
17
|
+
async test() {
|
|
18
|
+
return await fs.promises.readFile(path, "utf8");
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const TestController = SocketFunction.register("TestController-019ca5fa-52ef-73bf-b918-4d9cde51b618", new TestControllerBase(), () => ({
|
|
23
|
+
test: {
|
|
24
|
+
compress: true,
|
|
25
|
+
},
|
|
26
|
+
}));
|
|
10
27
|
async function main() {
|
|
11
28
|
await Querysub.hostService("testwatcher");
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
29
|
+
|
|
30
|
+
while (true) {
|
|
31
|
+
try {
|
|
32
|
+
let otherNode = await getControllerNodeId(TestController);
|
|
33
|
+
if (!otherNode) throw new Error("No other node found");
|
|
34
|
+
let result = await TestController.nodes[otherNode].test();
|
|
35
|
+
let correct = await fs.promises.readFile(path, "utf8");
|
|
36
|
+
if (result !== correct) throw new Error("Result does not match correct");
|
|
37
|
+
console.log(`Received ${result.length} bytes from ${otherNode}`);
|
|
38
|
+
} catch (e: any) {
|
|
39
|
+
console.error(e);
|
|
40
|
+
}
|
|
41
|
+
await delay(timeInSecond * 1);
|
|
15
42
|
}
|
|
16
43
|
}
|
|
17
44
|
|