querysub 0.193.0 → 0.195.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.193.0",
3
+ "version": "0.195.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",
@@ -20,7 +20,7 @@ import { isDefined } from "../misc";
20
20
  import { diskLog } from "../diagnostics/logs/diskLogger";
21
21
  import { getBootedEdgeNode } from "../-0-hooks/hooks";
22
22
 
23
- let HEARTBEAT_INTERVAL = timeInMinute * 2;
23
+ let HEARTBEAT_INTERVAL = timeInMinute * 15;
24
24
  // Interval which we check other heartbeats
25
25
  let CHECK_INTERVAL = HEARTBEAT_INTERVAL;
26
26
  // If the heartbeat is older than thing, it fails the dead check
@@ -30,19 +30,19 @@ let DEAD_THRESHOLD = HEARTBEAT_INTERVAL * 2;
30
30
  let DEAD_CHECK_COUNT = 4;
31
31
  // If we find we are unable to write our heartbeat, we have to kill our own process. Otherwise it may
32
32
  // be too out of sync, and might commit unverified data to the disk.
33
- let SUICIDE_HEARTBEAT_THRESHOLD = timeInMinute * 15;
33
+ let SUICIDE_HEARTBEAT_THRESHOLD = HEARTBEAT_INTERVAL * 4;
34
34
 
35
- let CLIENTSIDE_POLL_RATE = timeInMinute * 5;
35
+ let CLIENTSIDE_POLL_RATE = timeInMinute * 30;
36
36
 
37
37
  // We can't poll backblaze too often. One a minute starts to cost around 10 cents per month.
38
38
  // So once a second would cost 6 USD per month, per service... which starts to get expensive.
39
- let DISK_AUDIT_RATE = timeInMinute * 5;
39
+ let DISK_AUDIT_RATE = timeInMinute * 15;
40
40
  // We CAN poll our API frequently. The overhead for calls should be far less than 1ms, and
41
41
  // the bandwidth should also be effectively nothing. Maybe 2.5GB per month if we send a
42
42
  // request per second, and each request is 1000 bytes (which as we use websockets, it
43
- // probably is less than that). Which is around 2.5 cents on backblaze IF we go over
43
+ // probably is less than that). Which is around 2.5 cents on digital ocean IF we go over
44
44
  // our 1TB/month allowance.
45
- let API_AUDIT_RATE = timeInSecond * 15;
45
+ let API_AUDIT_RATE = timeInSecond * 30;
46
46
  let API_AUDIT_COUNT = 12;
47
47
 
48
48
 
