querysub 0.424.0 → 0.425.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/0-path-value-core/PathValueCommitter.ts +53 -32
- package/src/0-path-value-core/PathValueController.ts +2 -1
- package/src/0-path-value-core/ValidStateComputer.ts +32 -4
- package/src/0-path-value-core/pathValueCore.ts +1 -1
- package/src/4-querysub/Querysub.ts +0 -2
- package/src/diagnostics/logs/IndexedLogs/BufferIndexLogsOptimizationConstants.ts +2 -1
- package/src/diagnostics/logs/IndexedLogs/IndexedLogs.ts +1 -2
- package/src/diagnostics/logs/IndexedLogs/moveIndexLogsToPublic.ts +5 -5
- package/src/diagnostics/logs/ObjectDisplay.tsx +1 -1
package/package.json
CHANGED
|
@@ -15,7 +15,7 @@ import debugbreak from "debugbreak";
|
|
|
15
15
|
import { red } from "socket-function/src/formatting/logColors";
|
|
16
16
|
import { isClient } from "../config2";
|
|
17
17
|
import { auditLog, isDebugLogEnabled } from "./auditLogs";
|
|
18
|
-
import { authorityLookup } from "./AuthorityLookup";
|
|
18
|
+
import { AuthorityEntry, authorityLookup } from "./AuthorityLookup";
|
|
19
19
|
import { debugNodeId } from "../-c-identity/IdentityController";
|
|
20
20
|
import { decodeNodeId } from "../-a-auth/certs";
|
|
21
21
|
import { decodeParentFilter, encodeParentFilter } from "./hackedPackedPathParentFiltering";
|
|
@@ -28,6 +28,10 @@ setImmediate(() => import("../4-querysub/Querysub"));
|
|
|
28
28
|
|
|
29
29
|
const MAX_SEND_TRY_COUNT = 3;
|
|
30
30
|
|
|
31
|
+
// NOTE: This isn't very efficient, but it is safer. It's quadratic depending on the number of authorities for the server. Which shouldn't be so bad. I mean, why would we have four times redundancy? That's a lot. And even then, we're sending sixteen times the amount of traffic. That's fine, I guess. I think it's more reasonable for us to have two times redundancy, maybe even three times redundancy. With two times redundancy, it's only sending the value four times, which is reasonable. And the trade-off is there's basically no way that we can lose the data.
|
|
32
|
+
// - If we only send to one authority and then they rebroadcast it, it's possible that they die after receiving the value. If we send it to both value servers but they don't re-broadcast, it's possible that we die while broadcasting the values after having only sent a few. However, by sending it to both servers and having both servers send it to the other servers, it means that if we die while broadcasting it, it's fine. And if the server dies that we want to send it to, it's fine because we send it to both servers.
|
|
33
|
+
const BROADCAST_TO_ALL_AUTHORITIES = true;
|
|
34
|
+
|
|
31
35
|
export type BatchValues = {
|
|
32
36
|
pathValues: PathValue[],
|
|
33
37
|
parentsSynced?: string[];
|
|
@@ -207,8 +211,9 @@ class PathValueCommitter {
|
|
|
207
211
|
source: pathValue.source,
|
|
208
212
|
otherAuthorities,
|
|
209
213
|
});
|
|
214
|
+
continue;
|
|
210
215
|
}
|
|
211
|
-
|
|
216
|
+
function sendToAuthority(otherAuthority: AuthorityEntry) {
|
|
212
217
|
let values = valuesPerOtherAuthority.get(otherAuthority.nodeId);
|
|
213
218
|
if (!values) {
|
|
214
219
|
values = [];
|
|
@@ -216,6 +221,15 @@ class PathValueCommitter {
|
|
|
216
221
|
}
|
|
217
222
|
values.push(pathValue);
|
|
218
223
|
}
|
|
224
|
+
if (BROADCAST_TO_ALL_AUTHORITIES) {
|
|
225
|
+
for (let otherAuthority of otherAuthorities) {
|
|
226
|
+
sendToAuthority(otherAuthority);
|
|
227
|
+
}
|
|
228
|
+
} else {
|
|
229
|
+
// NOTE: Path routing is pretty strict. Once we disconnect, it'll remove it from the list of authorities. And if we can't talk to it, the retry logic will call us again.
|
|
230
|
+
let otherAuthority = otherAuthorities[~~(Math.random() * otherAuthorities.length)];
|
|
231
|
+
sendToAuthority(otherAuthority);
|
|
232
|
+
}
|
|
219
233
|
}
|
|
220
234
|
|
|
221
235
|
// Don't send to bad nodes for 60 seconds
|
|
@@ -268,30 +282,41 @@ class PathValueCommitter {
|
|
|
268
282
|
});
|
|
269
283
|
}
|
|
270
284
|
});
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
let
|
|
275
|
-
let
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
285
|
+
// If we broadcast to all authorities, we can't also retry, as this means if one authority is down, we end up infinitely looping. And it breaks the servers and the logs.
|
|
286
|
+
if (!BROADCAST_TO_ALL_AUTHORITIES) {
|
|
287
|
+
void forwardPromise.catch(async error => {
|
|
288
|
+
let byTryCount = new Map<number, PathValue[]>();
|
|
289
|
+
for (let value of values) {
|
|
290
|
+
let tryCount = tryCountPerValue.get(value) ?? 0;
|
|
291
|
+
let arr = byTryCount.get(tryCount) ?? [];
|
|
292
|
+
arr.push(value);
|
|
293
|
+
byTryCount.set(tryCount, arr);
|
|
294
|
+
}
|
|
295
|
+
for (let [tryCount, values] of byTryCount.entries()) {
|
|
296
|
+
tryCount++;
|
|
297
|
+
if (tryCount > MAX_SEND_TRY_COUNT) {
|
|
298
|
+
console.error(`Failed to send values after ${MAX_SEND_TRY_COUNT} tries. Giving up.`, {
|
|
299
|
+
error: error.message,
|
|
300
|
+
otherAuthority,
|
|
301
|
+
count: values.length,
|
|
302
|
+
});
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
for (let value of values) {
|
|
306
|
+
console.error(`Retrying to send values after ${tryCount} tries.`, {
|
|
307
|
+
error: error.message,
|
|
308
|
+
otherAuthority,
|
|
309
|
+
path: value.path,
|
|
310
|
+
timeId: value.time.time,
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
void pathValueCommitter.broadcastValues({
|
|
314
|
+
values: new Set(values),
|
|
315
|
+
tryCount: tryCount,
|
|
286
316
|
});
|
|
287
|
-
continue;
|
|
288
317
|
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
tryCount: tryCount,
|
|
292
|
-
});
|
|
293
|
-
}
|
|
294
|
-
});
|
|
318
|
+
});
|
|
319
|
+
}
|
|
295
320
|
|
|
296
321
|
pathValueCommitter.addCommitPromise(forwardPromise);
|
|
297
322
|
});
|
|
@@ -352,9 +377,10 @@ class PathValueCommitter {
|
|
|
352
377
|
return true;
|
|
353
378
|
});
|
|
354
379
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
380
|
+
// NOTE: If we are the authority on it, it means we're not going to have a remote watch node ID. And if we did, it would be ourselves. So this also implicitly filters out any initial triggers for values that we are the authority on.
|
|
381
|
+
for (let path of Array.from(batch.initialTriggers.values)) {
|
|
382
|
+
if (isWrongAuthority(path, undefined, "initialTrigger")) {
|
|
383
|
+
batch.initialTriggers.values.delete(path);
|
|
358
384
|
}
|
|
359
385
|
}
|
|
360
386
|
for (let parentPath of Array.from(batch.initialTriggers.parentPaths)) {
|
|
@@ -362,11 +388,6 @@ class PathValueCommitter {
|
|
|
362
388
|
continue;
|
|
363
389
|
}
|
|
364
390
|
|
|
365
|
-
// TODO: Remove this breakpoint eventually. This can happen naturally when servers go down?
|
|
366
|
-
require("debugbreak")(2);
|
|
367
|
-
debugger;
|
|
368
|
-
remoteWatcher.isFinalRemoteWatchPath({ parentPath, nodeId: batch.sourceNodeId });
|
|
369
|
-
|
|
370
391
|
console.warn(`Ignoring parent path which we aren't watching. From ${debugNodeId(batch.sourceNodeId)}.`, {
|
|
371
392
|
parentPath,
|
|
372
393
|
sourceNodeId: debugNodeId(batch.sourceNodeId),
|
|
@@ -310,8 +310,9 @@ export class PathValueControllerBase {
|
|
|
310
310
|
public async getValuesByPathAndTime(entries: AuditSnapshotEntry[]): Promise<Buffer[]> {
|
|
311
311
|
let values: PathValue[] = [];
|
|
312
312
|
for (let entry of entries) {
|
|
313
|
-
let value = authorityStorage.
|
|
313
|
+
let value = authorityStorage.getValueExact(entry.path, entry.time);
|
|
314
314
|
if (!value) continue;
|
|
315
|
+
if (!value.valid) continue;
|
|
315
316
|
if (value.isTransparent) continue;
|
|
316
317
|
if (value.canGCValue) continue;
|
|
317
318
|
values.push(value);
|
|
@@ -2,7 +2,7 @@ import { keyByArray, binarySearchIndex } from "socket-function/src/misc";
|
|
|
2
2
|
import { measureFnc } from "socket-function/src/profiling/measure";
|
|
3
3
|
import { isNode } from "typesafecss";
|
|
4
4
|
import { isDiskAudit } from "../config";
|
|
5
|
-
import { getPathFromStr } from "../path";
|
|
5
|
+
import { getParentPathStr, getPathFromStr } from "../path";
|
|
6
6
|
import { auditLog } from "./auditLogs";
|
|
7
7
|
import { PathRouter } from "./PathRouter";
|
|
8
8
|
import { PathValue, authorityStorage, compareTime, debugPathValuePath, ReadLock, byLockGroup, isCoreQuiet, debugRejections, debugTime, debugPathValue, MAX_CHANGE_AGE } from "./pathValueCore";
|
|
@@ -40,13 +40,42 @@ class ValidStateComputer {
|
|
|
40
40
|
|
|
41
41
|
let ourValues: PathValue[] = [];
|
|
42
42
|
let notOurValues: PathValue[] = [];
|
|
43
|
-
|
|
43
|
+
pathValues = pathValues.filter(value => {
|
|
44
|
+
// NOTE: I think if it's initial trigger we need to process it anyways?
|
|
45
|
+
if (!config.initialTriggers.values.has(value.path)) {
|
|
46
|
+
// Ignore values we already have. We receive duplicates, often due to values being broadcast multiple times in order to prevent data loss.
|
|
47
|
+
let existingValue = authorityStorage.getValueExact(value.path, value.time);
|
|
48
|
+
// If we already have the value and the valid state is the exact same, then remove it from path values and don't even add it to our values. Pretend like we never received it.
|
|
49
|
+
if (existingValue && !!existingValue.valid === !!value.valid) {
|
|
50
|
+
let parentPath = getParentPathStr(value.path);
|
|
51
|
+
// We check for the parent trigger late because getting the parent path is a little bit expensive
|
|
52
|
+
if (!config.initialTriggers.parentPaths.has(parentPath)) {
|
|
53
|
+
if (PathRouter.isLocalPath(value.path)) {
|
|
54
|
+
require("debugbreak")(2);
|
|
55
|
+
debugger;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
console.log(`Ignoring duplicate value.`, {
|
|
59
|
+
path: value.path,
|
|
60
|
+
timeId: value.time.time,
|
|
61
|
+
timeIdVersion: value.time.version,
|
|
62
|
+
timeIdFull: value.time,
|
|
63
|
+
existingValid: existingValue?.valid,
|
|
64
|
+
newValid: value.valid,
|
|
65
|
+
});
|
|
66
|
+
// NOTE: This should be safe. If this causes any problems, it will probably be if we're not the authority on it. In which case, we could move this check so it only happens if we are the authority on it. However, I think even if we aren't the authority, if we already have the exact value and the valid state hasn't changed, we really shouldn't have to ingest it. Watchers should be triggering when they start watching, so they shouldn't need us to trigger them again by re-ingesting it.
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
44
72
|
if (!PathRouter.isSelfAuthority(value.path)) {
|
|
45
73
|
notOurValues.push(value);
|
|
46
74
|
} else {
|
|
47
75
|
ourValues.push(value);
|
|
48
76
|
}
|
|
49
|
-
|
|
77
|
+
return true;
|
|
78
|
+
});
|
|
50
79
|
authorityStorage.ingestValues(notOurValues, { doNotArchive });
|
|
51
80
|
|
|
52
81
|
|
|
@@ -93,7 +122,6 @@ class ValidStateComputer {
|
|
|
93
122
|
}
|
|
94
123
|
}
|
|
95
124
|
|
|
96
|
-
|
|
97
125
|
let initialPrevValidStates: Map<PathValue, boolean | undefined> | undefined = this.capturePrevValidStates(ourValues);
|
|
98
126
|
authorityStorage.ingestValues(ourValues, { doNotArchive });
|
|
99
127
|
|
|
@@ -431,7 +431,7 @@ class AuthorityPathValueStorage {
|
|
|
431
431
|
public getValue(path: string): unknown {
|
|
432
432
|
return pathValueSerializer.getPathValue(this.getValueAtTime(path));
|
|
433
433
|
}
|
|
434
|
-
public
|
|
434
|
+
public getValueExact(path: string, time: Time): PathValue | undefined {
|
|
435
435
|
let value = this.getValueAtTime(path, time, true);
|
|
436
436
|
if (value && compareTime(value.time, time) === 0) {
|
|
437
437
|
return value;
|
|
@@ -1026,8 +1026,6 @@ export class Querysub {
|
|
|
1026
1026
|
RequireController.addMapGetModules(async (result, args) => {
|
|
1027
1027
|
let configObj = args[2] as { signedIdentity: SignedIdentity | undefined } | undefined;
|
|
1028
1028
|
if (!await isAllowedToSeeSource(configObj?.signedIdentity)) {
|
|
1029
|
-
require("debugbreak")(2);
|
|
1030
|
-
debugger;
|
|
1031
1029
|
await isAllowedToSeeSource(configObj?.signedIdentity);
|
|
1032
1030
|
//console.log(red(`Not allowed to see source`));
|
|
1033
1031
|
for (let [key, value] of Object.entries(result.modules)) {
|
|
@@ -13,7 +13,8 @@ export const STREAMING_BLOCK_THRESHOLD = 1024 * 1024;
|
|
|
13
13
|
|
|
14
14
|
export const PUBLIC_MOVE_THRESHOLD = timeInHour * 3;
|
|
15
15
|
// Max uncompressed data size
|
|
16
|
-
|
|
16
|
+
// NOTE: This used to be 512MB, but searching was really slow. I think making this smaller will make searching faster?
|
|
17
|
+
export const MAX_SINGLE_FILE_DATA = 1024 * 1024 * 64;
|
|
17
18
|
|
|
18
19
|
export const MOVING_TIMEOUT = timeInMinute * 5;
|
|
19
20
|
|
|
@@ -479,8 +479,7 @@ export class IndexedLogs<T> {
|
|
|
479
479
|
if (onResultsCallback) {
|
|
480
480
|
interval = setInterval(async () => {
|
|
481
481
|
updateResultsStats();
|
|
482
|
-
let
|
|
483
|
-
let shouldContinue = await onResultsCallback(results);
|
|
482
|
+
let shouldContinue = await onResultsCallback(getFinalResults());
|
|
484
483
|
if (!shouldContinue) {
|
|
485
484
|
if (!results.cancel) {
|
|
486
485
|
console.log(blue(`Cancelled search on ${this.config.name}`));
|
|
@@ -6,7 +6,7 @@ import { BufferIndex } from "./BufferIndex";
|
|
|
6
6
|
import { LogStreamer } from "./LogStreamer";
|
|
7
7
|
import { TimeFileTree } from "./TimeFileTree";
|
|
8
8
|
import { blue, green, magenta } from "socket-function/src/formatting/logColors";
|
|
9
|
-
import { formatNumber, formatTime } from "socket-function/src/formatting/format";
|
|
9
|
+
import { formatDateTime, formatNumber, formatTime } from "socket-function/src/formatting/format";
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
|
|
@@ -150,15 +150,15 @@ export async function moveLogsToPublic(config: {
|
|
|
150
150
|
let groups: typeof initialGroups = [];
|
|
151
151
|
for (let group of initialGroups) {
|
|
152
152
|
let currentSize = 0;
|
|
153
|
-
|
|
153
|
+
|
|
154
154
|
for (let path of group) {
|
|
155
155
|
let size = sizeMap.get(path) || 0;
|
|
156
|
-
|
|
156
|
+
|
|
157
157
|
if (groups.length === 0 || currentSize + size > sizeThreshold) {
|
|
158
158
|
groups.push([]);
|
|
159
159
|
currentSize = 0;
|
|
160
160
|
}
|
|
161
|
-
|
|
161
|
+
|
|
162
162
|
groups[groups.length - 1].push(path);
|
|
163
163
|
currentSize += size;
|
|
164
164
|
}
|
|
@@ -256,7 +256,7 @@ export async function moveLogsToPublic(config: {
|
|
|
256
256
|
await localLogs.del(path.fullPath + indexExtension);
|
|
257
257
|
}));
|
|
258
258
|
|
|
259
|
-
console.log(green(`(${i + 1}/${groups.length}) Wrote ${encoded.length} log files to public (${formatNumber(encoded.reduce((acc, x) => acc + x.uncompressedSize, 0))}B compressed to ${formatNumber(encoded.reduce((acc, x) => acc + x.compressedSize, 0))}B + ${formatNumber(encoded.reduce((acc, x) => acc + x.index.length, 0))}B index) in ${formatTime(Date.now() - time)}`) + ` (${blue(config.loggerName)})`);
|
|
259
|
+
console.log(green(`(${i + 1}/${groups.length}) Wrote ${encoded.length} log files to public from ${group.length} input files, ${formatDateTime(startTime)} to ${formatDateTime(endTime)} (${formatNumber(encoded.reduce((acc, x) => acc + x.uncompressedSize, 0))}B compressed to ${formatNumber(encoded.reduce((acc, x) => acc + x.compressedSize, 0))}B + ${formatNumber(encoded.reduce((acc, x) => acc + x.index.length, 0))}B index) in ${formatTime(Date.now() - time)}`) + ` (${blue(config.loggerName)})`);
|
|
260
260
|
}
|
|
261
261
|
|
|
262
262
|
// Clean up orphaned index files (index files without corresponding data files)
|
|
@@ -99,7 +99,7 @@ export class ObjectDisplay extends qreact.Component<{
|
|
|
99
99
|
<span className={css.maxHeight("70vh").overflowAuto} onClick={log}>
|
|
100
100
|
<div class={css.relative}>
|
|
101
101
|
{renderValue(value, [])}
|
|
102
|
-
<MeasureHeightCSS />
|
|
102
|
+
{/* <MeasureHeightCSS /> */}
|
|
103
103
|
</div>
|
|
104
104
|
</span>
|
|
105
105
|
</ShowMore>;
|