querysub 0.318.0 → 0.320.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "querysub",
3
- "version": "0.318.0",
3
+ "version": "0.320.0",
4
4
  "main": "index.js",
5
5
  "license": "MIT",
6
6
  "note1": "note on node-forge fork, see https://github.com/digitalbazaar/forge/issues/744 for details",
@@ -394,7 +394,11 @@ class TransactionLocker {
394
394
  let pastTime = Date.now() - ARCHIVE_PROPAGATION_TIME * 10;
395
395
  let veryBadFiles = newFiles.filter(x => x.createTime < pastTime);
396
396
  if (veryBadFiles.length > 0) {
397
- console.error(`Old files suddenly appeared. This isn't possible, if they are old, they should have appeared when they were created! This likely means that our getKeys() failed to actually read all of the files. This is bad and can result in us deleting seemingly broken files for missing a confirmation, when they in fact had a confirmation.`, { files: veryBadFiles.map(x => x.file) });
397
+ console.error(`Old files suddenly appeared. This isn't possible, if they are old, they should have appeared when they were created! This likely means that our getKeys() failed to actually read all of the files. This is bad and can result in us deleting seemingly broken files for missing a confirmation, when they in fact had a confirmation.`, {
398
+ newlyAppearingOldFiles: veryBadFiles.map(x => x.file),
399
+ prevFiles: this.lastFilesRead.map(x => x.file),
400
+ newFiles: files.map(x => x.file),
401
+ });
398
402
  }
399
403
  }
400
404
  this.lastFilesRead = files;
@@ -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
- await loadEntryPointsByHash({
127
- host,
128
- entryPaths: config.entryPaths,
129
- hash,
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) {
@@ -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>{config.parameters.key}</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
- <Button onClick={() => {
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...