querysub 0.440.0 → 0.442.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/.claude/settings.local.json +8 -0
- package/bin/mcp-indexed-logs.js +6 -0
- package/package.json +7 -4
- package/spec.txt +1 -0
- package/src/-a-archives/archiveCache.ts +1 -1
- package/src/-b-authorities/dnsAuthority.ts +1 -1
- package/src/-e-certs/EdgeCertController.ts +2 -8
- package/src/-f-node-discovery/NodeDiscovery.ts +14 -7
- package/src/-g-core-values/NodeCapabilities.ts +4 -0
- package/src/0-path-value-core/AuthorityLookup.ts +9 -4
- package/src/0-path-value-core/LockWatcher2.ts +1 -0
- package/src/0-path-value-core/PathValueController.ts +1 -1
- package/src/0-path-value-core/PathWatcher.ts +17 -19
- package/src/0-path-value-core/pathValueArchives.ts +20 -2
- package/src/0-path-value-core/pathValueCore.ts +5 -3
- package/src/1-path-client/RemoteWatcher.ts +17 -22
- package/src/1-path-client/pathValueClientWatcher.ts +1 -1
- package/src/2-proxy/PathValueProxyWatcher.ts +6 -6
- package/src/2-proxy/TransactionDelayer.ts +4 -7
- package/src/4-querysub/Querysub.ts +28 -7
- package/src/4-querysub/QuerysubController.ts +9 -2
- package/src/archiveapps/archiveJoinEntry.ts +1 -1
- package/src/diagnostics/ValuePathWarning.tsx +68 -0
- package/src/diagnostics/logs/IndexedLogs/BufferIndex.ts +113 -1
- package/src/diagnostics/logs/IndexedLogs/BufferUnitIndex.ts +4 -4
- package/src/diagnostics/logs/IndexedLogs/IndexedLogs.ts +49 -12
- package/src/diagnostics/logs/IndexedLogs/MCPIndexedLogs.ts +389 -0
- package/src/diagnostics/logs/IndexedLogs/MCPIndexedLogsEntry.ts +190 -0
- package/src/diagnostics/logs/diskLogger.ts +3 -0
- package/src/diagnostics/logs/errorNotifications2/errorNotifications.ts +2 -1
- package/src/diagnostics/managementPages.tsx +5 -1
- package/src/library-components/SyncedController.ts +2 -1
|
@@ -32,7 +32,7 @@ import { inlineNestedCalls, syncSchema } from "../3-path-functions/syncSchema";
|
|
|
32
32
|
import type { identityStorageKey, IdentityStorageType } from "../-a-auth/certs";
|
|
33
33
|
|
|
34
34
|
import { ExternalRenderClass, qreact } from "../4-dom/qreact";
|
|
35
|
-
import { configRootDiscoveryLocation, getOwnNodeId } from "../-f-node-discovery/NodeDiscovery";
|
|
35
|
+
import { configRootDiscoveryLocation, getOwnNodeId, onNodeBroadcasted, onNodeDiscoveryReady } from "../-f-node-discovery/NodeDiscovery";
|
|
36
36
|
import { pathValueCommitter } from "../0-path-value-core/PathValueCommitter";
|
|
37
37
|
import debugbreak from "debugbreak";
|
|
38
38
|
import { registerDynamicResource } from "../diagnostics/trackResources";
|
|
@@ -183,6 +183,9 @@ export class Querysub {
|
|
|
183
183
|
public static AUDIT_PREDICTIONS = true;
|
|
184
184
|
public static SIMULATE_LAG = 0;
|
|
185
185
|
|
|
186
|
+
public static registerAliveChecker = <T>(config: AliveChecker<T>) => registerAliveChecker(config);
|
|
187
|
+
public static registerGarbageCollection = <T>(config: AliveChecker<T>) => registerAliveChecker(config);
|
|
188
|
+
|
|
186
189
|
/** Delay used when functions are specified as delayCommit */
|
|
187
190
|
public static DELAY_COMMIT_DELAY = 1000 * 3;
|
|
188
191
|
|
|
@@ -287,11 +290,16 @@ export class Querysub {
|
|
|
287
290
|
* isn't presently necessary in most serverside scripts.
|
|
288
291
|
*/
|
|
289
292
|
@measureFnc
|
|
290
|
-
public static async optionalStartupWait() {
|
|
293
|
+
public static async optionalStartupWait(addTime?: (name: string) => void) {
|
|
294
|
+
// Used by authorityLookup, pulled out here so timing is more clear.
|
|
295
|
+
await onNodeDiscoveryReady();
|
|
296
|
+
addTime?.("onNodeDiscoveryReady");
|
|
291
297
|
await authorityLookup.startSyncing();
|
|
298
|
+
addTime?.("authorityLookup.startSyncing");
|
|
292
299
|
await measureBlock(async () => {
|
|
293
300
|
await waitForFirstTimeSync();
|
|
294
301
|
}, "waitForFirstTimeSync");
|
|
302
|
+
addTime?.("waitForFirstTimeSync");
|
|
295
303
|
}
|
|
296
304
|
|
|
297
305
|
public static createWatcher(watcher: (obj: SyncWatcher) => void, options?: Partial<WatcherOptions<unknown>>): {
|
|
@@ -1046,18 +1054,30 @@ export class Querysub {
|
|
|
1046
1054
|
if (isClient()) {
|
|
1047
1055
|
throw new Error(`--client processes cannot host a service. Either stop passing --client and keep the process on the network and trusted, or stop calling hostServer and call Querysub.configRootDiscoveryLocation instead.`);
|
|
1048
1056
|
}
|
|
1057
|
+
let times: {
|
|
1058
|
+
name: string;
|
|
1059
|
+
duration: number
|
|
1060
|
+
}[] = [];
|
|
1061
|
+
let time = Date.now();
|
|
1062
|
+
function addTime(name: string) {
|
|
1063
|
+
times.push({ name, duration: Date.now() - time });
|
|
1064
|
+
time = Date.now();
|
|
1065
|
+
}
|
|
1049
1066
|
|
|
1050
1067
|
this.socketFunctionInit();
|
|
1051
|
-
|
|
1068
|
+
let mountPromise = SocketFunction.mount({
|
|
1052
1069
|
public: isPublic(),
|
|
1053
1070
|
port,
|
|
1054
1071
|
...await getThreadKeyCert(),
|
|
1055
1072
|
});
|
|
1056
1073
|
|
|
1057
|
-
|
|
1074
|
+
let publishPromise = publishMachineARecords();
|
|
1075
|
+
await Promise.all([mountPromise, publishPromise]);
|
|
1076
|
+
addTime("mount & publish a records");
|
|
1077
|
+
|
|
1078
|
+
await Querysub.optionalStartupWait(addTime);
|
|
1058
1079
|
|
|
1059
|
-
|
|
1060
|
-
console.log(magenta(`Started hosting service ${name}`));
|
|
1080
|
+
console.log(magenta(`Started hosting service ${name}`) + ` | ${times.map(t => `${blue(t.name)}: ${green(formatTime(t.duration))}`).join(" | ")}`);
|
|
1061
1081
|
}
|
|
1062
1082
|
|
|
1063
1083
|
/** Syncs keys AND values (as we won't return a key for a value that is undefined). */
|
|
@@ -1330,7 +1350,7 @@ import "../diagnostics/trackResources";
|
|
|
1330
1350
|
import { formatNumber, formatTime } from "socket-function/src/formatting/format";
|
|
1331
1351
|
import { getCountPerPaint } from "../functional/onNextPaint";
|
|
1332
1352
|
import { addEpsilons } from "../bits";
|
|
1333
|
-
import { blue, magenta } from "socket-function/src/formatting/logColors";
|
|
1353
|
+
import { blue, green, magenta } from "socket-function/src/formatting/logColors";
|
|
1334
1354
|
import { MachineController } from "../deployManager/machineController";
|
|
1335
1355
|
import { getRecords, setRecord } from "../-b-authorities/dnsAuthority";
|
|
1336
1356
|
import { testTCPIsListening } from "socket-function/src/networking";
|
|
@@ -1339,4 +1359,5 @@ import { onAllPredictionsFinished } from "../-0-hooks/hooks";
|
|
|
1339
1359
|
import { LOCAL_DOMAIN } from "../0-path-value-core/PathRouter";
|
|
1340
1360
|
import { authorityLookup } from "../0-path-value-core/AuthorityLookup";
|
|
1341
1361
|
import { encodeParentFilter } from "../0-path-value-core/hackedPackedPathParentFiltering";
|
|
1362
|
+
import { AliveChecker, registerAliveChecker } from "../2-proxy/garbageCollection";
|
|
1342
1363
|
|
|
@@ -40,6 +40,7 @@ import { getDomain, isBootstrapOnly } from "../config";
|
|
|
40
40
|
import { flushPredictionQueueBase, runInPredictionQueue, syncHasPendingPredictionsBase } from "./predictionQueue";
|
|
41
41
|
import { PathRouter } from "../0-path-value-core/PathRouter";
|
|
42
42
|
import { authorityLookup } from "../0-path-value-core/AuthorityLookup";
|
|
43
|
+
import { PathValueArchives } from "../0-path-value-core/pathValueArchives";
|
|
43
44
|
|
|
44
45
|
let yargObj = isNodeTrue() && yargs(process.argv)
|
|
45
46
|
.option("fncfilter", { type: "string", default: "", desc: `Sets the filterable state for function calls, causing them to target specific FunctionRunners. If no FunctionRunner matches, all functions will fail to run. For example: "devtestserver" will match a FunctionRunner that uses the "devtestserver" filter. Merges with the existing filterable state if a client sets it explicitly.` })
|
|
@@ -490,7 +491,7 @@ export class QuerysubControllerBase {
|
|
|
490
491
|
|
|
491
492
|
if (removedPaths.size > 0) {
|
|
492
493
|
let [removedParentPaths, removedJustPaths] = splitParentPaths(removedPaths);
|
|
493
|
-
pathWatcher.unwatchPath({ callback: callerId, paths: removedJustPaths, parentPaths: removedParentPaths });
|
|
494
|
+
pathWatcher.unwatchPath({ callback: callerId, paths: removedJustPaths, parentPaths: removedParentPaths, reason: "QuerysubController.watch removed paths (permissions)" });
|
|
494
495
|
}
|
|
495
496
|
if (allowedPaths.size > 0) {
|
|
496
497
|
let [newParentPathsAllowed, newPathsAllowed] = splitParentPaths(allowedPaths);
|
|
@@ -531,7 +532,7 @@ export class QuerysubControllerBase {
|
|
|
531
532
|
delPermissionsPath(appendToPathStr(path, ""), true);
|
|
532
533
|
}
|
|
533
534
|
}
|
|
534
|
-
pathWatcher.unwatchPath({ callback: callerId, ...config });
|
|
535
|
+
pathWatcher.unwatchPath({ callback: callerId, ...config, reason: "QuerysubController.unwatch" });
|
|
535
536
|
}
|
|
536
537
|
|
|
537
538
|
// NOTE: Calls are going to be temporary and random. Any user can use any call ID, so technically you could clobber other users' call IDs, or your our. There wouldn't really be any benefit. Nothing would really happen if you do that, so I don't believe these need to be kept secret. I think if you know someone else's call ID you might be able to read that data, but also it's securely random, so you're not going to be able to guess the call ID.
|
|
@@ -686,6 +687,10 @@ export class QuerysubControllerBase {
|
|
|
686
687
|
}
|
|
687
688
|
return result;
|
|
688
689
|
}
|
|
690
|
+
|
|
691
|
+
public async debugGetValuePathCount(): Promise<number> {
|
|
692
|
+
return PathValueArchives.getValuePathCount();
|
|
693
|
+
}
|
|
689
694
|
}
|
|
690
695
|
|
|
691
696
|
export const QuerysubController = SocketFunction.register(
|
|
@@ -696,11 +701,13 @@ export const QuerysubController = SocketFunction.register(
|
|
|
696
701
|
watch: { compress: true, },
|
|
697
702
|
unwatch: { compress: true, },
|
|
698
703
|
addCall: { compress: true, },
|
|
704
|
+
// NOTE: Most of these debug functions are pretty innocuous. A lot of it is actually already exposed, and other parts of it is fine. It's fine for the user to know what node a path is on. It's fine for them to know how many value paths there are.
|
|
699
705
|
debugGetPathNodeIds: {},
|
|
700
706
|
debugGetNodeSpecs: {},
|
|
701
707
|
debugGetSingleReadNode: {},
|
|
702
708
|
getModulePath: {},
|
|
703
709
|
getDevFunctionSpecFromCall: {},
|
|
710
|
+
debugGetValuePathCount: {},
|
|
704
711
|
}),
|
|
705
712
|
() => ({
|
|
706
713
|
|
|
@@ -110,7 +110,7 @@ async function runGenesisJoinIteration(config?: { force?: boolean }) {
|
|
|
110
110
|
});
|
|
111
111
|
}
|
|
112
112
|
|
|
113
|
-
console.log(magenta(`Joining ${formatNumber(usedFiles.length)} files with ${formatNumber(allCombinedValues.length)} values in ${formatNumber(totalSize)} bytes. Original count: ${formatNumber(originalCount)}, under path count: ${formatNumber(underPathCount)}, within time range count: ${formatNumber(withinTimeRangeCount)}`));
|
|
113
|
+
console.log(magenta(`Joining ${formatNumber(usedFiles.length)} => ${transaction.createFiles.length} files with ${formatNumber(allCombinedValues.length)} values in ${formatNumber(totalSize)} bytes. Original count: ${formatNumber(originalCount)}, under path count: ${formatNumber(underPathCount)}, within time range count: ${formatNumber(withinTimeRangeCount)}`));
|
|
114
114
|
|
|
115
115
|
return [transaction];
|
|
116
116
|
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { qreact } from "../4-dom/qreact";
|
|
2
|
+
import { css } from "../4-dom/css";
|
|
3
|
+
import { Querysub, QuerysubController, querysubNodeId } from "../4-querysub/QuerysubController";
|
|
4
|
+
import { isCurrentUserSuperUser } from "../user-implementation/userData";
|
|
5
|
+
import { t } from "../2-proxy/schema2";
|
|
6
|
+
|
|
7
|
+
module.hotreload = true;
|
|
8
|
+
|
|
9
|
+
export class ValuePathWarning extends qreact.Component {
|
|
10
|
+
state = t.state({
|
|
11
|
+
count: t.number
|
|
12
|
+
});
|
|
13
|
+
componentDidMount() {
|
|
14
|
+
Querysub.onCommitFinished(async () => {
|
|
15
|
+
let nodeId = await querysubNodeId();
|
|
16
|
+
if (!nodeId) return;
|
|
17
|
+
let count = await QuerysubController.nodes[nodeId].debugGetValuePathCount();
|
|
18
|
+
Querysub.localCommit(() => this.state.count = count);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
renderBase() {
|
|
22
|
+
if (!isCurrentUserSuperUser()) return undefined;
|
|
23
|
+
let count = this.state.count;
|
|
24
|
+
if (!count) return undefined;
|
|
25
|
+
|
|
26
|
+
if (count <= 150) {
|
|
27
|
+
return <div className={css.hbox(4)}>
|
|
28
|
+
<span>📄</span>
|
|
29
|
+
<span>{count}</span>
|
|
30
|
+
</div>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let bgClass = "";
|
|
34
|
+
let flashing = false;
|
|
35
|
+
if (count > 500) {
|
|
36
|
+
bgClass = css.hsl(0, 80, 60);
|
|
37
|
+
flashing = true;
|
|
38
|
+
} else if (count > 300) {
|
|
39
|
+
bgClass = css.hsl(0, 80, 60);
|
|
40
|
+
} else {
|
|
41
|
+
bgClass = css.hsl(50, 100, 40);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let animClassName = "ValuePathWarning-flash";
|
|
45
|
+
return <div className={css.hbox(4).pad2(6, 2) + " " + bgClass + (flashing ? " " + animClassName : "")}>
|
|
46
|
+
<span>📄</span>
|
|
47
|
+
<span>{count}</span>
|
|
48
|
+
<span>warning: high number of files, join is likely not running</span>
|
|
49
|
+
{flashing && <style>{`
|
|
50
|
+
@keyframes ${animClassName}-anim {
|
|
51
|
+
0%, 100% { background-color: hsl(0, 80%, 60%); }
|
|
52
|
+
50% { background-color: hsl(0, 80%, 30%); }
|
|
53
|
+
}
|
|
54
|
+
.${animClassName} {
|
|
55
|
+
animation: ${animClassName}-anim 0.6s infinite;
|
|
56
|
+
}
|
|
57
|
+
`}</style>}
|
|
58
|
+
</div>;
|
|
59
|
+
}
|
|
60
|
+
render() {
|
|
61
|
+
try {
|
|
62
|
+
return this.renderBase();
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error("Error in rendering ValuePathWarning:", error);
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -9,7 +9,7 @@ import { cacheArgsEqual, cacheLimited, cacheWeak, lazy } from "socket-function/s
|
|
|
9
9
|
import { measureBlock, measureFnc, measureWrap } from "socket-function/src/profiling/measure";
|
|
10
10
|
import { formatNumber, formatTime } from "socket-function/src/formatting/format";
|
|
11
11
|
import { magenta, yellow } from "socket-function/src/formatting/logColors";
|
|
12
|
-
import { Unit, getAllUnits, Reader, createOffsetReader, SearchParams, IndexedLogResults } from "./BufferIndexHelpers";
|
|
12
|
+
import { Unit, getAllUnits, Reader, BufferReader, createOffsetReader, SearchParams, IndexedLogResults } from "./BufferIndexHelpers";
|
|
13
13
|
import { createMatchesPattern, getSearchUnits } from "./bufferSearchFindMatcher";
|
|
14
14
|
import { UnitSet } from "./BufferUnitSet";
|
|
15
15
|
import { BufferUnitIndex } from "./BufferUnitIndex";
|
|
@@ -409,6 +409,118 @@ export class BufferIndex {
|
|
|
409
409
|
results.blockSearchTime += Date.now() - blockSearchTimeStart;
|
|
410
410
|
}
|
|
411
411
|
|
|
412
|
+
// Returns the block indices in `index` that could contain a match for `query`.
|
|
413
|
+
// Mirrors the AND/OR scan inside findLocal and BufferUnitIndex.find but does
|
|
414
|
+
// not read or scan data — only the index. Used by MCPIndexedLogs to split
|
|
415
|
+
// search into "find candidate blocks" then "scan those blocks".
|
|
416
|
+
@measureFnc
|
|
417
|
+
public static async findMatchingBlocks(config: {
|
|
418
|
+
index: Buffer;
|
|
419
|
+
dataReader: Reader;
|
|
420
|
+
query: Buffer;
|
|
421
|
+
disableWildCards?: boolean;
|
|
422
|
+
results: IndexedLogResults;
|
|
423
|
+
}): Promise<number[]> {
|
|
424
|
+
let { index, dataReader, query, results } = config;
|
|
425
|
+
let allSearchUnits = getSearchUnits(query, !!config.disableWildCards);
|
|
426
|
+
if (allSearchUnits.length === 0) return [];
|
|
427
|
+
|
|
428
|
+
let type = index[0];
|
|
429
|
+
if (!type) {
|
|
430
|
+
type = (await dataReader.read(0, 1))?.[0];
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
if (type === STREAM_TYPE) {
|
|
434
|
+
if (index.length === 0) {
|
|
435
|
+
index = await BufferIndex.rebuildLocalIndexFromData(dataReader);
|
|
436
|
+
if (index.length === 0) return [];
|
|
437
|
+
}
|
|
438
|
+
index = await BufferIndex.fixPartialIndex({ index, dataReader, results });
|
|
439
|
+
|
|
440
|
+
let decoded = decodeTypeHeader(index);
|
|
441
|
+
if (!decoded) return [];
|
|
442
|
+
let indexEntries = await indexStreamerType.getAllBlocks(decoded.data);
|
|
443
|
+
|
|
444
|
+
let matching: number[] = [];
|
|
445
|
+
for (let i = 0; i < indexEntries.length; i++) {
|
|
446
|
+
let blockIndexData = indexEntries[i];
|
|
447
|
+
for (let or of allSearchUnits) {
|
|
448
|
+
let hasAllUnits = true;
|
|
449
|
+
for (let unit of or) {
|
|
450
|
+
if (!UnitSet.has(blockIndexData, unit)) {
|
|
451
|
+
hasAllUnits = false;
|
|
452
|
+
break;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
if (hasAllUnits) {
|
|
456
|
+
matching.push(i);
|
|
457
|
+
break;
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
return matching;
|
|
462
|
+
} else if (type === BULK_TYPE) {
|
|
463
|
+
let candidateSet = new Set<number>();
|
|
464
|
+
for (let or of allSearchUnits) {
|
|
465
|
+
let blocks = BufferUnitIndex.findBlocks({ units: or, index });
|
|
466
|
+
for (let b of blocks) candidateSet.add(b);
|
|
467
|
+
}
|
|
468
|
+
let result = Array.from(candidateSet);
|
|
469
|
+
sort(result, x => x);
|
|
470
|
+
return result;
|
|
471
|
+
}
|
|
472
|
+
return [];
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
// Loads and returns just the buffers in a single block, by index.
|
|
476
|
+
// For STREAM_TYPE, blockIndex matches the index entry order produced by
|
|
477
|
+
// findMatchingBlocks. For BULK_TYPE, blockIndex matches BufferUnitIndex's
|
|
478
|
+
// internal block ordering (also what findMatchingBlocks returns).
|
|
479
|
+
@measureFnc
|
|
480
|
+
public static async getBlockBuffers(config: {
|
|
481
|
+
index: Buffer;
|
|
482
|
+
dataReader: Reader;
|
|
483
|
+
blockIndex: number;
|
|
484
|
+
}): Promise<Buffer[]> {
|
|
485
|
+
let { index, dataReader, blockIndex } = config;
|
|
486
|
+
|
|
487
|
+
let type = index[0];
|
|
488
|
+
if (!type) {
|
|
489
|
+
type = (await dataReader.read(0, 1))?.[0];
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
if (type === STREAM_TYPE) {
|
|
493
|
+
let headerBuf = await dataReader.read(0, 5);
|
|
494
|
+
if (headerBuf.length < 5) return [];
|
|
495
|
+
let headerSize = headerBuf.readInt32LE(1);
|
|
496
|
+
let totalHeaderSize = 1 + 4 + headerSize;
|
|
497
|
+
let dataWithoutHeaderReader = createOffsetReader(dataReader, totalHeaderSize);
|
|
498
|
+
|
|
499
|
+
let blocks = await dataStreamerType.getBlockRange({
|
|
500
|
+
reader: dataWithoutHeaderReader,
|
|
501
|
+
startIndex: blockIndex,
|
|
502
|
+
endIndex: blockIndex + 1,
|
|
503
|
+
});
|
|
504
|
+
if (blocks.length === 0) return [];
|
|
505
|
+
try {
|
|
506
|
+
let decompressed = CompressedStream.decode(blocks[0]);
|
|
507
|
+
return await blockStreamerType.getAllBlocks(decompressed);
|
|
508
|
+
} catch (e) {
|
|
509
|
+
return [];
|
|
510
|
+
}
|
|
511
|
+
} else if (type === BULK_TYPE) {
|
|
512
|
+
let obj = await BufferUnitIndex.getBlock(dataReader, blockIndex);
|
|
513
|
+
let blockReader = new BufferReader(obj.block);
|
|
514
|
+
let bufferCount = await BufferUnitIndex.getBufferCountFromBlock(blockReader);
|
|
515
|
+
let result: Buffer[] = [];
|
|
516
|
+
for (let i = 0; i < bufferCount; i++) {
|
|
517
|
+
result.push(await BufferUnitIndex.getBufferFromBlock(blockReader, i));
|
|
518
|
+
}
|
|
519
|
+
return result;
|
|
520
|
+
}
|
|
521
|
+
return [];
|
|
522
|
+
}
|
|
523
|
+
|
|
412
524
|
@measureFnc
|
|
413
525
|
public static async find(config: {
|
|
414
526
|
index: Buffer;
|
|
@@ -611,7 +611,7 @@ export class BufferUnitIndex {
|
|
|
611
611
|
}
|
|
612
612
|
|
|
613
613
|
@measureFnc
|
|
614
|
-
|
|
614
|
+
public static findBlocks(config: {
|
|
615
615
|
units: number[];
|
|
616
616
|
index: Buffer;
|
|
617
617
|
}): number[] {
|
|
@@ -679,12 +679,12 @@ export class BufferUnitIndex {
|
|
|
679
679
|
}
|
|
680
680
|
|
|
681
681
|
|
|
682
|
-
|
|
682
|
+
public static async getBlockCount(reader: Reader): Promise<number> {
|
|
683
683
|
const headerBuffer = await reader.read(4, 8);
|
|
684
684
|
return headerBuffer.readUInt32LE(0);
|
|
685
685
|
}
|
|
686
686
|
|
|
687
|
-
|
|
687
|
+
public static async getBlock(reader: Reader, blockIndex: number, debugOffsets?: {
|
|
688
688
|
startOffset: number;
|
|
689
689
|
endOffset: number;
|
|
690
690
|
}): Promise<{
|
|
@@ -715,7 +715,7 @@ export class BufferUnitIndex {
|
|
|
715
715
|
return (await reader.read(0, 4)).readUInt32LE(0);
|
|
716
716
|
}
|
|
717
717
|
|
|
718
|
-
|
|
718
|
+
public static async getBufferFromBlock(reader: Reader, bufferIndex: number): Promise<Buffer> {
|
|
719
719
|
let startOffset = (await reader.read(4 + bufferIndex * 4, 4)).readUInt32LE(0);
|
|
720
720
|
let endOffset = (await reader.read(4 + (bufferIndex + 1) * 4, 4)).readUInt32LE(0);
|
|
721
721
|
let buffer = await reader.read(startOffset, endOffset - startOffset);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { lazy } from "socket-function/src/caching";
|
|
1
|
+
import { cache, lazy } from "socket-function/src/caching";
|
|
2
2
|
import { Archives, nestArchives } from "../../../-a-archives/archives";
|
|
3
3
|
import { deepCloneJSON, keyByArray, nextId, sort, timeInHour, timeInMinute, timeInSecond, timeoutToUndefinedSilent } from "socket-function/src/misc";
|
|
4
4
|
import { BufferIndex } from "./BufferIndex";
|
|
@@ -27,6 +27,7 @@ import { getAllNodeIds } from "../../../-f-node-discovery/NodeDiscovery";
|
|
|
27
27
|
import { NodeCapabilitiesController } from "../../../-g-core-values/NodeCapabilities";
|
|
28
28
|
import { getLoggers2Async } from "../diskLogger";
|
|
29
29
|
import { watchAllValues } from "../errorNotifications2/logWatcher";
|
|
30
|
+
import { wrapArchivesWithCache } from "../../../-a-archives/archiveCache";
|
|
30
31
|
|
|
31
32
|
// Ensure it's available so that the controller is listening on all servers
|
|
32
33
|
watchAllValues;
|
|
@@ -113,6 +114,23 @@ export class IndexedLogs<T> {
|
|
|
113
114
|
});
|
|
114
115
|
return archives;
|
|
115
116
|
};
|
|
117
|
+
public debugGetCachedLogs = cache((config: {
|
|
118
|
+
type: "local" | "public";
|
|
119
|
+
}) => {
|
|
120
|
+
let usePublic = config.type === "public";
|
|
121
|
+
let archives = usePublic ? getArchivesBackblaze(getDomain()) : getArchivesHome(getDomain());
|
|
122
|
+
archives = nestArchives("final-indexed-logs/" + this.config.name, archives);
|
|
123
|
+
archives = wrapArchivesWithCache(archives);
|
|
124
|
+
archives = createArchivesMemoryCache(archives, {
|
|
125
|
+
maxSize: 1024 * 1024 * 1024 * 12,
|
|
126
|
+
maxCount: 1000 * 500,
|
|
127
|
+
fullyImmutable: true,
|
|
128
|
+
});
|
|
129
|
+
return archives;
|
|
130
|
+
});
|
|
131
|
+
public debugIsPublic() {
|
|
132
|
+
return isPublic();
|
|
133
|
+
}
|
|
116
134
|
private getPublicLogs = lazy((): Archives => {
|
|
117
135
|
return this.getPublicLogsBase(isPublic());
|
|
118
136
|
});
|
|
@@ -756,14 +774,20 @@ export class IndexedLogs<T> {
|
|
|
756
774
|
}
|
|
757
775
|
}
|
|
758
776
|
|
|
777
|
+
|
|
778
|
+
function getLogByName(name: string): IndexedLogs<unknown> {
|
|
779
|
+
let indexedLogs = loggerByName.get(name);
|
|
780
|
+
if (!indexedLogs) throw new Error(`Indexed logs ${name} not found, have ${Array.from(loggerByName.keys()).join(" | ")}`);
|
|
781
|
+
return indexedLogs;
|
|
782
|
+
}
|
|
759
783
|
class IndexedLogClient {
|
|
784
|
+
|
|
760
785
|
public async onFind(config: {
|
|
761
786
|
findId: string;
|
|
762
787
|
indexedLogsName: string;
|
|
763
788
|
result: unknown;
|
|
764
789
|
}) {
|
|
765
|
-
let indexedLogs =
|
|
766
|
-
if (!indexedLogs) throw new Error(`Indexed logs ${config.indexedLogsName} not found`);
|
|
790
|
+
let indexedLogs = getLogByName(config.indexedLogsName);
|
|
767
791
|
indexedLogs.onFindResult({
|
|
768
792
|
findId: config.findId,
|
|
769
793
|
result: config.result,
|
|
@@ -774,8 +798,7 @@ class IndexedLogClient {
|
|
|
774
798
|
indexedLogsName: string;
|
|
775
799
|
results: IndexedLogResults;
|
|
776
800
|
}): Promise<boolean> {
|
|
777
|
-
let indexedLogs =
|
|
778
|
-
if (!indexedLogs) throw new Error(`Indexed logs ${config.indexedLogsName} not found`);
|
|
801
|
+
let indexedLogs = getLogByName(config.indexedLogsName);
|
|
779
802
|
return await indexedLogs.onResults({
|
|
780
803
|
findId: config.findId,
|
|
781
804
|
results: config.results,
|
|
@@ -789,8 +812,7 @@ class IndexedLogShim {
|
|
|
789
812
|
params: SearchParams;
|
|
790
813
|
}): Promise<IndexedLogResults> {
|
|
791
814
|
let caller = SocketFunction.getCaller();
|
|
792
|
-
let indexedLogs =
|
|
793
|
-
if (!indexedLogs) throw new Error(`Indexed logs ${config.indexedLogsName} not found`);
|
|
815
|
+
let indexedLogs = getLogByName(config.indexedLogsName);
|
|
794
816
|
|
|
795
817
|
return indexedLogs.find({
|
|
796
818
|
params: config.params,
|
|
@@ -822,8 +844,7 @@ class IndexedLogShim {
|
|
|
822
844
|
only?: "local" | "public";
|
|
823
845
|
forceReadProduction?: boolean;
|
|
824
846
|
}): Promise<TimeFilePathWithSize[]> {
|
|
825
|
-
let indexedLogs =
|
|
826
|
-
if (!indexedLogs) throw new Error(`Indexed logs ${config.indexedLogsName} not found`);
|
|
847
|
+
let indexedLogs = getLogByName(config.indexedLogsName);
|
|
827
848
|
return indexedLogs.getPaths({
|
|
828
849
|
startTime: config.startTime,
|
|
829
850
|
endTime: config.endTime,
|
|
@@ -835,17 +856,30 @@ class IndexedLogShim {
|
|
|
835
856
|
public async forceMoveLogsToPublic(config: {
|
|
836
857
|
indexedLogsName: string;
|
|
837
858
|
}) {
|
|
838
|
-
let indexedLogs =
|
|
839
|
-
if (!indexedLogs) throw new Error(`Indexed logs ${config.indexedLogsName} not found`);
|
|
859
|
+
let indexedLogs = getLogByName(config.indexedLogsName);
|
|
840
860
|
await indexedLogs.moveLogsToPublic(true);
|
|
841
861
|
}
|
|
842
862
|
|
|
843
863
|
public async hasLogger(name: string) {
|
|
844
864
|
return loggerByName.has(name);
|
|
845
865
|
}
|
|
866
|
+
|
|
867
|
+
public async hasPendingInRange(config: {
|
|
868
|
+
indexedLogsName: string;
|
|
869
|
+
startTime: number;
|
|
870
|
+
endTime: number;
|
|
871
|
+
}): Promise<boolean> {
|
|
872
|
+
let logs = getLogByName(config.indexedLogsName);
|
|
873
|
+
let paths = await logs.getPaths({
|
|
874
|
+
startTime: config.startTime,
|
|
875
|
+
endTime: config.endTime,
|
|
876
|
+
only: "local",
|
|
877
|
+
});
|
|
878
|
+
return paths.some(p => p.logCount === undefined);
|
|
879
|
+
}
|
|
846
880
|
}
|
|
847
881
|
|
|
848
|
-
const IndexedLogShimController = SocketFunction.register(
|
|
882
|
+
export const IndexedLogShimController = SocketFunction.register(
|
|
849
883
|
"IndexedLogShim-019c87b7-73ca-72ec-91b3-2d45ebb616cd",
|
|
850
884
|
new IndexedLogShim(),
|
|
851
885
|
() => ({
|
|
@@ -860,6 +894,9 @@ const IndexedLogShimController = SocketFunction.register(
|
|
|
860
894
|
},
|
|
861
895
|
hasLogger: {
|
|
862
896
|
hooks: [assertIsManagementUser]
|
|
897
|
+
},
|
|
898
|
+
hasPendingInRange: {
|
|
899
|
+
hooks: [assertIsManagementUser]
|
|
863
900
|
}
|
|
864
901
|
})
|
|
865
902
|
);
|