querysub 0.14.0 → 0.16.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/-f-node-discovery/NodeDiscovery.ts +48 -79
- package/src/-g-core-values/NodeCapabilities.ts +1 -1
- package/src/0-path-value-core/PathValueCommitter.ts +0 -1
- package/src/1-path-client/pathValueClientWatcher.ts +4 -1
- package/src/2-proxy/PathValueProxyWatcher.ts +61 -20
- package/src/3-path-functions/PathFunctionHelpers.ts +1 -1
- package/src/4-querysub/Querysub.ts +5 -7
- package/src/4-querysub/querysubPrediction.ts +1 -1
- package/src/config.ts +6 -1
- package/src/diagnostics/NodeViewer.tsx +27 -5
- package/src/diagnostics/logs/DiskLoggerPage.tsx +5 -12
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@ import { SocketFunction } from "socket-function/SocketFunction";
|
|
|
2
2
|
import { getArchives } from "../-a-archives/archives";
|
|
3
3
|
import { getDomain, isDevDebugbreak, isNoNetwork } from "../config";
|
|
4
4
|
import { measureWrap } from "socket-function/src/profiling/measure";
|
|
5
|
-
import { isNode, sha256Hash, timeInMinute, timeInSecond } from "socket-function/src/misc";
|
|
5
|
+
import { isNode, sha256Hash, throttleFunction, timeInMinute, timeInSecond } from "socket-function/src/misc";
|
|
6
6
|
import { errorToUndefined, errorToUndefinedSilent, ignoreErrors, logErrors, timeoutToUndefined, timeoutToUndefinedSilent } from "../errors";
|
|
7
7
|
import { requiresNetworkTrustHook } from "../-d-trust/NetworkTrust2";
|
|
8
8
|
import { delay, runInfinitePoll, runInfinitePollCallAtStart } from "socket-function/src/batching";
|
|
@@ -21,9 +21,9 @@ import { getPublicIP } from "../misc/networking";
|
|
|
21
21
|
|
|
22
22
|
import dns from "dns/promises";
|
|
23
23
|
import { isDefined } from "../misc";
|
|
24
|
-
import { diskLog } from "../diagnostics/logs/diskLogger";
|
|
24
|
+
import { diskLog, noDiskLogPrefix } from "../diagnostics/logs/diskLogger";
|
|
25
25
|
|
|
26
|
-
let HEARTBEAT_INTERVAL = timeInMinute *
|
|
26
|
+
let HEARTBEAT_INTERVAL = timeInMinute * 2;
|
|
27
27
|
// Interval which we check other heartbeats
|
|
28
28
|
let CHECK_INTERVAL = HEARTBEAT_INTERVAL;
|
|
29
29
|
// If the heartbeat is older than thing, it fails the dead check
|
|
@@ -37,6 +37,8 @@ let SUICIDE_HEARTBEAT_THRESHOLD = timeInMinute * 15;
|
|
|
37
37
|
|
|
38
38
|
let CLIENTSIDE_POLL_RATE = timeInMinute * 5;
|
|
39
39
|
|
|
40
|
+
let API_AUDIT_RATE = timeInSecond * 5;
|
|
41
|
+
|
|
40
42
|
let MEMORY_AUDIT_RATE = timeInMinute * 5;
|
|
41
43
|
let MEMORY_AUDIT_COUNT = 3;
|
|
42
44
|
|
|
@@ -64,6 +66,9 @@ export function getOwnNodeId(): string {
|
|
|
64
66
|
}
|
|
65
67
|
return nodeId;
|
|
66
68
|
}
|
|
69
|
+
export function getMountNodeId(): string | undefined | "" {
|
|
70
|
+
return SocketFunction.mountedNodeId;
|
|
71
|
+
}
|
|
67
72
|
export function getOwnNodeIdAssert(): string {
|
|
68
73
|
let nodeId = SocketFunction.mountedNodeId;
|
|
69
74
|
if (!nodeId) {
|
|
@@ -154,19 +159,8 @@ function addNodeIdBase(nodeId: string) {
|
|
|
154
159
|
allNodeIds2.add(nodeId);
|
|
155
160
|
onNodesChanged();
|
|
156
161
|
}
|
|
157
|
-
function removeNodeIds(nodeIds: string[]) {
|
|
158
|
-
diskLog("removeNodeIds", { nodeIds });
|
|
159
|
-
nodeIds = nodeIds.filter(nodeId => allNodeIds2.has(nodeId));
|
|
160
|
-
if (nodeIds.length === 0) return;
|
|
161
|
-
for (let nodeId of nodeIds) {
|
|
162
|
-
if (logging) {
|
|
163
|
-
console.log(red(`Removed node directly ${nodeId}`));
|
|
164
|
-
}
|
|
165
|
-
allNodeIds2.delete(nodeId);
|
|
166
|
-
}
|
|
167
|
-
onNodesChanged();
|
|
168
|
-
}
|
|
169
162
|
function setNodeIds(nodeIds: string[]) {
|
|
163
|
+
nodeIds = nodeIds.filter(x => x !== SPECIAL_NODE_ID_FOR_UNMOUNTED_NODE);
|
|
170
164
|
diskLog("setNodeIds", { nodeIds });
|
|
171
165
|
// Also try all localhost ports, if we are in dev mode
|
|
172
166
|
if (isNode() && isDevDebugbreak()) {
|
|
@@ -250,25 +244,18 @@ async function syncArchives() {
|
|
|
250
244
|
async function runHeartbeatAuditLoop() {
|
|
251
245
|
await onNodeDiscoveryReady();
|
|
252
246
|
let deadCount = new Map<string, number>();
|
|
253
|
-
|
|
247
|
+
// 90% of the normal interval, so we don't run at the same tmie as the other audit
|
|
248
|
+
await runInfinitePollCallAtStart(CHECK_INTERVAL * 0.9, async () => {
|
|
254
249
|
if (shutdown) return;
|
|
255
250
|
// Wait a bit longer, to try to prevent all nodes from synchronizing their audit times.
|
|
256
|
-
await delay(
|
|
251
|
+
await delay(CHECK_INTERVAL * Math.random() * 0.1);
|
|
257
252
|
//console.log(magenta(`Auditing node list`));
|
|
258
253
|
|
|
259
254
|
let deadTime = Date.now() - DEAD_THRESHOLD;
|
|
260
255
|
let nodeIds = await archives().find("");
|
|
261
|
-
//
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
console.log(yellow(`Found unexpected node, adding it and broadcasting it, ${nodeId}`));
|
|
265
|
-
addNodeId(nodeId);
|
|
266
|
-
for (let nodeId of allNodeIds2) {
|
|
267
|
-
if (isOwnNodeId(nodeId)) continue;
|
|
268
|
-
ignoreErrors(NodeDiscoveryController.nodes[nodeId].addNode(nodeId));
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
256
|
+
// We spent the money checking the node list, so we might as well update it
|
|
257
|
+
setNodeIds(nodeIds);
|
|
258
|
+
|
|
272
259
|
let removedNodeIds: string[] = [];
|
|
273
260
|
for (let nodeId of nodeIds) {
|
|
274
261
|
let lastTime = Number((await archives().get(nodeId))?.toString()) || 0;
|
|
@@ -291,62 +278,45 @@ async function runHeartbeatAuditLoop() {
|
|
|
291
278
|
}
|
|
292
279
|
}
|
|
293
280
|
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
// Broadcast to all nodes the removal
|
|
297
|
-
for (let nodeId of allNodeIds2) {
|
|
298
|
-
if (isOwnNodeId(nodeId)) continue;
|
|
299
|
-
ignoreErrors(NodeDiscoveryController.nodes[nodeId].removeNodes(removedNodeIds));
|
|
281
|
+
if (removedNodeIds.length > 0) {
|
|
282
|
+
void tellEveryoneNodesChanges();
|
|
300
283
|
}
|
|
301
284
|
});
|
|
302
285
|
}
|
|
303
286
|
|
|
304
|
-
async function
|
|
305
|
-
let
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
let hash = await errorToUndefinedSilent(NodeDiscoveryController.nodes[nodeId].getAllNodesHash());
|
|
312
|
-
if (!hash) break;
|
|
313
|
-
let ownHash = getAllNodesHash();
|
|
314
|
-
if (hash === ownHash) break;
|
|
315
|
-
await delay(stableWait);
|
|
316
|
-
let hash2 = await errorToUndefinedSilent(NodeDiscoveryController.nodes[nodeId].getAllNodesHash());
|
|
317
|
-
if (!hash2) break;
|
|
318
|
-
if (hash === hash2) {
|
|
319
|
-
console.log(yellow(`Node discovery is out of sync, syncing with disk. Told by node ${nodeId}, our hash of ${ownHash} is wrong and should be ${hash2}`));
|
|
320
|
-
return true;
|
|
321
|
-
}
|
|
322
|
-
// Otherwise, the hash changed, so... we maybe we just caught it while it was changing.
|
|
323
|
-
// Wait less time, to try to catch up with the rate of change
|
|
324
|
-
stableWait /= 2;
|
|
325
|
-
if (stableWait < timeInSecond) {
|
|
326
|
-
// It is changing so fast we better just sync with the disk
|
|
327
|
-
console.log(yellow(`Node discovery is changing fast and likely out of sync, resyncing with disk. Told by node ${nodeId}`));
|
|
328
|
-
return true;
|
|
329
|
-
}
|
|
287
|
+
async function fastMemorySync(retries = 3) {
|
|
288
|
+
let checkNode = shuffle(Array.from(allNodeIds2).filter(x => x !== getOurNodeId()), Date.now()).slice(0, MEMORY_AUDIT_COUNT).at(0);
|
|
289
|
+
if (!checkNode) return;
|
|
290
|
+
let otherNodes = await errorToUndefinedSilent(NodeDiscoveryController.nodes[checkNode].getAllNodeIds());
|
|
291
|
+
if (!otherNodes) {
|
|
292
|
+
if (retries > 0) {
|
|
293
|
+
await fastMemorySync(retries - 1);
|
|
330
294
|
}
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
// If they are missing nodes that's fine. We constantly have extra nodes, and have to function correctly
|
|
298
|
+
// with extra nodes. However, if we are missing nodes, we'd prefer to have them quickly, so we should
|
|
299
|
+
// sync now.
|
|
300
|
+
let missingNodes = otherNodes.filter(nodeId => !allNodeIds2.has(nodeId));
|
|
301
|
+
if (missingNodes.length > 0) {
|
|
302
|
+
console.log(yellow(`Node list is missing nodes, resyncing node`), { checkNode, missingNodes, otherNodes });
|
|
303
|
+
await syncArchives();
|
|
331
304
|
}
|
|
332
305
|
}
|
|
333
306
|
|
|
334
307
|
async function runMemoryAuditLoop() {
|
|
335
308
|
await onNodeDiscoveryReady();
|
|
336
|
-
runInfinitePoll(MEMORY_AUDIT_RATE,
|
|
337
|
-
|
|
338
|
-
// per month to do this, which... is a lot less than the server to run this will cost!
|
|
339
|
-
//if (await isOutOfSync()) {
|
|
340
|
-
await syncArchives();
|
|
341
|
-
//}
|
|
342
|
-
});
|
|
309
|
+
runInfinitePoll(MEMORY_AUDIT_RATE, syncArchives);
|
|
310
|
+
runInfinitePoll(API_AUDIT_RATE, fastMemorySync);
|
|
343
311
|
}
|
|
344
312
|
|
|
345
313
|
async function writeHeartbeat() {
|
|
346
314
|
if (shutdown) return;
|
|
347
315
|
let now = Date.now();
|
|
348
316
|
console.log(green(`Writing heartbeat ${formatDateTime(now)}`));
|
|
349
|
-
|
|
317
|
+
let nodeId = getMountNodeId();
|
|
318
|
+
if (!nodeId) return;
|
|
319
|
+
await archives().set(nodeId, Buffer.from(now + ""));
|
|
350
320
|
}
|
|
351
321
|
|
|
352
322
|
async function runMainSyncLoops(discoveryReady: PromiseObj<void>) {
|
|
@@ -491,23 +461,22 @@ export async function nodeDiscoveryShutdown() {
|
|
|
491
461
|
if (isServer()) {
|
|
492
462
|
await archives().del(getOwnNodeId());
|
|
493
463
|
}
|
|
494
|
-
|
|
495
|
-
// (but do bother to tell other nodes!)
|
|
496
|
-
await Promise.allSettled(Array.from(allNodeIds2).map(async nodeId => {
|
|
497
|
-
if (isOwnNodeId(nodeId)) return;
|
|
498
|
-
await timeoutToUndefinedSilent(timeInSecond * 5, errorToUndefinedSilent(NodeDiscoveryController.nodes[nodeId].removeNodes([getOwnNodeId()])));
|
|
499
|
-
}));
|
|
464
|
+
void tellEveryoneNodesChanges();
|
|
500
465
|
}
|
|
466
|
+
const tellEveryoneNodesChanges = throttleFunction(1000, function tellEveryoneNodesChanges() {
|
|
467
|
+
for (let nodeId of allNodeIds2) {
|
|
468
|
+
if (isOwnNodeId(nodeId)) continue;
|
|
469
|
+
ignoreErrors(NodeDiscoveryController.nodes[nodeId].resyncNodes());
|
|
470
|
+
}
|
|
471
|
+
});
|
|
501
472
|
|
|
502
473
|
|
|
503
474
|
class NodeDiscoveryControllerBase {
|
|
504
475
|
public async addNode(nodeId: string) {
|
|
505
476
|
addNodeId(nodeId);
|
|
506
477
|
}
|
|
507
|
-
public async
|
|
508
|
-
|
|
509
|
-
console.log(`remote removeNode request`, { callerId, nodeIds });
|
|
510
|
-
removeNodeIds(nodeIds);
|
|
478
|
+
public async resyncNodes() {
|
|
479
|
+
await syncArchives();
|
|
511
480
|
}
|
|
512
481
|
public async getAllNodesHash(): Promise<string> {
|
|
513
482
|
return getAllNodesHash();
|
|
@@ -529,7 +498,7 @@ const NodeDiscoveryController = SocketFunction.register(
|
|
|
529
498
|
new NodeDiscoveryControllerBase(),
|
|
530
499
|
() => ({
|
|
531
500
|
addNode: { hooks: [requiresNetworkTrustHook] },
|
|
532
|
-
|
|
501
|
+
resyncNodes: { hooks: [requiresNetworkTrustHook] },
|
|
533
502
|
getAllNodesHash: { hooks: [requiresNetworkTrustHook] },
|
|
534
503
|
// Skip client hooks, so we don't block on authentication (IdentityController), as some of these functions
|
|
535
504
|
// are needed for authentication to finish!
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { SocketFunction } from "socket-function/SocketFunction";
|
|
6
6
|
import { SocketRegistered } from "socket-function/SocketFunctionTypes";
|
|
7
|
-
import { errorToUndefined, errorToUndefinedSilent,
|
|
7
|
+
import { errorToUndefined, errorToUndefinedSilent, timeoutToUndefinedSilent } from "../errors";
|
|
8
8
|
import { getAllNodeIds } from "../-f-node-discovery/NodeDiscovery";
|
|
9
9
|
import { green, red, yellow } from "socket-function/src/formatting/logColors";
|
|
10
10
|
import { shuffle } from "../misc/random";
|
|
@@ -55,7 +55,6 @@ class PathValueCommitter {
|
|
|
55
55
|
// Returns true if all the writes are accepted by some node (this should almost always be the case)
|
|
56
56
|
public commitValues(values: PathValue[], predictWrites: "predictWrites" | undefined): void {
|
|
57
57
|
if (values.length === 0) return;
|
|
58
|
-
|
|
59
58
|
ActionsHistory.OnWrite(values);
|
|
60
59
|
|
|
61
60
|
let now = Date.now();
|
|
@@ -318,7 +318,10 @@ export class ClientWatcher {
|
|
|
318
318
|
void Promise.resolve().finally(() => { throw e; });
|
|
319
319
|
}
|
|
320
320
|
this.activeWatchSpec = undefined;
|
|
321
|
-
if (
|
|
321
|
+
if (
|
|
322
|
+
Date.now() - time > ClientWatcher.MAX_TRIGGER_TIME
|
|
323
|
+
&& !isDevDebugbreak()
|
|
324
|
+
) {
|
|
322
325
|
stoppedEarly = true;
|
|
323
326
|
break;
|
|
324
327
|
}
|
|
@@ -12,7 +12,7 @@ import { getThreadKeyCert } from "../-a-auth/certs";
|
|
|
12
12
|
import { ActionsHistory } from "../diagnostics/ActionsHistory";
|
|
13
13
|
import { registerResource } from "../diagnostics/trackResources";
|
|
14
14
|
import { ClientWatcher, WatchSpecData, clientWatcher } from "../1-path-client/pathValueClientWatcher";
|
|
15
|
-
import { createPathValueProxy, getProxyPath, isValueProxy, isValueProxy2 } from "./pathValueProxy";
|
|
15
|
+
import { createPathValueProxy, getPathFromProxy, getProxyPath, isValueProxy, isValueProxy2 } from "./pathValueProxy";
|
|
16
16
|
import { authorityStorage, compareTime, ReadLock, epochTime, getNextTime, MAX_ACCEPTED_CHANGE_AGE, PathValue, Time, getCreatorId } from "../0-path-value-core/pathValueCore";
|
|
17
17
|
import { runCodeWithDatabase, rawSchema } from "./pathDatabaseProxyBase";
|
|
18
18
|
import { LOCAL_DOMAIN } from "../0-path-value-core/PathController";
|
|
@@ -23,7 +23,7 @@ import { LOCAL_DOMAIN_PATH } from "../0-path-value-core/NodePathAuthorities";
|
|
|
23
23
|
import { registerPeriodic } from "../diagnostics/periodic";
|
|
24
24
|
import { remoteWatcher } from "../1-path-client/RemoteWatcher";
|
|
25
25
|
import { Schema2, Schema2Fncs } from "./schema2";
|
|
26
|
-
import { getDomain } from "../config";
|
|
26
|
+
import { devDebugbreak, getDomain } from "../config";
|
|
27
27
|
|
|
28
28
|
import type { CallSpec } from "../3-path-functions/PathFunctionRunner";
|
|
29
29
|
import type { FunctionMetadata } from "../3-path-functions/syncSchema";
|
|
@@ -364,6 +364,25 @@ export type PermissionsChecker = {
|
|
|
364
364
|
|
|
365
365
|
class SyncWatcherTag { }
|
|
366
366
|
|
|
367
|
+
function isRecursiveMatch(paths: Set<string>, path: string): boolean {
|
|
368
|
+
if (paths.size === 0) return false;
|
|
369
|
+
if (paths.has(path)) return true;
|
|
370
|
+
for (let otherPath of paths) {
|
|
371
|
+
if (path.includes(otherPath) || otherPath.includes(path)) {
|
|
372
|
+
return true;
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
return false;
|
|
376
|
+
}
|
|
377
|
+
function removeMatches(paths: Set<string>, path: string): void {
|
|
378
|
+
paths.delete(path);
|
|
379
|
+
for (let otherPath of paths) {
|
|
380
|
+
if (path.includes(otherPath) || otherPath.includes(path)) {
|
|
381
|
+
paths.delete(otherPath);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
|
|
367
386
|
export class PathValueProxyWatcher {
|
|
368
387
|
public static BREAK_ON_READS = new Set<string>();
|
|
369
388
|
public static BREAK_ON_WRITES = new Set<string>();
|
|
@@ -474,7 +493,8 @@ export class PathValueProxyWatcher {
|
|
|
474
493
|
};
|
|
475
494
|
public getCallback = (pathStr: string, syncParentKeys?: "parentKeys", readTransparent?: "readTransparent"): { value: unknown } | undefined => {
|
|
476
495
|
if (PathValueProxyWatcher.BREAK_ON_READS.size > 0 && proxyWatcher.isAllSynced()) {
|
|
477
|
-
if (PathValueProxyWatcher.BREAK_ON_READS
|
|
496
|
+
if (isRecursiveMatch(PathValueProxyWatcher.BREAK_ON_READS, pathStr)) {
|
|
497
|
+
const unwatch = () => PathValueProxyWatcher.BREAK_ON_READS.delete(pathStr); unwatch;
|
|
478
498
|
debugger;
|
|
479
499
|
}
|
|
480
500
|
}
|
|
@@ -548,13 +568,15 @@ export class PathValueProxyWatcher {
|
|
|
548
568
|
|
|
549
569
|
private setCallback = (pathStr: string, value: unknown, inRecursion = false, allowSpecial = false): void => {
|
|
550
570
|
if (PathValueProxyWatcher.BREAK_ON_WRITES.size > 0 && proxyWatcher.isAllSynced()) {
|
|
551
|
-
if (PathValueProxyWatcher.BREAK_ON_WRITES
|
|
571
|
+
if (isRecursiveMatch(PathValueProxyWatcher.BREAK_ON_WRITES, pathStr)) {
|
|
572
|
+
let unwatch = () => PathValueProxyWatcher.BREAK_ON_WRITES.delete(pathStr); unwatch;
|
|
552
573
|
debugger;
|
|
553
574
|
}
|
|
554
575
|
}
|
|
555
576
|
|
|
556
577
|
if (PathValueProxyWatcher.SET_FUNCTION_WATCH_ON_WRITES.size > 0 && proxyWatcher.isAllSynced()) {
|
|
557
|
-
if (PathValueProxyWatcher.SET_FUNCTION_WATCH_ON_WRITES
|
|
578
|
+
if (isRecursiveMatch(PathValueProxyWatcher.SET_FUNCTION_WATCH_ON_WRITES, pathStr)) {
|
|
579
|
+
let unwatch = () => PathValueProxyWatcher.SET_FUNCTION_WATCH_ON_WRITES.delete(pathStr); unwatch;
|
|
558
580
|
PathValueProxyWatcher.BREAK_ON_CALL.add(
|
|
559
581
|
getPathStr2(getCurrentCall().ModuleId, getCurrentCall().FunctionId)
|
|
560
582
|
);
|
|
@@ -562,10 +584,9 @@ export class PathValueProxyWatcher {
|
|
|
562
584
|
}
|
|
563
585
|
|
|
564
586
|
if (PathValueProxyWatcher.LOG_WRITES_INCLUDES.size > 0 && proxyWatcher.isAllSynced()) {
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
}
|
|
587
|
+
if (isRecursiveMatch(PathValueProxyWatcher.LOG_WRITES_INCLUDES, pathStr)) {
|
|
588
|
+
let unwatch = () => PathValueProxyWatcher.LOG_WRITES_INCLUDES.delete(pathStr); unwatch;
|
|
589
|
+
console.log(`Write path "${pathStr}" = ${value}`);
|
|
569
590
|
}
|
|
570
591
|
}
|
|
571
592
|
if (value === specialObjectWriteSymbol) {
|
|
@@ -597,15 +618,6 @@ export class PathValueProxyWatcher {
|
|
|
597
618
|
throw new Error(`Tried to write a non-local path in a "noLocks" watcher, ${watcher.debugName}, path ${pathStr}`);
|
|
598
619
|
}
|
|
599
620
|
|
|
600
|
-
if (!pathStr.startsWith(LOCAL_DOMAIN_PATH)) {
|
|
601
|
-
// Copy the value, to ensure any proxy values aren't attempted to be written
|
|
602
|
-
// to the database. A bit slower, but... it should be fine. We COULD do this
|
|
603
|
-
// later on, but this makes it easier to attribute lag and errors to the original source.
|
|
604
|
-
// - If we had a proxy, we WOULD copy it eventually, but we would copy it later,
|
|
605
|
-
// which would cause an error, because the reader wouldn't be trackable.
|
|
606
|
-
value = deepCloneCborx(value);
|
|
607
|
-
}
|
|
608
|
-
|
|
609
621
|
if (watcher.permissionsChecker) {
|
|
610
622
|
if (!watcher.permissionsChecker.checkPermissions(pathStr).allowed) {
|
|
611
623
|
if (value !== specialObjectWriteValue) {
|
|
@@ -713,6 +725,14 @@ export class PathValueProxyWatcher {
|
|
|
713
725
|
};
|
|
714
726
|
addWrites(pathStr, value);
|
|
715
727
|
} else {
|
|
728
|
+
if (!pathStr.startsWith(LOCAL_DOMAIN_PATH)) {
|
|
729
|
+
// Copy the value, to ensure any proxy values aren't attempted to be written
|
|
730
|
+
// to the database. A bit slower, but... it should be fine. We COULD do this
|
|
731
|
+
// later on, but this makes it easier to attribute lag and errors to the original source.
|
|
732
|
+
// - If we had a proxy, we WOULD copy it eventually, but we would copy it later,
|
|
733
|
+
// which would cause an error, because the reader wouldn't be trackable.
|
|
734
|
+
value = deepCloneCborx(value);
|
|
735
|
+
}
|
|
716
736
|
watcher.pendingWrites.set(pathStr, value);
|
|
717
737
|
}
|
|
718
738
|
};
|
|
@@ -1705,6 +1725,23 @@ export class PathValueProxyWatcher {
|
|
|
1705
1725
|
watcher.pendingUnsyncedParentAccesses.delete(path);
|
|
1706
1726
|
}
|
|
1707
1727
|
}
|
|
1728
|
+
|
|
1729
|
+
public debug_breakOnWrite(proxy: unknown | (() => unknown)) {
|
|
1730
|
+
let path = typeof proxy === "function" ? getProxyPath(proxy as any) : getPathFromProxy(proxy);
|
|
1731
|
+
if (!path) {
|
|
1732
|
+
console.error(`Value is not a proxy, and so cannot get path. Either pass a proxy, or a getter for a value.`);
|
|
1733
|
+
return;
|
|
1734
|
+
}
|
|
1735
|
+
PathValueProxyWatcher.BREAK_ON_WRITES.add(path);
|
|
1736
|
+
}
|
|
1737
|
+
public debug_logOnWrite(proxy: unknown | (() => unknown)) {
|
|
1738
|
+
let path = typeof proxy === "function" ? getProxyPath(proxy as any) : getPathFromProxy(proxy);
|
|
1739
|
+
if (!path) {
|
|
1740
|
+
console.error(`Value is not a proxy, and so cannot get path. Either pass a proxy, or a getter for a value.`);
|
|
1741
|
+
return;
|
|
1742
|
+
}
|
|
1743
|
+
PathValueProxyWatcher.LOG_WRITES_INCLUDES.add(path);
|
|
1744
|
+
}
|
|
1708
1745
|
}
|
|
1709
1746
|
|
|
1710
1747
|
// gitHash => domainName => moduleId => schema
|
|
@@ -1730,13 +1767,17 @@ function getBaseMatchingSchema(pathStr: string): {
|
|
|
1730
1767
|
schema: Schema2;
|
|
1731
1768
|
nestedPath: string[];
|
|
1732
1769
|
} | undefined {
|
|
1733
|
-
let
|
|
1770
|
+
let schemaForHash = (
|
|
1771
|
+
schemas.get(getCurrentCallObj()?.fnc.gitRef || "ambient")
|
|
1772
|
+
// Default to the ambient schema for clientside calls.
|
|
1773
|
+
|| schemas.get("ambient")
|
|
1774
|
+
);
|
|
1734
1775
|
|
|
1735
1776
|
if (_noAtomicSchema) return undefined;
|
|
1736
1777
|
if (getPathIndex(pathStr, 3) !== "Data") return undefined;
|
|
1737
1778
|
let domain = getPathIndex(pathStr, 0)!;
|
|
1738
1779
|
let moduleId = getPathIndex(pathStr, 2)!;
|
|
1739
|
-
let schema =
|
|
1780
|
+
let schema = schemaForHash?.get(domain)?.get(moduleId);
|
|
1740
1781
|
if (!schema) return undefined;
|
|
1741
1782
|
let nestedPathStr = getPathSuffix(pathStr, 4);
|
|
1742
1783
|
let nestedPath = getPathFromStr(nestedPathStr);
|
|
@@ -174,7 +174,7 @@ export function writeFunctionCall(config: {
|
|
|
174
174
|
return callSpec;
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
export function parseArgs(call: CallSpec) {
|
|
177
|
+
export function parseArgs(call: CallSpec): unknown[] {
|
|
178
178
|
return decodeCborx(Buffer.from(call.argsEncoded, "base64"));
|
|
179
179
|
}
|
|
180
180
|
|
|
@@ -177,6 +177,10 @@ export class Querysub {
|
|
|
177
177
|
public static unwatchCurrentFnc = () => PathValueProxyWatcher.BREAK_ON_CALL.delete(
|
|
178
178
|
getPathStr2(getCurrentCall().ModuleId, getCurrentCall().FunctionId)
|
|
179
179
|
);
|
|
180
|
+
public static watchWrites = (value: unknown) => proxyWatcher.debug_breakOnWrite(value);
|
|
181
|
+
public static debugWrites = (value: unknown) => proxyWatcher.debug_breakOnWrite(value);
|
|
182
|
+
public static breakOnWrite = (value: unknown) => proxyWatcher.debug_breakOnWrite(value);
|
|
183
|
+
public static logWrites = (value: unknown) => proxyWatcher.debug_logOnWrite(value);
|
|
180
184
|
|
|
181
185
|
public static watchUnsyncedComponents = () => qreact.watchUnsyncedComponents();
|
|
182
186
|
public static watchAnyUnsyncedComponents = () => qreact.watchUnsyncedComponents().size > 0;
|
|
@@ -317,12 +321,6 @@ export class Querysub {
|
|
|
317
321
|
return isSynced(value);
|
|
318
322
|
}
|
|
319
323
|
|
|
320
|
-
public static watchWrites(get: () => unknown) {
|
|
321
|
-
let path = getProxyPath(get);
|
|
322
|
-
if (!path) throw new Error(`Failed to get path`);
|
|
323
|
-
PathValueProxyWatcher.LOG_WRITES_INCLUDES.add(path);
|
|
324
|
-
}
|
|
325
|
-
|
|
326
324
|
public static assertDomainAllowed(path: string) {
|
|
327
325
|
let domain = getPathIndexAssert(path, 0);
|
|
328
326
|
if (!Querysub.trustedDomains.has(domain)) {
|
|
@@ -875,4 +873,4 @@ setImmediate(() => {
|
|
|
875
873
|
registerGetCompressNetwork(() => Querysub.COMPRESS_NETWORK);
|
|
876
874
|
registerGetCompressDisk(() => Querysub.COMPRESS_DISK);
|
|
877
875
|
|
|
878
|
-
(
|
|
876
|
+
(globalThis as any).Querysub = Querysub;
|
|
@@ -486,7 +486,7 @@ export async function getCallWrites(config: {
|
|
|
486
486
|
// throw new Error(`Module not found ${call.DomainName}.${call.ModuleId}`);
|
|
487
487
|
// }
|
|
488
488
|
let moduleObject = domainObject.PathFunctionRunner[call.ModuleId];
|
|
489
|
-
if (!(call.FunctionId in moduleObject.Sources)) {
|
|
489
|
+
if (!(call.FunctionId in moduleObject.Sources) && Querysub.isAllSynced()) {
|
|
490
490
|
throw new Error(`Function not found ${call.DomainName}.${call.ModuleId}.${call.FunctionId}`);
|
|
491
491
|
}
|
|
492
492
|
let functionSpec = atomicObjectRead(moduleObject.Sources[call.FunctionId]);
|
package/src/config.ts
CHANGED
|
@@ -39,9 +39,14 @@ export function devDebugbreak() {
|
|
|
39
39
|
debugbreak(2);
|
|
40
40
|
debugger;
|
|
41
41
|
}
|
|
42
|
+
let debuggedEnabled = false;
|
|
42
43
|
export function isDevDebugbreak() {
|
|
43
|
-
return yargObj.debugbreak;
|
|
44
|
+
return yargObj.debugbreak || !isNode() && location.hostname.startsWith("noproxy.") || debuggedEnabled;
|
|
44
45
|
}
|
|
46
|
+
export function enableDebugging() {
|
|
47
|
+
debuggedEnabled = true;
|
|
48
|
+
}
|
|
49
|
+
(globalThis as any).enableDebugging = enableDebugging;
|
|
45
50
|
|
|
46
51
|
|
|
47
52
|
export function authorityRaw() {
|
|
@@ -32,8 +32,11 @@ import { setRecord } from "../-b-authorities/dnsAuthority";
|
|
|
32
32
|
import tls from "tls";
|
|
33
33
|
import net from "net";
|
|
34
34
|
import { ButtonSelector } from "../library-components/ButtonSelector";
|
|
35
|
-
import { assertIsManagementUser } from "./managementPages";
|
|
35
|
+
import { assertIsManagementUser, managementPageURL } from "./managementPages";
|
|
36
36
|
import { SocketRegistered } from "socket-function/SocketFunctionTypes";
|
|
37
|
+
import { ATag } from "../library-components/ATag";
|
|
38
|
+
import { filterURL, selectedNodeId } from "./logs/DiskLoggerPage";
|
|
39
|
+
import { getSyncedController } from "../library-components/SyncedController";
|
|
37
40
|
|
|
38
41
|
|
|
39
42
|
type NodeData = {
|
|
@@ -162,7 +165,7 @@ export class NodeViewer extends qreact.Component {
|
|
|
162
165
|
|
|
163
166
|
let tables: { [key: string]: NodeData[] } = {};
|
|
164
167
|
tables["PathValueServer"] = [];
|
|
165
|
-
tables["
|
|
168
|
+
tables["querysub-function"] = [];
|
|
166
169
|
tables["Querysub"] = [];
|
|
167
170
|
for (let datum of nodeDatas) {
|
|
168
171
|
if (datum.live_authorityPaths?.length) {
|
|
@@ -170,7 +173,7 @@ export class NodeViewer extends qreact.Component {
|
|
|
170
173
|
} else if (datum.live_exposedControllers?.includes("QuerysubController-6db5ef05-7563-4473-a440-2f64f03fe6ef")) {
|
|
171
174
|
tables["Querysub"].push(datum);
|
|
172
175
|
} else if (datum.live_entryPoint?.endsWith("function.js")) {
|
|
173
|
-
tables["
|
|
176
|
+
tables["querysub-function"].push(datum);
|
|
174
177
|
} else {
|
|
175
178
|
let entryName = datum.live_entryPoint?.replaceAll("\\", "/").split("/").pop() || "Unknown";
|
|
176
179
|
tables[entryName] = tables[entryName] || [];
|
|
@@ -179,7 +182,7 @@ export class NodeViewer extends qreact.Component {
|
|
|
179
182
|
}
|
|
180
183
|
|
|
181
184
|
let builtinGroups = {
|
|
182
|
-
"Default": ["buttons", "devToolsURL", "ip", "uptime", "Heap", "All Memory", "Blocking Lag", "port", "threadId", "machineId", "apiError", "live_entryPoint"],
|
|
185
|
+
"Default": ["buttons", "devToolsURL", "nodeId", "ip", "uptime", "Heap", "All Memory", "Blocking Lag", "port", "threadId", "machineId", "apiError", "live_entryPoint"],
|
|
183
186
|
};
|
|
184
187
|
// Column => group
|
|
185
188
|
let builtInGroupsLookup = new Map<string, string>();
|
|
@@ -256,6 +259,23 @@ export class NodeViewer extends qreact.Component {
|
|
|
256
259
|
);
|
|
257
260
|
},
|
|
258
261
|
},
|
|
262
|
+
nodeId: {
|
|
263
|
+
formatter: (obj) => {
|
|
264
|
+
let str = String(obj);
|
|
265
|
+
if (str.startsWith("http")) return formatValue(obj);
|
|
266
|
+
https://noproxy.querysub.com:1111/?page=test&showingmanagement&managementpage=DiskLoggerPage&nodeId=b4d19fba5f79d48c3.b68f46ffaffad0636.querysub.com%3A41863&filter=%22%5C%22__nodeId%5C%22%3A%5C%22b654f522bf3a67c49.b68f46ffaffad0636.querysub.com%3A33473%5C%22%22
|
|
267
|
+
|
|
268
|
+
return (
|
|
269
|
+
<ATag values={[
|
|
270
|
+
managementPageURL.getOverride("DiskLoggerPage"),
|
|
271
|
+
selectedNodeId.getOverride(str),
|
|
272
|
+
filterURL.getOverride(`"__nodeId":"${str}"`),
|
|
273
|
+
]}>
|
|
274
|
+
Logs
|
|
275
|
+
</ATag>
|
|
276
|
+
);
|
|
277
|
+
},
|
|
278
|
+
},
|
|
259
279
|
ip: {},
|
|
260
280
|
...x.table?.columns,
|
|
261
281
|
//capabilities: null,
|
|
@@ -500,4 +520,6 @@ export const NodeViewerController = SocketFunction.register(
|
|
|
500
520
|
// Auto expose, for getMiscInfo. This is protected via assertIsManagementUser anyways.
|
|
501
521
|
//noAutoExpose: true,
|
|
502
522
|
}
|
|
503
|
-
);
|
|
523
|
+
);
|
|
524
|
+
|
|
525
|
+
export let nodeViewerController = getSyncedController(NodeViewerController);
|
|
@@ -18,7 +18,7 @@ import { LogType } from "../errorLogs/ErrorLogCore";
|
|
|
18
18
|
import { canHaveChildren } from "socket-function/src/types";
|
|
19
19
|
import { measureBlock } from "socket-function/src/profiling/measure";
|
|
20
20
|
import { binarySearchBasic, list, sort, timeInDay, timeInHour } from "socket-function/src/misc";
|
|
21
|
-
import { NodeViewerController } from "../NodeViewer";
|
|
21
|
+
import { NodeViewerController, nodeViewerController } from "../NodeViewer";
|
|
22
22
|
import { red } from "socket-function/src/formatting/logColors";
|
|
23
23
|
import { errorMessage, formatValue, genericFormat, warnMessage } from "../../5-diagnostics/GenericFormat";
|
|
24
24
|
import { hslToRGB } from "socket-function/src/formatting/colors";
|
|
@@ -51,9 +51,9 @@ import { TimeRangeSelector } from "../../library-components/TimeRangeSelector";
|
|
|
51
51
|
module.hotreload = true;
|
|
52
52
|
module.hotreload = true;
|
|
53
53
|
|
|
54
|
-
let selectedNodeId = new URLParam("nodeId", "");
|
|
54
|
+
export let selectedNodeId = new URLParam("nodeId", "");
|
|
55
55
|
|
|
56
|
-
let filterURL = new URLParam("filter", "");
|
|
56
|
+
export let filterURL = new URLParam("filter", "");
|
|
57
57
|
let sortOldestFirst = new URLParam("oldestFirst", false);
|
|
58
58
|
let selectedFields = new URLParam("selectedFields", "");
|
|
59
59
|
|
|
@@ -426,12 +426,7 @@ const processLogs = cacheShallowConfigArgEqual((config: {
|
|
|
426
426
|
return true;
|
|
427
427
|
})).filter(x => x.length > 0);
|
|
428
428
|
function logMatches(filter: string, log: LogObj) {
|
|
429
|
-
|
|
430
|
-
if (key.toLowerCase().includes(filter)) return true;
|
|
431
|
-
if (canHaveChildren(value) && JSON.stringify(value).toLowerCase().includes(filter)) return true;
|
|
432
|
-
if (String(value).toLowerCase().includes(filter)) return true;
|
|
433
|
-
}
|
|
434
|
-
return false;
|
|
429
|
+
return JSON.stringify(log).toLowerCase().includes(filter);
|
|
435
430
|
}
|
|
436
431
|
logMatchesFilter = function logMatchesFilter(log: LogObj) {
|
|
437
432
|
return (
|
|
@@ -567,6 +562,4 @@ export const DiskLoggerController = SocketFunction.register(
|
|
|
567
562
|
}
|
|
568
563
|
);
|
|
569
564
|
|
|
570
|
-
let diskLoggerController = getSyncedController(DiskLoggerController);
|
|
571
|
-
|
|
572
|
-
let nodeViewerController = getSyncedController(NodeViewerController);
|
|
565
|
+
let diskLoggerController = getSyncedController(DiskLoggerController);
|