@@ -392,7 +392,7 @@ async function runMainSyncLoops(discoveryReady: PromiseObj<void>) {
392
392
 
393
393
  console.log(magenta(`Node discovery is loaded`));
394
394
 
395
- await runInfinitePoll(HEARTBEAT_INTERVAL, async function nodeDiscoverHeartbeat() {
395
+ await runInfinitePollCallAtStart(HEARTBEAT_INTERVAL, async function nodeDiscoverHeartbeat() {
396
396
  // If we waited too long, other nodes might think we are dead. In which case, we SHOULD terminate.
397
397
  if (!isNoNetwork()) {
398
398
  // FIRST, verify we didn't delay too long (to make sure we kill any nodes that were disconnected
@@ -759,9 +759,11 @@ export class PathValueProxyWatcher {
759
759
  ) {
760
760
  if (!watcher.pendingWrites.has(pathStr)) {
761
761
  let parentPathStr = pathStr;
762
+ let isLocal = pathStr.startsWith(LOCAL_DOMAIN_PATH);
763
+ let depthToData = isLocal ? 1 : DEPTH_TO_DATA;
762
764
  while (true) {
763
765
  parentPathStr = getParentPathStr(parentPathStr);
764
- if (getPathDepth(parentPathStr) < DEPTH_TO_DATA) break;
766
+ if (getPathDepth(parentPathStr) < depthToData) break;
765
767
  // We don't need to check all parents, as if any value is set, all parents will
766
768
  // likely have their specialObjectWriteValue set, due to a previous run of this function!
767
769
  if (watcher.pendingWrites.has(parentPathStr)) break;
@@ -26,7 +26,7 @@ import { onEdgeNodesChanged } from "./edgeBootstrap";
26
26
  import { startEdgeNotifier } from "./edgeClientWatcher";
27
27
  import { getGitRefLive, getGitURLLive } from "./git";
28
28
 
29
- const UPDATE_POLL_INTERVAL = timeInMinute;
29
+ const UPDATE_POLL_INTERVAL = timeInMinute * 15;
30
30
  const DEAD_NODE_COUNT_THRESHOLD = 15;
31
31
 
32
32
  const edgeNodeStorage = isNodeTrue() && nestArchives("edgenodes/", getArchivesBackblazePublic(getDomain()));
@@ -2,14 +2,16 @@ import { SocketFunction } from "socket-function/SocketFunction";
2
2
  import { qreact } from "../../src/4-dom/qreact";
3
3
  import { DeployController } from "./deploySchema";
4
4
  import { css } from "typesafecss";
5
+ import { syncedIsAnyLoading } from "../library-components/SyncedController";
5
6
 
6
7
  export class DeployPage extends qreact.Component {
7
8
  render() {
8
9
  let controller = DeployController(SocketFunction.browserNodeId());
10
+ let allInfo = controller.getAllInfo();
9
11
  return <div className={css.vbox(10)}>
10
12
  <h1>DeployPage</h1>
11
13
  <div className={css.whiteSpace("pre-wrap")}>
12
- {JSON.stringify(controller.getAllInfo(), null, 4)}
14
+ {JSON.stringify(allInfo, null, 4)}
13
15
  </div>
14
16
  </div>;
15
17
  }
@@ -18,20 +20,6 @@ export class DeployPage extends qreact.Component {
18
20
 
19
21
  todonext
20
22
 
21
- # Domain config file
22
- - Have the domain configured via a file in the repo, so we don't have to set it on every single script
23
- - This also makes it possible to use binary scripts directly in querysub without having to add a script in our package.json to set the domain parameter
24
- - FORWARD the main scripts
25
- function
26
- function-public
27
- server
28
- server-public
29
- deploy
30
- ... anything that uses node_modules, which is a lot...
31
- - And remove all --domain arguments (which is a lot as well)
32
- - ALSO, support setting arbitrary config parameter in this config file?
33
- - Maybe on a per script basis, giving scripts names.
34
-
35
23
  5) synced controller loading indicator
36
24
  - Probably which wataches all controllers (on all nodes), for any pending, shown globally
37
25
  6) Validate that our loading indicator work, as well as our notifications on changed configuration, etc
@@ -28,7 +28,7 @@ const getLiveMachineInfo = measureWrap(async function getLiveMachineInfo() {
28
28
  machineInfo.info.lscpu = await errorToUndefinedSilent(runPromise("lscpu")) || "";
29
29
  machineInfo.info.id = await errorToUndefinedSilent(runPromise("id")) || await errorToUndefinedSilent(runPromise("whoami")) || "";
30
30
 
31
- // TODO: Populate services via checking tmux for special keywords ("-dply", probably...)
31
+ // TODO: Populate services via checking tmux for special keywords ("-apply", probably...)
32
32
 
33
33
  return machineInfo;
34
34
  });
@@ -38,20 +38,11 @@ const updateMachineInfo = measureWrap(async function updateMachineInfo() {
38
38
  await new DeployControllerBase().setMachineInfo(machineInfo.machineId, machineInfo);
39
39
  });
40
40
 
41
- //todonext
42
- // The child process will be run with shell, and then we'll watch it (don't use runPromise)
43
-
44
- function getTotalTime(module: NodeJS.Module, now: number) {
45
- return (module.evalEndTime || now) - (module.evalStartTime || now);
46
- }
47
41
 
48
42
 
49
43
  export async function machineApplyMain() {
50
- console.log(getDomain());
51
- process.exit();
52
- // await Querysub.hostService("machine-apply");
53
-
54
- // await updateMachineInfo();
44
+ await Querysub.hostService("machine-apply");
45
+ await updateMachineInfo();
55
46
  }
56
47
 
57
48
  machineApplyMain().catch(console.error);
@@ -341,7 +341,6 @@ class ManagementRoot extends qreact.Component {
341
341
  </div>
342
342
  }
343
343
  </div>
344
-
345
344
  </>
346
345
  );
347
346
  }
@@ -1,9 +1,10 @@
1
- import { atomic, atomicObjectWrite, atomicObjectWriteNoFreeze, doAtomicWrites, proxyWatcher } from "../../src/2-proxy/PathValueProxyWatcher";
1
+ import { atomic, atomicObjectWrite, atomicObjectWriteNoFreeze, doAtomicWrites, isTransparentValue, proxyWatcher, specialObjectWriteValue } from "../../src/2-proxy/PathValueProxyWatcher";
2
2
  import { Querysub } from "../../src/4-querysub/Querysub";
3
3
  import { SocketFunction } from "socket-function/SocketFunction";
4
4
  import { FullCallType, SocketRegistered } from "socket-function/SocketFunctionTypes";
5
5
  import { onHotReload } from "socket-function/hot/HotReloadController";
6
6
  import { cache } from "socket-function/src/caching";
7
+ import { formatTime } from "socket-function/src/formatting/format";
7
8
  import { nextId } from "socket-function/src/misc";
8
9
  import { getCallObj } from "socket-function/src/nodeProxy";
9
10
  import { MaybePromise } from "socket-function/src/types";
@@ -31,6 +32,22 @@ onHotReload(() => {
31
32
  });
32
33
  });
33
34
 
35
+ export function syncedIsAnyLoading() {
36
+ return Querysub.fastRead(() => {
37
+ for (let controllerId in syncedData()) {
38
+ for (let nodeId in syncedData()[controllerId]) {
39
+ for (let fnc in syncedData()[controllerId][nodeId]) {
40
+ for (let argsHash in syncedData()[controllerId][nodeId][fnc]) {
41
+ if (atomic(syncedData()[controllerId][nodeId][fnc][argsHash].promise)) {
42
+ return true;
43
+ }
44
+ }
45
+ }
46
+ }
47
+ }
48
+ });
49
+ }
50
+
34
51
  let syncedData = Querysub.createLocalSchema<{
35
52
  [controllerId: string]: {
36
53
  [nodeId: string]: {
@@ -166,12 +183,20 @@ export function getSyncedController<T extends SocketRegistered>(
166
183
 
167
184
  // NOTE: If we are invalidated when the promise is running, nothing happens (as we don't watch invalidated if we are running with a promise). BUT, if the promise isn't running, we will run again, and start running again. In this way we don't queue up a lot if we invalidate a lot, but we do always run again after the invalidation to get the latest result!
168
185
  if (!promise && (!result || atomic(obj.invalidated))) {
186
+ let timeStart = Date.now();
187
+ function logFinished() {
188
+ let duration = Date.now() - timeStart;
189
+ if (duration > 500) {
190
+ console.warn(`Slow call ${fncName} took ${formatTime(duration)}`);
191
+ }
192
+ }
169
193
  let promise = controller.nodes[nodeId][fncName](...args) as Promise<unknown>;
170
194
  doAtomicWrites(() => {
171
195
  obj.promise = promise;
172
196
  });
173
197
  promise.then(
174
198
  result => {
199
+ logFinished();
175
200
  Querysub.commitLocal(() => {
176
201
  obj.result = atomicObjectWriteNoFreeze({ result });
177
202
  obj.promise = undefined;
@@ -192,6 +217,7 @@ export function getSyncedController<T extends SocketRegistered>(
192
217
  });
193
218
  },
194
219
  error => {
220
+ logFinished();
195
221
  Querysub.commitLocal(() => {
196
222
  obj.result = atomicObjectWriteNoFreeze({ error });
197
223
  obj.promise = undefined;
@@ -0,0 +1,50 @@
1
+ import { css } from "typesafecss";
2
+ import { qreact } from "../4-dom/qreact";
3
+ import { syncedIsAnyLoading } from "./SyncedController";
4
+ import { formatTime } from "socket-function/src/formatting/format";
5
+
6
+ export class SyncedControllerLoadingIndicator extends qreact.Component {
7
+ timeLastLoaded = Date.now();
8
+ lastWasLoaded = false;
9
+ render() {
10
+ if (!syncedIsAnyLoading()) {
11
+ if (!this.lastWasLoaded) {
12
+ console.log(`Loaded all SyncedController calls in ${formatTime(Date.now() - this.timeLastLoaded)}`);
13
+ }
14
+ this.timeLastLoaded = Date.now();
15
+ this.lastWasLoaded = true;
16
+ return undefined;
17
+ }
18
+ this.lastWasLoaded = false;
19
+ const spinAnimationClass = `SyncedControllerLoadingIndicator-spin`;
20
+
21
+ return <div className={
22
+ css.position("fixed").bottom(0).left(0).zIndex(1000)
23
+ .padding("12px 16px")
24
+ .background("rgba(0, 0, 0, 0.8)")
25
+ .color("white")
26
+ .borderTopRightRadius(8)
27
+ .hbox(8)
28
+ }>
29
+ <div className={
30
+ css.width("16px").height("16px")
31
+ .border("2px solid rgba(255, 255, 255, 0.3)")
32
+ .borderTop("2px solid white")
33
+ .borderRadius("50%")
34
+ + " " + spinAnimationClass
35
+ }></div>
36
+ <span>Syncing data...</span>
37
+ <style>
38
+ {`
39
+ @keyframes ${spinAnimationClass} {
40
+ 0% { transform: rotate(0deg); }
41
+ 100% { transform: rotate(360deg); }
42
+ }
43
+ .${spinAnimationClass} {
44
+ animation: ${spinAnimationClass} 1s linear infinite;
45
+ }
46
+ `}
47
+ </style>
48
+ </div>;
49
+ }
50
+ }