querysub 0.508.0 → 0.509.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/bin/function-public.js +2 -0
- package/bin/server.js +0 -2
- package/package.json +3 -4
- package/spec.txt +5 -0
- package/src/-f-node-discovery/NodeDiscovery.ts +1 -1
- package/src/-g-core-values/NodeCapabilities.ts +4 -0
- package/src/-g-core-values/scheduledShutdown.ts +48 -0
- package/src/0-path-value-core/AuthorityLookup.ts +50 -9
- package/src/0-path-value-core/PathRouter.ts +10 -31
- package/src/0-path-value-core/PathRouterRouteOverride.ts +14 -98
- package/src/0-path-value-core/PathRouterServerAuthoritySpec.tsx +1 -4
- package/src/0-path-value-core/PathValueCommitter.ts +1 -6
- package/src/0-path-value-core/PathValueController.ts +5 -7
- package/src/0-path-value-core/PathWatcher.ts +8 -11
- package/src/0-path-value-core/hackedPackedPathParentFiltering.ts +8 -22
- package/src/0-path-value-core/pathValueCore.ts +4 -5
- package/src/1-path-client/RemoteWatcher.ts +47 -34
- package/src/2-proxy/archiveMoveHarness.ts +2 -1
- package/src/3-path-functions/PathFunctionHelpers.ts +11 -10
- package/src/3-path-functions/PathFunctionRunner.ts +165 -24
- package/src/3-path-functions/functionReplay.ts +2 -0
- package/src/3-path-functions/syncSchema.ts +0 -2
- package/src/4-deploy/edgeBootstrap.ts +10 -0
- package/src/4-deploy/edgeClientWatcher.tsx +4 -6
- package/src/4-deploy/edgeNodes.ts +52 -12
- package/src/4-querysub/FunctionRunnerTracking.ts +290 -0
- package/src/4-querysub/Querysub.ts +11 -16
- package/src/4-querysub/QuerysubController.ts +13 -11
- package/src/4-querysub/permissions.ts +3 -0
- package/src/4-querysub/querysubPrediction.ts +2 -2
- package/src/config.ts +1 -3
- package/src/deployManager/components/ServiceDetailPage.tsx +127 -73
- package/src/deployManager/components/ServicesListPage.tsx +3 -3
- package/src/deployManager/machineApplyMainCode.ts +139 -153
- package/src/deployManager/machineController.ts +12 -61
- package/src/deployManager/machineSchema.ts +84 -3
- package/src/deployManager/setupMachineMain.ts +2 -1
- package/src/diagnostics/grossStats/GrossStatsController.ts +3 -2
- package/src/diagnostics/logs/errorTickets/autoFixer.ts +3 -2
- package/src/diagnostics/logs/logGitHashes.ts +1 -1
- package/src/diagnostics/managementPages.tsx +2 -0
- package/src/diagnostics/misc-pages/AuthoritySpecPage.tsx +36 -28
- package/src/diagnostics/misc-pages/FunctionCaptureAnalyzePage.tsx +1 -1
- package/src/library-components/NetworkSelector.tsx +77 -0
- package/src/misc.ts +19 -0
- package/src/user-implementation/userData.ts +1 -1
- package/src/zipThreaded.ts +3 -2
- package/bin/server-dev.js +0 -11
|
@@ -9,9 +9,7 @@ import { pathValueSerializer } from "../-h-path-value-serialize/PathValueSeriali
|
|
|
9
9
|
import { pathValueCommitter } from "./PathValueCommitter";
|
|
10
10
|
import { auditLog, isDebugLogEnabled } from "./auditLogs";
|
|
11
11
|
import { debugNodeId, debugNodeThread } from "../-c-identity/IdentityController";
|
|
12
|
-
import { getSlowdown, isDiskAudit, getDomain
|
|
13
|
-
import { getPathNetwork } from "./PathRouterRouteOverride";
|
|
14
|
-
import { decodeParentFilter } from "./hackedPackedPathParentFiltering";
|
|
12
|
+
import { getSlowdown, isDiskAudit, getDomain } from "../config";
|
|
15
13
|
import { decodeNodeId } from "sliftutils/misc/https/certs";
|
|
16
14
|
import { areNodeIdsEqual, isOwnNodeId } from "../-f-node-discovery/NodeDiscovery";
|
|
17
15
|
import { getNodeIdIP } from "socket-function/src/nodeCache";
|
|
@@ -235,10 +233,10 @@ export class PathValueControllerBase {
|
|
|
235
233
|
if (isDebugLogEnabled()) {
|
|
236
234
|
let sourceNodeId = debugNodeId(callerId);
|
|
237
235
|
for (let value of config.paths) {
|
|
238
|
-
auditLog("WATCH PATH", { path: value,
|
|
236
|
+
auditLog("WATCH PATH", { path: value, sourceNodeId, sourceNodeThreadId: debugNodeThread(callerId) });
|
|
239
237
|
}
|
|
240
238
|
for (let value of config.parentPaths) {
|
|
241
|
-
auditLog("WATCH PARENT PATH", { path: value,
|
|
239
|
+
auditLog("WATCH PARENT PATH", { path: value, sourceNodeId });
|
|
242
240
|
}
|
|
243
241
|
}
|
|
244
242
|
pathWatcher.watchPath({
|
|
@@ -254,10 +252,10 @@ export class PathValueControllerBase {
|
|
|
254
252
|
if (isDebugLogEnabled()) {
|
|
255
253
|
let sourceNodeId = debugNodeId(SocketFunction.getCaller().nodeId);
|
|
256
254
|
for (let value of config.paths) {
|
|
257
|
-
auditLog("UNWATCHING PATH", { path: value,
|
|
255
|
+
auditLog("UNWATCHING PATH", { path: value, sourceNodeId, sourceNodeThreadId: debugNodeThread(callerId) });
|
|
258
256
|
}
|
|
259
257
|
for (let value of config.parentPaths) {
|
|
260
|
-
auditLog("UNWATCHING PARENT PATH", { path: value,
|
|
258
|
+
auditLog("UNWATCHING PARENT PATH", { path: value, sourceNodeId });
|
|
261
259
|
}
|
|
262
260
|
}
|
|
263
261
|
pathWatcher.unwatchPath({ paths: config.paths, parentPaths: config.parentPaths, callback: callerId, reason: "PathValueController.unwatchLatest" });
|
|
@@ -11,9 +11,7 @@ import { PathValueControllerBase } from "./PathValueController";
|
|
|
11
11
|
import { PathRouter } from "./PathRouter";
|
|
12
12
|
import { auditLog } from "./auditLogs";
|
|
13
13
|
import { WatchConfig, authorityStorage, PathValue, NodeId, compareTime, isCoreQuiet, MAX_CHANGE_AGE, createMissingEpochValue } from "./pathValueCore";
|
|
14
|
-
import {
|
|
15
|
-
import { getPathNetwork } from "./PathRouterRouteOverride";
|
|
16
|
-
import { DEFAULT_NETWORK } from "../config";
|
|
14
|
+
import { matchesParentRangeFilter } from "./hackedPackedPathParentFiltering";
|
|
17
15
|
import { isClient } from "../config2";
|
|
18
16
|
import { delay } from "socket-function/src/batching";
|
|
19
17
|
import { isClientNodeId } from "socket-function/src/nodeCache";
|
|
@@ -172,22 +170,21 @@ class PathWatcher {
|
|
|
172
170
|
incrementWatcherSequence();
|
|
173
171
|
if (isOwnNodeId(config.nodeId)) {
|
|
174
172
|
for (let path of newPathsWatched) {
|
|
175
|
-
auditLog("new local WATCH VALUE", { path
|
|
173
|
+
auditLog("new local WATCH VALUE", { path });
|
|
176
174
|
}
|
|
177
175
|
for (let path of newParentsWatched) {
|
|
178
|
-
auditLog("new local WATCH PARENT", { path
|
|
176
|
+
auditLog("new local WATCH PARENT", { path });
|
|
179
177
|
}
|
|
180
178
|
} else {
|
|
181
179
|
for (let path of newPathsWatched) {
|
|
182
180
|
auditLog("new non-local WATCH VALUE", {
|
|
183
181
|
path,
|
|
184
|
-
network: getPathNetwork(path),
|
|
185
182
|
watcher: config.nodeId,
|
|
186
183
|
sourceNodeThreadId: debugNodeThread(config.nodeId),
|
|
187
184
|
});
|
|
188
185
|
}
|
|
189
186
|
for (let path of newParentsWatched) {
|
|
190
|
-
auditLog("new non-local WATCH PARENT", { path,
|
|
187
|
+
auditLog("new non-local WATCH PARENT", { path, watcher: config.nodeId });
|
|
191
188
|
}
|
|
192
189
|
}
|
|
193
190
|
console.info(`New PathValue watches`, {
|
|
@@ -247,9 +244,9 @@ class PathWatcher {
|
|
|
247
244
|
obj.watchers.delete(callback);
|
|
248
245
|
|
|
249
246
|
if (isOwnNodeId(callback)) {
|
|
250
|
-
auditLog("local UNWATCH PARENT", { path,
|
|
247
|
+
auditLog("local UNWATCH PARENT", { path, reason, remainingWatchers: obj.watchers.size });
|
|
251
248
|
} else {
|
|
252
|
-
auditLog("non-local UNWATCH PARENT", { path,
|
|
249
|
+
auditLog("non-local UNWATCH PARENT", { path, watcher: callback, remoteNodeId: debugNodeId(callback), remoteNodeThreadId: debugNodeThread(callback), reason, remainingWatchers: obj.watchers.size });
|
|
253
250
|
}
|
|
254
251
|
|
|
255
252
|
if (obj.watchers.size === 0) {
|
|
@@ -284,9 +281,9 @@ class PathWatcher {
|
|
|
284
281
|
watchers.watchers.delete(callback);
|
|
285
282
|
|
|
286
283
|
if (isOwnNodeId(callback)) {
|
|
287
|
-
auditLog("local UNWATCH VALUE", { path,
|
|
284
|
+
auditLog("local UNWATCH VALUE", { path, reason });
|
|
288
285
|
} else {
|
|
289
|
-
auditLog("non-local UNWATCH VALUE", { path,
|
|
286
|
+
auditLog("non-local UNWATCH VALUE", { path, watcher: callback, reason });
|
|
290
287
|
}
|
|
291
288
|
|
|
292
289
|
if (watchers.watchers.size === 0) {
|
|
@@ -3,8 +3,6 @@ import { getPathDepth, getPathIndexAssert, hack_getPackedPathSuffix, hack_setPac
|
|
|
3
3
|
import { AuthoritySpec, PathRouter } from "./PathRouter";
|
|
4
4
|
import { cache } from "socket-function/src/caching";
|
|
5
5
|
import { authorityLookup } from "./AuthorityLookup";
|
|
6
|
-
import { assertValidNetwork, getPathNetwork } from "./PathRouterRouteOverride";
|
|
7
|
-
import { DEFAULT_NETWORK } from "../config";
|
|
8
6
|
|
|
9
7
|
|
|
10
8
|
let getSpecForChildPath = (path: string) => authorityLookup.getOurSpec();
|
|
@@ -18,14 +16,9 @@ export function matchesParentRangeFilter(config: {
|
|
|
18
16
|
packedPath: string;
|
|
19
17
|
}) {
|
|
20
18
|
// If it equals the non packed path, then it must not be packed, and so it must match.
|
|
21
|
-
if (config.parentPath === config.packedPath)
|
|
22
|
-
return getPathNetwork(config.fullPath) === DEFAULT_NETWORK;
|
|
23
|
-
}
|
|
19
|
+
if (config.parentPath === config.packedPath) return true;
|
|
24
20
|
let filter = decodeParentFilter(config.packedPath);
|
|
25
|
-
if (!filter)
|
|
26
|
-
return getPathNetwork(config.fullPath) === DEFAULT_NETWORK;
|
|
27
|
-
}
|
|
28
|
-
if (getPathNetwork(config.fullPath) !== (filter.network || DEFAULT_NETWORK)) return false;
|
|
21
|
+
if (!filter) return true;
|
|
29
22
|
if (filter.start <= 0 && filter.end >= 1) return true;
|
|
30
23
|
let route = PathRouter.getRouteFull({ path: config.fullPath, spec: getSpecForChildPath(config.fullPath) });
|
|
31
24
|
return filter.start <= route && route < filter.end;
|
|
@@ -33,14 +26,12 @@ export function matchesParentRangeFilter(config: {
|
|
|
33
26
|
|
|
34
27
|
export const filterChildPathsBase = measureWrap(
|
|
35
28
|
function filterChildPathsBase(parentPath: string, packedSuffix: string, paths: Set<string>): Set<string> {
|
|
36
|
-
let [startFractionStr, endFractionStr
|
|
29
|
+
let [startFractionStr, endFractionStr] = packedSuffix.split("|");
|
|
37
30
|
let startFraction = Number(startFractionStr);
|
|
38
31
|
let endFraction = Number(endFractionStr);
|
|
39
|
-
let filterNetwork = network || DEFAULT_NETWORK;
|
|
40
32
|
|
|
41
33
|
let filtered = new Set<string>();
|
|
42
34
|
for (let path of paths) {
|
|
43
|
-
if (getPathNetwork(path) !== filterNetwork) continue;
|
|
44
35
|
// TODO: We can make this significantly more efficient as once we know if it's a prefix or not, we know the underlying function call every time. However, that starts to get complicated, and I don't think this is going to be a bottleneck.
|
|
45
36
|
let route = PathRouter.getRouteFull({ path, spec: getSpecForChildPath(path) });
|
|
46
37
|
if (startFraction <= route && route < endFraction) {
|
|
@@ -56,19 +47,14 @@ export function encodeParentFilter(config: {
|
|
|
56
47
|
path: string;
|
|
57
48
|
startFraction: number;
|
|
58
49
|
endFraction: number;
|
|
59
|
-
network?: string;
|
|
60
50
|
}) {
|
|
61
|
-
|
|
62
|
-
if (config.network) {
|
|
63
|
-
assertValidNetwork(config.network);
|
|
64
|
-
suffix += `|${config.network}`;
|
|
65
|
-
}
|
|
66
|
-
return hack_setPackedPathSuffix(config.path, suffix);
|
|
51
|
+
return hack_setPackedPathSuffix(config.path, `${config.startFraction}|${config.endFraction}`);
|
|
67
52
|
}
|
|
68
|
-
export function decodeParentFilter(path: string): { path: string; start: number, end: number
|
|
53
|
+
export function decodeParentFilter(path: string): { path: string; start: number, end: number } | undefined {
|
|
69
54
|
let packedSuffix = hack_getPackedPathSuffix(path);
|
|
70
55
|
if (!packedSuffix) return undefined;
|
|
71
|
-
let [startStr, endStr
|
|
56
|
+
let [startStr, endStr] = packedSuffix.split("|");
|
|
72
57
|
let unpackedPath = hack_stripPackedPath(path);
|
|
73
|
-
return { path: unpackedPath, start: Number(startStr), end: Number(endStr)
|
|
58
|
+
return { path: unpackedPath, start: Number(startStr), end: Number(endStr) };
|
|
59
|
+
|
|
74
60
|
}
|
|
@@ -24,8 +24,7 @@ import { fastHash } from "../misc/hash";
|
|
|
24
24
|
import { authorityLookup } from "./AuthorityLookup";
|
|
25
25
|
import { onPathInteracted } from "../diagnostics/pathAuditerCallback";
|
|
26
26
|
import { decodeParentFilter, filterChildPathsBase } from "./hackedPackedPathParentFiltering";
|
|
27
|
-
import {
|
|
28
|
-
import { isDiskAudit, DEFAULT_NETWORK } from "../config";
|
|
27
|
+
import { isDiskAudit } from "../config";
|
|
29
28
|
import { removeRange } from "../rangeMath";
|
|
30
29
|
import { remoteWatcher } from "../1-path-client/RemoteWatcher";
|
|
31
30
|
import { setFlag } from "socket-function/require/compileFlags";
|
|
@@ -570,14 +569,14 @@ class AuthorityPathValueStorage {
|
|
|
570
569
|
if (this.DEBUG_UNWATCH) {
|
|
571
570
|
console.log(blue(`Unsyncing path at ${Date.now()}`), path);
|
|
572
571
|
}
|
|
573
|
-
auditLog("DESTROY PATH", { path
|
|
572
|
+
auditLog("DESTROY PATH", { path });
|
|
574
573
|
|
|
575
574
|
this.isSyncedCache.delete(path);
|
|
576
575
|
this.removePathFromStorage(path, "unwatched");
|
|
577
576
|
}
|
|
578
577
|
public markParentPathAsUnwatched(path: string) {
|
|
579
578
|
if (this.parentsSynced.has(path)) {
|
|
580
|
-
console.info(`Unwatching path that is a parent synced path (fine, but might be an issue)`, { path
|
|
579
|
+
console.info(`Unwatching path that is a parent synced path (fine, but might be an issue)`, { path });
|
|
581
580
|
}
|
|
582
581
|
// NOTE: I don't think we have to handle the case where we are the authority,
|
|
583
582
|
// because we don't delete any actual data here.
|
|
@@ -696,7 +695,7 @@ class AuthorityPathValueStorage {
|
|
|
696
695
|
public addParentSyncs(parentSyncs: { parentPath: string; sourceNodeId: string }[]) {
|
|
697
696
|
for (let obj of parentSyncs) {
|
|
698
697
|
if (isDebugLogEnabled()) {
|
|
699
|
-
auditLog("RECEIVED PARENT PATH", { parentPath: obj.parentPath,
|
|
698
|
+
auditLog("RECEIVED PARENT PATH", { parentPath: obj.parentPath, remoteNodeId: debugNodeId(obj.sourceNodeId), remoteNodeThreadId: debugNodeThread(obj.sourceNodeId) });
|
|
700
699
|
}
|
|
701
700
|
|
|
702
701
|
let decoded = decodeParentFilter(obj.parentPath);
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { SocketFunction } from "socket-function/SocketFunction";
|
|
3
3
|
import { batchFunction, runInSerial, runInfinitePoll } from "socket-function/src/batching";
|
|
4
4
|
import { cache } from "socket-function/src/caching";
|
|
5
|
-
import { isNode, sort } from "socket-function/src/misc";
|
|
5
|
+
import { isNode, sort, timeInMinute, timeInSecond } from "socket-function/src/misc";
|
|
6
6
|
import { measureFnc, measureBlock } from "socket-function/src/profiling/measure";
|
|
7
7
|
import { getOwnNodeId, isOwnNodeId } from "../-f-node-discovery/NodeDiscovery";
|
|
8
8
|
import { PathValueController } from "../0-path-value-core/PathValueController";
|
|
@@ -20,8 +20,6 @@ import { auditLog, isDebugLogEnabled } from "../0-path-value-core/auditLogs";
|
|
|
20
20
|
import { debugNodeThread } from "../-c-identity/IdentityController";
|
|
21
21
|
import { authorityLookup } from "../0-path-value-core/AuthorityLookup";
|
|
22
22
|
import { decodeParentFilter, encodeParentFilter, registerGetSpecForChildPath } from "../0-path-value-core/hackedPackedPathParentFiltering";
|
|
23
|
-
import { getPathNetwork } from "../0-path-value-core/PathRouterRouteOverride";
|
|
24
|
-
import { DEFAULT_NETWORK } from "../config";
|
|
25
23
|
import { removeRange, rangesOverlap } from "../rangeMath";
|
|
26
24
|
import { evaluateValidStates } from "../config2";
|
|
27
25
|
|
|
@@ -30,6 +28,10 @@ setImmediate(() => import("../4-querysub/Querysub"));
|
|
|
30
28
|
// NOTE: In some cases this can half our the PathValues sent. AND, it can reduce our sync requests by N, and as those are uploads those are event more expensive. However, it also complicates other code, requiring not just checking if a value is synced, but also the parent path. We should try to make this stable, but worst case scenario, we can set this to false, and it will make our synchronization a lot easier to verify.
|
|
31
29
|
const STOP_KEYS_DOUBLE_SENDS = true;
|
|
32
30
|
|
|
31
|
+
// How close to an authority's scheduled shutdown we start treating it like a disconnect (rehoming all the paths we watch on it).
|
|
32
|
+
const SHUTDOWN_REHOME_WINDOW = timeInMinute * 5;
|
|
33
|
+
const SHUTDOWN_REHOME_POLL_INTERVAL = timeInSecond * 30;
|
|
34
|
+
|
|
33
35
|
// NOTE: If a parent watch is broken up between multiple nodes, we generally watch everything on all those nodes and then filter when we receive the data. This isn't efficient, and we should probably change it. However, in practice, it's probably fine, as it's unlikely for the watches to be broken up to a much finer granularity than the network (it would require very strange partially overlapping sharding cases which no reasonable sharding setup would ever satisfy).
|
|
34
36
|
export class RemoteWatcher {
|
|
35
37
|
public static DEBUG = false;
|
|
@@ -48,10 +50,8 @@ export class RemoteWatcher {
|
|
|
48
50
|
start: number;
|
|
49
51
|
// exclusive
|
|
50
52
|
end: number;
|
|
51
|
-
// encodeParentFilter({ path: cleanPath, startFraction: start, endFraction: end
|
|
53
|
+
// encodeParentFilter({ path: cleanPath, startFraction: start, endFraction: end })
|
|
52
54
|
finalPath: string;
|
|
53
|
-
// undefined means the default network
|
|
54
|
-
network?: string;
|
|
55
55
|
// All of the requests paths that request it (pre decodeParentFilter).
|
|
56
56
|
inputPaths: Set<string>;
|
|
57
57
|
|
|
@@ -126,6 +126,32 @@ export class RemoteWatcher {
|
|
|
126
126
|
// the chances the server will be useless is high, so... lag is probably fine in exchange for faster
|
|
127
127
|
// reconnection.
|
|
128
128
|
private reconnectLoop = runInfinitePoll(10000, () => this.tryToReconnectNow());
|
|
129
|
+
|
|
130
|
+
// Once an authority is this close to its scheduled shutdown, we act as if it disconnected, and find new homes for all the paths we watch on it (PathRouter avoids shutting down nodes, so the rewatch picks a different authority when one exists).
|
|
131
|
+
private rehomedShutdownNodes = new Set<string>();
|
|
132
|
+
private shutdownRehomeLoop = runInfinitePoll(SHUTDOWN_REHOME_POLL_INTERVAL, () => this.rehomeShuttingDownAuthorities());
|
|
133
|
+
private rehomeShuttingDownAuthorities() {
|
|
134
|
+
let authorityIds = new Set<string>(this.remoteWatchPaths.values());
|
|
135
|
+
for (let watchObj of this.remoteWatchParents2.values()) {
|
|
136
|
+
for (let range of watchObj.ranges) {
|
|
137
|
+
authorityIds.add(range.authorityId);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
for (let authorityId of authorityIds) {
|
|
141
|
+
if (this.rehomedShutdownNodes.has(authorityId)) continue;
|
|
142
|
+
let shutdownTime = authorityLookup.getScheduledShutdownTime(authorityId);
|
|
143
|
+
if (shutdownTime === undefined || shutdownTime - Date.now() > SHUTDOWN_REHOME_WINDOW) continue;
|
|
144
|
+
this.rehomedShutdownNodes.add(authorityId);
|
|
145
|
+
console.log(yellow(`Authority ${authorityId} is scheduled to shut down at ${new Date(shutdownTime).toLocaleString()}, rehoming all paths watched on it`));
|
|
146
|
+
logErrors(this.refreshAllWatches(authorityId));
|
|
147
|
+
}
|
|
148
|
+
// Nodes we no longer watch don't need an entry (otherwise this grows forever, as every switchover shuts down a node)
|
|
149
|
+
for (let nodeId of Array.from(this.rehomedShutdownNodes)) {
|
|
150
|
+
if (!authorityIds.has(nodeId)) {
|
|
151
|
+
this.rehomedShutdownNodes.delete(nodeId);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
129
155
|
private async tryToReconnectNow() {
|
|
130
156
|
if (!this.disconnectedPaths.size && !this.disconnectedParents.size) return;
|
|
131
157
|
|
|
@@ -143,10 +169,8 @@ export class RemoteWatcher {
|
|
|
143
169
|
let parentPath = getParentPathStr(path);
|
|
144
170
|
let watchObj = this.remoteWatchParents2.get(parentPath);
|
|
145
171
|
if (!watchObj) return undefined;
|
|
146
|
-
let pathNetwork = getPathNetwork(path);
|
|
147
172
|
// NOTE: PathRouter.getChildReadNodes PROMISES That all of the nodes will hash in the same way, and if they all hash in the same way, and they have different ranges, then every path will uniquely map to a single value.
|
|
148
173
|
return watchObj.ranges.find(x => {
|
|
149
|
-
if ((x.network || DEFAULT_NETWORK) !== pathNetwork) return false;
|
|
150
174
|
let route = PathRouter.getRouteFull({ path, spec: x.authoritySpec });
|
|
151
175
|
return x.start <= route && route < x.end;
|
|
152
176
|
});
|
|
@@ -155,10 +179,8 @@ export class RemoteWatcher {
|
|
|
155
179
|
let parentPath = getParentPathStr(path);
|
|
156
180
|
let watchObj = this.remoteWatchParents2.get(parentPath);
|
|
157
181
|
if (!watchObj) return undefined;
|
|
158
|
-
let pathNetwork = getPathNetwork(path);
|
|
159
182
|
// NOTE: PathRouter.getChildReadNodes PROMISES That all of the nodes will hash in the same way, and if they all hash in the same way, and they have different ranges, then every path will uniquely map to a single value.
|
|
160
183
|
for (let range of watchObj.ranges) {
|
|
161
|
-
if ((range.network || DEFAULT_NETWORK) !== pathNetwork) continue;
|
|
162
184
|
let route = PathRouter.getRouteFull({ path, spec: range.authoritySpec });
|
|
163
185
|
if (range.start <= route && route < range.end) {
|
|
164
186
|
return route;
|
|
@@ -266,17 +288,17 @@ export class RemoteWatcher {
|
|
|
266
288
|
if (totalMissingPaths === 0) {
|
|
267
289
|
let authorities = authorityLookup.getTopologySync();
|
|
268
290
|
authorityId = PathRouter.getReadyAuthority(path)?.nodeId;
|
|
269
|
-
console.warn(`Missing authority for path ${path}
|
|
291
|
+
console.warn(`Missing authority for path ${path}`, { authorities });
|
|
270
292
|
}
|
|
271
293
|
totalMissingPaths++;
|
|
272
294
|
if (!this.disconnectedPaths.has(path)) {
|
|
273
295
|
newDisconnectPaths++;
|
|
274
296
|
this.disconnectedPaths.add(path);
|
|
275
|
-
auditLog("NO AUTHORITY FOR PATH", { path
|
|
297
|
+
auditLog("NO AUTHORITY FOR PATH", { path });
|
|
276
298
|
}
|
|
277
299
|
continue;
|
|
278
300
|
}
|
|
279
|
-
auditLog("FOUND AUTHORITY FOR PATH", { path,
|
|
301
|
+
auditLog("FOUND AUTHORITY FOR PATH", { path, authorityId });
|
|
280
302
|
this.disconnectedPaths.delete(path);
|
|
281
303
|
foundPaths++;
|
|
282
304
|
|
|
@@ -297,7 +319,6 @@ export class RemoteWatcher {
|
|
|
297
319
|
|
|
298
320
|
let decodedParentFilter = decodeParentFilter(path);
|
|
299
321
|
let cleanPath = decodedParentFilter?.path ?? path;
|
|
300
|
-
let network = decodedParentFilter?.network;
|
|
301
322
|
|
|
302
323
|
let rangeStart = 0;
|
|
303
324
|
let rangeEnd = 1;
|
|
@@ -306,8 +327,6 @@ export class RemoteWatcher {
|
|
|
306
327
|
rangeEnd = decodedParentFilter.end;
|
|
307
328
|
}
|
|
308
329
|
|
|
309
|
-
const sameNetwork = (rangeNetwork: string | undefined) => (rangeNetwork || DEFAULT_NETWORK) === (network || DEFAULT_NETWORK);
|
|
310
|
-
|
|
311
330
|
let watchObj = this.remoteWatchParents2.get(cleanPath);
|
|
312
331
|
|
|
313
332
|
if (watchObj && !config.tryReconnect) {
|
|
@@ -317,13 +336,11 @@ export class RemoteWatcher {
|
|
|
317
336
|
end: rangeEnd,
|
|
318
337
|
}];
|
|
319
338
|
for (let range of watchObj.ranges) {
|
|
320
|
-
if (!sameNetwork(range.network)) continue;
|
|
321
339
|
removeRange(requiredRanges, range);
|
|
322
340
|
}
|
|
323
341
|
if (requiredRanges.length === 0) {
|
|
324
342
|
this.disconnectedParents.delete(path);
|
|
325
343
|
for (let range of watchObj.ranges) {
|
|
326
|
-
if (!sameNetwork(range.network)) continue;
|
|
327
344
|
if (rangesOverlap(range, { start: rangeStart, end: rangeEnd })) {
|
|
328
345
|
range.inputPaths.add(path);
|
|
329
346
|
}
|
|
@@ -332,8 +349,7 @@ export class RemoteWatcher {
|
|
|
332
349
|
}
|
|
333
350
|
}
|
|
334
351
|
|
|
335
|
-
let
|
|
336
|
-
let { nodes } = PathRouter.getChildReadNodes(readNodesPath, {
|
|
352
|
+
let { nodes } = PathRouter.getChildReadNodes(cleanPath, {
|
|
337
353
|
// Pass existing connected node ids as preferred
|
|
338
354
|
preferredNodeIds: watchObj?.ranges.map(x => x.authorityId) || []
|
|
339
355
|
});
|
|
@@ -361,7 +377,6 @@ export class RemoteWatcher {
|
|
|
361
377
|
// Compute which parts of [rangeStart, rangeEnd] aren't yet covered by existing watches
|
|
362
378
|
let missingRanges: { start: number; end: number }[] = [{ start: rangeStart, end: rangeEnd }];
|
|
363
379
|
for (let range of watchObj.ranges) {
|
|
364
|
-
if (!sameNetwork(range.network)) continue;
|
|
365
380
|
removeRange(missingRanges, range);
|
|
366
381
|
}
|
|
367
382
|
|
|
@@ -374,14 +389,13 @@ export class RemoteWatcher {
|
|
|
374
389
|
let clippedStart = Math.max(node.range.start, missing.start);
|
|
375
390
|
let clippedEnd = Math.min(node.range.end, missing.end);
|
|
376
391
|
|
|
377
|
-
let finalPath = encodeParentFilter({ path: cleanPath, startFraction: clippedStart, endFraction: clippedEnd
|
|
392
|
+
let finalPath = encodeParentFilter({ path: cleanPath, startFraction: clippedStart, endFraction: clippedEnd });
|
|
378
393
|
let existingRange = watchObj.ranges.find(x => x.finalPath === finalPath && x.authorityId === node.nodeId);
|
|
379
394
|
if (!existingRange) {
|
|
380
395
|
existingRange = {
|
|
381
396
|
start: clippedStart,
|
|
382
397
|
end: clippedEnd,
|
|
383
398
|
finalPath,
|
|
384
|
-
network,
|
|
385
399
|
inputPaths: new Set(),
|
|
386
400
|
authorityId: node.nodeId,
|
|
387
401
|
suppressedWatches: new Set(),
|
|
@@ -401,7 +415,6 @@ export class RemoteWatcher {
|
|
|
401
415
|
|
|
402
416
|
// Register path on pre-existing ranges that already cover parts of [rangeStart, rangeEnd]
|
|
403
417
|
for (let range of watchObj.ranges) {
|
|
404
|
-
if (!sameNetwork(range.network)) continue;
|
|
405
418
|
if (rangesOverlap(range, { start: rangeStart, end: rangeEnd })) {
|
|
406
419
|
range.inputPaths.add(path);
|
|
407
420
|
}
|
|
@@ -413,7 +426,7 @@ export class RemoteWatcher {
|
|
|
413
426
|
console.warn(`Some paths have no authority. We will search for an authority periodically until we find an authority. New missing ${newDisconnectPaths} paths and ${newDisconnectParents} parent paths, total missing ${this.disconnectedPaths.size} paths and ${this.disconnectedParents.size} parent paths.`);
|
|
414
427
|
let first10 = Array.from(this.disconnectedPaths).slice(0, 10);
|
|
415
428
|
for (let path of first10) {
|
|
416
|
-
console.log(`\t
|
|
429
|
+
console.log(`\t${path}`);
|
|
417
430
|
}
|
|
418
431
|
if (this.disconnectedPaths.size > 10) {
|
|
419
432
|
console.log(`\t... and ${this.disconnectedPaths.size - 10} more paths`);
|
|
@@ -433,10 +446,10 @@ export class RemoteWatcher {
|
|
|
433
446
|
if (isOwnNodeId(authorityId)) continue;
|
|
434
447
|
|
|
435
448
|
for (let path of paths) {
|
|
436
|
-
auditLog("remoteWatcher outer WATCH", { path,
|
|
449
|
+
auditLog("remoteWatcher outer WATCH", { path, remoteNodeId: authorityId });
|
|
437
450
|
}
|
|
438
451
|
for (let path of parentPaths) {
|
|
439
|
-
auditLog("remoteWatcher outer PARENT WATCH", { path,
|
|
452
|
+
auditLog("remoteWatcher outer PARENT WATCH", { path, remoteNodeId: authorityId });
|
|
440
453
|
}
|
|
441
454
|
|
|
442
455
|
if (STOP_KEYS_DOUBLE_SENDS) {
|
|
@@ -445,7 +458,7 @@ export class RemoteWatcher {
|
|
|
445
458
|
let range = this.getRemoteWatchParentRange(path);
|
|
446
459
|
if (range) {
|
|
447
460
|
range.suppressedWatches.add(path);
|
|
448
|
-
auditLog("remoteWatcher outer WATCH SUPPRESSED", { path,
|
|
461
|
+
auditLog("remoteWatcher outer WATCH SUPPRESSED", { path, remoteNodeId: authorityId });
|
|
449
462
|
return false;
|
|
450
463
|
}
|
|
451
464
|
return true;
|
|
@@ -497,10 +510,10 @@ export class RemoteWatcher {
|
|
|
497
510
|
}
|
|
498
511
|
for (let [authorityId, { paths, parentPaths }] of byAuthority) {
|
|
499
512
|
for (let path of paths) {
|
|
500
|
-
auditLog("remoteWatcher inner WATCH", { path,
|
|
513
|
+
auditLog("remoteWatcher inner WATCH", { path, remoteNodeId: authorityId });
|
|
501
514
|
}
|
|
502
515
|
for (let path of parentPaths) {
|
|
503
|
-
auditLog("remoteWatcher inner WATCH PARENT", { path,
|
|
516
|
+
auditLog("remoteWatcher inner WATCH PARENT", { path, remoteNodeId: authorityId });
|
|
504
517
|
}
|
|
505
518
|
// NOTE: We log watches here because we want to log batched watches
|
|
506
519
|
ActionsHistory.OnNewWatches({ newPathsWatched: paths, newParentsWatched: parentPaths, debugName: batched[0].debugName, authorityId });
|
|
@@ -511,10 +524,10 @@ export class RemoteWatcher {
|
|
|
511
524
|
};
|
|
512
525
|
if (isDebugLogEnabled()) {
|
|
513
526
|
for (let path of paths) {
|
|
514
|
-
auditLog("Asking to watch path", { path,
|
|
527
|
+
auditLog("Asking to watch path", { path, authorityId, targetNodeThreadId: debugNodeThread(authorityId) });
|
|
515
528
|
}
|
|
516
529
|
for (let path of parentPaths) {
|
|
517
|
-
auditLog("Asking to watch parent path", { path,
|
|
530
|
+
auditLog("Asking to watch parent path", { path, authorityId, targetNodeThreadId: debugNodeThread(authorityId) });
|
|
518
531
|
}
|
|
519
532
|
}
|
|
520
533
|
logErrors(RemoteWatcher.REMOTE_WATCH_FUNCTION(config, authorityId));
|
|
@@ -604,10 +617,10 @@ export class RemoteWatcher {
|
|
|
604
617
|
}
|
|
605
618
|
for (let [authorityIdBase, { paths, parentPaths }] of unwatchesPerAuthority.entries()) {
|
|
606
619
|
for (let path of paths) {
|
|
607
|
-
auditLog("remoteWatcher inner UNWATCH", { path,
|
|
620
|
+
auditLog("remoteWatcher inner UNWATCH", { path, remoteNodeId: authorityIdBase });
|
|
608
621
|
}
|
|
609
622
|
for (let path of parentPaths) {
|
|
610
|
-
auditLog("remoteWatcher inner UNWATCH PARENT", { path,
|
|
623
|
+
auditLog("remoteWatcher inner UNWATCH PARENT", { path, remoteNodeId: authorityIdBase });
|
|
611
624
|
}
|
|
612
625
|
if (!isOwnNodeId(authorityIdBase)) {
|
|
613
626
|
logErrors(RemoteWatcher.REMOTE_UNWATCH_FUNCTION({ paths, parentPaths }, authorityIdBase));
|
|
@@ -178,7 +178,8 @@ export async function runArchiveMover(config: {
|
|
|
178
178
|
excludedValues.push(value);
|
|
179
179
|
}
|
|
180
180
|
}
|
|
181
|
-
if
|
|
181
|
+
// If we are excluding it because we're filtering it, then there's no reason to rewrite the file. HOWEVER, If the file is empty, which can happen if it's corrupted, then we should move the file. Otherwise, corrupted files never get moved by anything.
|
|
182
|
+
if (includedValues.length === 0 && valuesObj.values.length > 0) {
|
|
182
183
|
continue;
|
|
183
184
|
}
|
|
184
185
|
if (excludedValues.length > 0) {
|
|
@@ -18,8 +18,14 @@ import { interceptCalls } from "../-0-hooks/hooks";
|
|
|
18
18
|
import { createRoutingOverrideKey } from "../0-path-value-core/PathRouterRouteOverride";
|
|
19
19
|
import { PathRouter } from "../0-path-value-core/PathRouter";
|
|
20
20
|
import { getPathFromProxy } from "../2-proxy/pathValueProxy";
|
|
21
|
-
import { getDomain,
|
|
21
|
+
import { getDomain, isPublic } from "../config";
|
|
22
22
|
import { isServer } from "../config2";
|
|
23
|
+
|
|
24
|
+
// Deferred, as syncSchema.ts imports us at module init, and FunctionRunnerTracking imports syncSchema.
|
|
25
|
+
let functionRunnerTracking: typeof import("../4-querysub/FunctionRunnerTracking") | undefined;
|
|
26
|
+
setImmediate(async () => {
|
|
27
|
+
functionRunnerTracking = await import("../4-querysub/FunctionRunnerTracking");
|
|
28
|
+
});
|
|
23
29
|
import { getTimeUnique } from "socket-function/src/bits";
|
|
24
30
|
|
|
25
31
|
// NOTE: We could deploy single functions, but... we will almost always be updating all functions at
|
|
@@ -160,14 +166,11 @@ export function writeFunctionCall(config: {
|
|
|
160
166
|
// which gives them about a minute to guess it...
|
|
161
167
|
let callId = now + "_" + secureRandom();
|
|
162
168
|
|
|
163
|
-
//
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
network = getPrimaryNetwork();
|
|
167
|
-
if (network === DEFAULT_NETWORK) {
|
|
168
|
-
network = undefined;
|
|
169
|
-
}
|
|
169
|
+
// The CALLER has to pick the network (instead of the receiving server), because the caller needs to watch the call's result path (which the network is part of) ahead of time — that is how predictions work.
|
|
170
|
+
if (!functionRunnerTracking) {
|
|
171
|
+
throw new Error(`Function runner tracking is not loaded yet, so a network cannot be picked for the call ${moduleId}.${functionId}`);
|
|
170
172
|
}
|
|
173
|
+
let network = functionRunnerTracking.getSelectedNetwork();
|
|
171
174
|
|
|
172
175
|
// We HAVE to override the routing id, otherwise PathFunctionRunner secondary sharding won't work
|
|
173
176
|
callId = getCallIdOverride({
|
|
@@ -175,12 +178,10 @@ export function writeFunctionCall(config: {
|
|
|
175
178
|
functionId,
|
|
176
179
|
callId,
|
|
177
180
|
args,
|
|
178
|
-
network,
|
|
179
181
|
}) || createRoutingOverrideKey({
|
|
180
182
|
originalKey: callId,
|
|
181
183
|
routeKey: callId,
|
|
182
184
|
remappedPrefix: getFunctionRunnerPrefix(moduleId),
|
|
183
|
-
network,
|
|
184
185
|
});
|
|
185
186
|
|
|
186
187
|
let argsEncoded = encodeArgs(args);
|