querysub 0.319.0 → 0.321.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/4-deploy/edgeNodes.ts +36 -7
- package/src/4-querysub/Querysub.ts +2 -2
- package/src/deployManager/components/ServicesListPage.tsx +15 -1
- package/src/deployManager/machineSchema.ts +7 -0
- package/src/diagnostics/logs/FastArchiveViewer.tsx +2 -2
- package/src/diagnostics/logs/errorNotifications/ErrorSuppressionUI.tsx +19 -18
- package/src/diagnostics/logs/lifeCycleAnalysis/spec.md +4 -0
package/package.json
CHANGED
|
@@ -7,7 +7,7 @@ import { getArchivesBackblazePublic } from "../-a-archives/archivesBackBlaze";
|
|
|
7
7
|
import { nestArchives } from "../-a-archives/archives";
|
|
8
8
|
import { SocketFunction } from "socket-function/SocketFunction";
|
|
9
9
|
import { delay, runInSerial, runInfinitePoll, runInfinitePollCallAtStart } from "socket-function/src/batching";
|
|
10
|
-
import { compare, compareArray, isNodeTrue, sort, timeInMinute } from "socket-function/src/misc";
|
|
10
|
+
import { compare, compareArray, isNodeTrue, sort, timeInMinute, timeInSecond } from "socket-function/src/misc";
|
|
11
11
|
import { cacheLimited, lazy } from "socket-function/src/caching";
|
|
12
12
|
import { canHaveChildren } from "socket-function/src/types";
|
|
13
13
|
import { hostArchives } from "../-b-authorities/cdnAuthority";
|
|
@@ -25,6 +25,7 @@ import { onEdgeNodesChanged } from "./edgeBootstrap";
|
|
|
25
25
|
import { startEdgeNotifier } from "./edgeClientWatcher";
|
|
26
26
|
import { getGitRefLive, getGitURLLive } from "./git";
|
|
27
27
|
import { DeployProgress } from "./deployFunctions";
|
|
28
|
+
import { PromiseObj } from "../promise";
|
|
28
29
|
|
|
29
30
|
const UPDATE_POLL_INTERVAL = timeInMinute * 15;
|
|
30
31
|
const DEAD_NODE_COUNT_THRESHOLD = 15;
|
|
@@ -117,19 +118,27 @@ export async function registerEdgeNode(config: {
|
|
|
117
118
|
|
|
118
119
|
if (!isLocal() && !noSyncing()) {
|
|
119
120
|
let loadedHash = "";
|
|
121
|
+
let firstLoad = new PromiseObj<void>();
|
|
120
122
|
Querysub.createWatcher(() => {
|
|
121
123
|
let hash = deploySchema()[getDomain()].deploy.live.hash;
|
|
122
124
|
if (hash === loadedHash || !Querysub.isAllSynced()) return;
|
|
123
125
|
|
|
124
126
|
loadedHash = hash;
|
|
125
127
|
void Promise.resolve().then(async () => {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
128
|
+
try {
|
|
129
|
+
await loadEntryPointsByHash({
|
|
130
|
+
host,
|
|
131
|
+
entryPaths: config.entryPaths,
|
|
132
|
+
hash,
|
|
133
|
+
});
|
|
134
|
+
} finally {
|
|
135
|
+
firstLoad.resolve();
|
|
136
|
+
}
|
|
131
137
|
});
|
|
132
138
|
});
|
|
139
|
+
console.log(`Waiting for entry point to finish loading for live hash before registering edge node`);
|
|
140
|
+
await firstLoad.promise;
|
|
141
|
+
console.log(`Finished waiting for entry point to finish loading for live hash before registering edge node`);
|
|
133
142
|
} else {
|
|
134
143
|
let gitHash = await getGitRefLive();
|
|
135
144
|
|
|
@@ -217,7 +226,7 @@ const startUpdateLoop = lazy(async () => {
|
|
|
217
226
|
startEdgeNotifier();
|
|
218
227
|
SocketFunction.expose(EdgeNodeController);
|
|
219
228
|
await getEdgeNodeConfigURL();
|
|
220
|
-
await runInfinitePollCallAtStart(UPDATE_POLL_INTERVAL, updateLoop);
|
|
229
|
+
await runInfinitePollCallAtStart(UPDATE_POLL_INTERVAL * (1 + Math.random() * 0.1), updateLoop);
|
|
221
230
|
});
|
|
222
231
|
|
|
223
232
|
async function updateLoop() {
|
|
@@ -292,6 +301,26 @@ async function updateEdgeNodesFile() {
|
|
|
292
301
|
|
|
293
302
|
console.log(magenta(`Updating ${edgeNodeIndexFile} with ${JSON.stringify(newEdgeNodeIndex)}`));
|
|
294
303
|
await edgeNodeStorage.set(edgeNodeIndexFile, Buffer.from(JSON.stringify(newEdgeNodeIndex)));
|
|
304
|
+
await delay(Math.random() * 1000);
|
|
305
|
+
async function verify() {
|
|
306
|
+
let buffer = await edgeNodeStorage.get(edgeNodeIndexFile);
|
|
307
|
+
let index = JSON.parse(buffer!.toString()) as EdgeNodesIndex;
|
|
308
|
+
let ownNodeId = getOwnNodeId();
|
|
309
|
+
return index.edgeNodes.some(x => x.nodeId === ownNodeId);
|
|
310
|
+
}
|
|
311
|
+
if (registeredEdgeNode) {
|
|
312
|
+
try {
|
|
313
|
+
if (!await verify()) {
|
|
314
|
+
console.error(`Found own our node wasn't in the edge node index file, trying to update again in a bit`);
|
|
315
|
+
await delay(Math.random() * 1000 + timeInSecond * 10);
|
|
316
|
+
await updateEdgeNodesFile();
|
|
317
|
+
}
|
|
318
|
+
} catch (e) {
|
|
319
|
+
console.error(`Failed to verify update to edge node index file due to an error, trying to update again in a bit`, { error: e });
|
|
320
|
+
await delay(Math.random() * 1000 + timeInSecond * 10);
|
|
321
|
+
await updateEdgeNodesFile();
|
|
322
|
+
}
|
|
323
|
+
}
|
|
295
324
|
}
|
|
296
325
|
|
|
297
326
|
export async function preloadUI(hash: string, progress?: DeployProgress) {
|
|
@@ -16,7 +16,7 @@ import { SocketFunction } from "socket-function/SocketFunction";
|
|
|
16
16
|
import { isHotReloading, watchFilesAndTriggerHotReloading } from "socket-function/hot/HotReloadController";
|
|
17
17
|
import { RequireController, setRequireBootRequire } from "socket-function/require/RequireController";
|
|
18
18
|
import { cache, cacheLimited, lazy } from "socket-function/src/caching";
|
|
19
|
-
import { getOwnMachineId, getThreadKeyCert, verifyMachineIdForPublicKey } from "../-a-auth/certs";
|
|
19
|
+
import { getOwnMachineId, getOwnThreadId, getThreadKeyCert, verifyMachineIdForPublicKey } from "../-a-auth/certs";
|
|
20
20
|
import { getHostedIP, getSNICerts, publishMachineARecords } from "../-e-certs/EdgeCertController";
|
|
21
21
|
import { LOCAL_DOMAIN, nodePathAuthority } from "../0-path-value-core/NodePathAuthorities";
|
|
22
22
|
import { debugCoreMode, registerGetCompressNetwork, encodeParentFilter, registerGetCompressDisk, authorityStorage } from "../0-path-value-core/pathValueCore";
|
|
@@ -519,7 +519,7 @@ export class Querysub {
|
|
|
519
519
|
|
|
520
520
|
public static getOwnNodeId = getOwnNodeId;
|
|
521
521
|
public static getSelfNodeId = getOwnNodeId;
|
|
522
|
-
public static getOwnThreadId =
|
|
522
|
+
public static getOwnThreadId = getOwnThreadId;
|
|
523
523
|
|
|
524
524
|
/** Set ClientWatcher.DEBUG_SOURCES to true for to be populated */
|
|
525
525
|
public static getTriggerReason() {
|
|
@@ -29,6 +29,12 @@ export class ServicesListPage extends qreact.Component {
|
|
|
29
29
|
|
|
30
30
|
let getMachineInfo = cache((machineId: string) => controller.getMachineInfo(machineId));
|
|
31
31
|
|
|
32
|
+
let keyCounts = new Map<string, number>();
|
|
33
|
+
for (let [serviceId, config] of services) {
|
|
34
|
+
let key = config?.parameters.key || "";
|
|
35
|
+
keyCounts.set(key, (keyCounts.get(key) || 0) + 1);
|
|
36
|
+
}
|
|
37
|
+
|
|
32
38
|
return <div className={css.vbox(16)}>
|
|
33
39
|
<div className={css.hbox(12).wrap}>
|
|
34
40
|
<h2 className={css.flexGrow(1)}>Services</h2>
|
|
@@ -87,6 +93,7 @@ export class ServicesListPage extends qreact.Component {
|
|
|
87
93
|
return acc + (machineInfo?.services[serviceId]?.totalTimesLaunched || 0);
|
|
88
94
|
}, 0);
|
|
89
95
|
let unknown = config.machineIds.length - runningMachines.length - failingMachines.length - missingMachines.length;
|
|
96
|
+
let duplicateKey = (keyCounts.get(config.parameters.key || "") || 0) > 1;
|
|
90
97
|
return <div className={css.hbox(10)}>
|
|
91
98
|
<Anchor noStyles key={serviceId}
|
|
92
99
|
values={[currentViewParam.getOverride("service-detail"), selectedServiceIdParam.getOverride(serviceId)]}
|
|
@@ -102,7 +109,14 @@ export class ServicesListPage extends qreact.Component {
|
|
|
102
109
|
<div className={css.hbox(12)}>
|
|
103
110
|
<div className={css.vbox(4).flexGrow(1)}>
|
|
104
111
|
<div className={css.fontSize(14).boldStyle}>{config.info.title}</div>
|
|
105
|
-
<div
|
|
112
|
+
<div className={css
|
|
113
|
+
+ (duplicateKey
|
|
114
|
+
&& css.hsl(0, 50, 50).colorhsl(0, 0, 95).pad2(4, 2))
|
|
115
|
+
}
|
|
116
|
+
title={duplicateKey ? "Duplicate key" : undefined}
|
|
117
|
+
>
|
|
118
|
+
{config.parameters.key}
|
|
119
|
+
</div>
|
|
106
120
|
<div>
|
|
107
121
|
{config.machineIds.length} configured {failingMachines.length > 0 && `(${failingMachines.length} failing)`} {missingMachines.length > 0 && `(${missingMachines.length} machine hasn't run service yet)`} {unknown > 0 && `(${unknown} unknown)`} • {totalLaunches} launches •
|
|
108
122
|
Deploy: {config.parameters.deploy ? "enabled" : "disabled"}
|
|
@@ -189,7 +189,14 @@ export class MachineServiceControllerBase {
|
|
|
189
189
|
public async setServiceConfigs(configs: ServiceConfig[]) {
|
|
190
190
|
let newMachines = new Set<string>();
|
|
191
191
|
let oldMachines = new Set<string>();
|
|
192
|
+
let usedKeys = new Set<string>();
|
|
192
193
|
for (let config of configs) {
|
|
194
|
+
if (usedKeys.has(config.parameters.key)) {
|
|
195
|
+
console.warn(`Duplicate key: ${JSON.stringify(config.parameters.key)}. Not allowed, as this will break things! We are unduplicating the key.`);
|
|
196
|
+
config.parameters.key += `-${Math.random().toString().slice(2)}`;
|
|
197
|
+
}
|
|
198
|
+
usedKeys.add(config.parameters.key);
|
|
199
|
+
|
|
193
200
|
let serviceId = config.serviceId;
|
|
194
201
|
config.info.lastUpdatedTime = Date.now();
|
|
195
202
|
let serviceConfig = await serviceConfigs.get(serviceId);
|
|
@@ -611,12 +611,12 @@ export class FastArchiveViewer<T> extends qreact.Component<{
|
|
|
611
611
|
{!this.state.finished && <LoaderAurora />}
|
|
612
612
|
{this.limitedScanCount > 0 && (
|
|
613
613
|
<div className={infoDisplay(60).boldStyle}>
|
|
614
|
-
{formatNumber(this.limitedScanCount)} scanned logs were rate limited. We do not track how many were ignored, there might be infinite missing logs.
|
|
614
|
+
{formatNumber(this.limitedScanCount)} scanned logs were rate limited at a file level. We do not track how many were ignored, there might be infinite missing logs.
|
|
615
615
|
</div>
|
|
616
616
|
)}
|
|
617
617
|
{this.limitedMatchCount > 0 && (
|
|
618
618
|
<div className={infoDisplay(0).boldStyle}>
|
|
619
|
-
{formatNumber(this.limitedMatchCount)} matched logs were rate limited. We do not track how many were ignored, there might be infinite missing logs.
|
|
619
|
+
{formatNumber(this.limitedMatchCount)} matched logs were rate limited at a file level. We do not track how many were ignored, there might be infinite missing logs.
|
|
620
620
|
</div>
|
|
621
621
|
)}
|
|
622
622
|
{this.state.pendingSyncInitializations > 0 && (
|
|
@@ -95,24 +95,7 @@ export class ErrorSuppressionUI extends qreact.Component<{
|
|
|
95
95
|
{this.state.matchedInput.trim() && <span className={css.opacity(0.6).fontSize(12).flexShrink0}>
|
|
96
96
|
matches {formatNumber(previewMatchCount)} (in top 1000)
|
|
97
97
|
</span>}
|
|
98
|
-
|
|
99
|
-
let value = this.state.matchedInput;
|
|
100
|
-
this.state.matchedInput = "";
|
|
101
|
-
void Querysub.onCommitFinished(async () => {
|
|
102
|
-
await controller.setSuppressionEntry.promise({
|
|
103
|
-
key: nextId(),
|
|
104
|
-
match: value,
|
|
105
|
-
comment: "",
|
|
106
|
-
lastUpdateTime: Date.now(),
|
|
107
|
-
expiresAt: NOT_AN_ERROR_EXPIRE_TIME,
|
|
108
|
-
});
|
|
109
|
-
Querysub.commit(() => {
|
|
110
|
-
this.props.rerunFilters();
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
}}>
|
|
114
|
-
Not an error
|
|
115
|
-
</Button>
|
|
98
|
+
|
|
116
99
|
<Button
|
|
117
100
|
onClick={() => {
|
|
118
101
|
let value = this.state.matchedInput;
|
|
@@ -134,6 +117,24 @@ export class ErrorSuppressionUI extends qreact.Component<{
|
|
|
134
117
|
>
|
|
135
118
|
Fixed
|
|
136
119
|
</Button>
|
|
120
|
+
<Button onClick={() => {
|
|
121
|
+
let value = this.state.matchedInput;
|
|
122
|
+
this.state.matchedInput = "";
|
|
123
|
+
void Querysub.onCommitFinished(async () => {
|
|
124
|
+
await controller.setSuppressionEntry.promise({
|
|
125
|
+
key: nextId(),
|
|
126
|
+
match: value,
|
|
127
|
+
comment: "",
|
|
128
|
+
lastUpdateTime: Date.now(),
|
|
129
|
+
expiresAt: NOT_AN_ERROR_EXPIRE_TIME,
|
|
130
|
+
});
|
|
131
|
+
Querysub.commit(() => {
|
|
132
|
+
this.props.rerunFilters();
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
}}>
|
|
136
|
+
Not an error
|
|
137
|
+
</Button>
|
|
137
138
|
</div>
|
|
138
139
|
|
|
139
140
|
<div className={css.vbox(8).fillWidth.overflowAuto.maxHeight("30vh")}>
|
|
@@ -4,12 +4,16 @@ Very small amount of data
|
|
|
4
4
|
https://127-0-0-1.querysubtest.com:7007/?hot&enableLogs&page=login&filter=%22431%22&showingmanagement&endTime=1755140880000&startTime=1754950020000&managementpage=LogViewer2
|
|
5
5
|
|
|
6
6
|
|
|
7
|
+
4) WTF... our http server is trying to run the queue. But we told it not to... Ugh...
|
|
7
8
|
|
|
8
9
|
5) Update all services, and move them to that machine
|
|
9
10
|
5) Verify the hezner server can run the site well
|
|
10
11
|
6) Take down our digital ocean server
|
|
11
12
|
7) Destroy our digital ocean server
|
|
12
13
|
|
|
14
|
+
9) Add a filter to JUST see rate limited logs (by clicking on the button. which just searches for the special text)
|
|
15
|
+
- say "click here to view rate limited logs"
|
|
16
|
+
|
|
13
17
|
10) "Started Listening" isn't being logged?
|
|
14
18
|
- https://127-0-0-1.querysubtest.com:7007/?enableLogs&page=login&showingmanagement&endTime=1757835685102.667&managementpage=LogViewer2&machineview=service-detail&startTime=1757745685102.667&serviceId=service-1756340309836&filter=__machineId%20%3D%20a794fbcf7b104c68%20%26%20Edge
|
|
15
19
|
- Or... maybe logs are lost SOMETIMES, and ALWAYS when we kill the server? Although... that would mean we have multiple issues. Ugh...
|