querysub 0.404.0 → 0.406.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/join.js CHANGED
@@ -1,4 +1,4 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  require("typenode");
4
- require("../src/archiveapps/archiveGCEntry");
4
+ require("../src/archiveapps/archiveJoinEntry");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "querysub",
3
- "version": "0.404.0",
3
+ "version": "0.406.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",
@@ -1,5 +1,5 @@
1
1
  import { sort } from "socket-function/src/misc";
2
- import { getLastPathPart, getPathDepth, getPathIndex, getPathStr1 } from "../path";
2
+ import { getLastPathPart, getPathDepth, getPathIndex, getPathStr1, hack_stripPackedPath } from "../path";
3
3
  import { AuthorityEntry, authorityLookup } from "./AuthorityLookup";
4
4
  import { PathValue } from "./pathValueCore";
5
5
  import { shuffle } from "../misc/random";
@@ -32,10 +32,7 @@ export type AuthoritySpec = {
32
32
  routeEnd: number;
33
33
  // If the path.startsWith(prefix), but prefix !== path, then we hash getPathIndex(path, hashIndex)
34
34
  // - For now, let's just never add overlapping prefixes.
35
- prefixes: {
36
- prefix: string;
37
- hashIndex: number;
38
- }[];
35
+ prefixes: string[];
39
36
  excludeDefault?: boolean;
40
37
  };
41
38
 
@@ -59,18 +56,21 @@ export class PathRouter {
59
56
  path: string;
60
57
  spec: AuthoritySpec;
61
58
  }): number {
62
- // NOTE: getSelfPathIdentifierTargets also hardcodes this logic, so it can hash many values quickly
59
+ // NOTE: getPathIdentifierTargets also hardcodes this logic, so it can hash many values quickly
63
60
  let { path, spec } = config;
61
+ path = hack_stripPackedPath(path);
64
62
  let override = getRoutingOverride(path);
65
63
  if (override) {
66
64
  if (!hasPrefixHash({ spec, prefixHash: override.prefixHash })) return -1;
67
65
  return override.route;
68
66
  }
69
67
 
70
- let prefix = spec.prefixes.find(x => path.startsWith(x.prefix) && x.prefix !== path);
68
+ let prefix = spec.prefixes.find(x => path.startsWith(x) && x !== path);
71
69
  if (prefix) {
72
- let key = getPathIndex(path, prefix.hashIndex);
73
- if (key === undefined) throw new Error(`Impossible, hash index ${prefix.hashIndex} is out of range for path ${path}, but it matched the prefix ${prefix.prefix}`);
70
+ let key = getPathIndex(path, getPathDepth(prefix));
71
+ if (key === undefined) {
72
+ throw new Error(`Impossible, hash index ${getPathDepth(prefix)} is out of range for path ${path}, but it matched the prefix ${prefix}`);
73
+ }
74
74
  return this.getSingleKeyRoute(key);
75
75
  }
76
76
  if (spec.excludeDefault) return -1;
@@ -107,13 +107,21 @@ export class PathRouter {
107
107
  private static encodeIdentifier(config: { prefixes: string[]; rangeStart: number; rangeEnd: number } | "remaining"): string {
108
108
  if (config === "remaining") return "P!REMAINING";
109
109
  let { prefixes, rangeStart, rangeEnd } = config;
110
- return ["P", rangeStart.toString(), rangeEnd.toString(), ...prefixes.map(x => this.getPrefixHash(x))].join("!");
110
+ return [
111
+ "P",
112
+ rangeStart.toString(),
113
+ rangeEnd.toString(),
114
+ ...prefixes.map(x => this.getPrefixHash(x)),
115
+ // Add some prefixes. They'll be interpreted as hashes, but it's fine because it's highly unlikely they'll collide with a real hash. AND... It's very useful for debugging what's in which file.
116
+ ...prefixes.slice(0, 5).map(x => getLastPathPart(x).replaceAll(/[^a-zA-Z0-9]/g, "").slice(0, 20)),
117
+ ""
118
+ ].join("!");
111
119
 
112
120
  }
113
121
  private static decodeIdentifier(identifier: string): { prefixHashes: string[]; rangeStart: number; rangeEnd: number } | "remaining" {
114
122
  if (!identifier.startsWith("P")) return "remaining";
115
123
  let parts = identifier.split("!");
116
- if (parts[1] === "REMAINING") return "remaining";
124
+ if (parts[1].startsWith("REMAINING")) return "remaining";
117
125
  return {
118
126
  rangeStart: parseFloat(parts[1]),
119
127
  rangeEnd: parseFloat(parts[2]),
@@ -125,7 +133,7 @@ export class PathRouter {
125
133
  // NOTE: Encodes all the data, even if it matches our spec or not. If you don't want this, you have to filter first
126
134
  // - If this becomes an issue we COULD filter, as we can do it quickly, but I don't think it is required, as all the present usecases prefilter anyways.
127
135
  @measureFnc
128
- public static getSelfPathIdentifierTargets(values: PathValue[]): Map<string, PathValue[]> {
136
+ public static getPathIdentifierTargets(values: PathValue[], ourSpec: AuthoritySpec): Map<string, PathValue[]> {
129
137
  // NOTE: The file size limit is 1024 bytes. But we also have our folder, etc, so we want to add enough buffer
130
138
  // - Shorter hashes means we can store more, but there's a point when the collisions make it less useful.
131
139
  const MAX_PREFIXES_PER_FILE = 50;
@@ -135,13 +143,11 @@ export class PathRouter {
135
143
  return new Map([[this.encodeIdentifier("remaining"), values]]);
136
144
  }
137
145
 
138
- let ourSpec = authorityLookup.getOurSpec();
139
-
140
146
  let prefixes = ourSpec.prefixes.slice();
141
- sort(prefixes, x => x.prefix.length);
147
+ sort(prefixes, x => x.length);
142
148
  function getPrefix(path: string): string | undefined {
143
149
  for (let prefix of prefixes) {
144
- if (path.startsWith(prefix.prefix)) return prefix.prefix;
150
+ if (path.startsWith(prefix) && prefix !== path) return prefix;
145
151
  }
146
152
  return undefined;
147
153
  }
@@ -216,7 +222,9 @@ export class PathRouter {
216
222
  let hashIndex = getPathDepth(prefix);
217
223
  for (let value of values) {
218
224
  let key = getPathIndex(value.path, hashIndex);
219
- if (key === undefined) throw new Error(`Impossible, hash index ${hashIndex} is out of range for path ${value.path}, but it matched the prefix ${prefix}`);
225
+ if (key === undefined) {
226
+ throw new Error(`Impossible, hash index ${hashIndex} is out of range for path ${value.path}, but it matched the prefix ${prefix}`);
227
+ }
220
228
  let route = this.getSingleKeyRoute(key);
221
229
  let routeIndex = Math.floor(route * splitCount);
222
230
  let routeValues = byRouteGroup.get(routeIndex);
@@ -253,7 +261,7 @@ export class PathRouter {
253
261
  // Match all remaining, as we don't store enough information to know what prefixes they excluded
254
262
  if (decodeObj === "remaining") return true;
255
263
 
256
- let ourHashes = authority.prefixes.map(x => this.getPrefixHash(x.prefix));
264
+ let ourHashes = authority.prefixes.map(x => this.getPrefixHash(x));
257
265
  // If it pulled off some values as prefixes, but we did full path hashes, then the hashes are going to be totally different. And so there could easily be overlap.
258
266
  if (decodeObj.prefixHashes.some(x => !ourHashes.includes(x))) return true;
259
267
  // However, if we hash everything the same, then the overlap is purely a case of if the route ranges overlap.
@@ -297,12 +305,12 @@ export class PathRouter {
297
305
 
298
306
  let groupByPrefixHash = new Map<string, AuthoritySpec[]>();
299
307
  for (let source of allSources) {
300
- let usedPrefixes = source.authoritySpec.prefixes.map(x => x.prefix);
308
+ let usedPrefixes = source.authoritySpec.prefixes;
301
309
 
302
310
  if (target.excludeDefault) {
303
311
  // This relaxes the restrictions, as with no default hashes, it means if a value isn't in one of our prefixes, even if it's inconsistent in the sources, it's fine.
304
312
  // (And... Otherwise, we can't filter these at all because otherwise the default/remaining group will be different, even if it's a superset/subset relationship)
305
- let targetUsedPrefixes = new Set(target.prefixes.map(x => x.prefix));
313
+ let targetUsedPrefixes = new Set(target.prefixes);
306
314
  usedPrefixes = usedPrefixes.filter(x => targetUsedPrefixes.has(x));
307
315
  }
308
316
  let prefixHash = sha256(JSON.stringify(usedPrefixes.sort()));
@@ -321,17 +329,17 @@ export class PathRouter {
321
329
  }
322
330
 
323
331
  function hashesSameAsTarget(spec: AuthoritySpec): boolean {
324
- let targetUsedPrefixes = new Set(target.prefixes.map(x => x.prefix));
332
+ let targetUsedPrefixes = new Set(target.prefixes);
325
333
  if (target.excludeDefault) {
326
334
  // If target is excluding default, then it doesn't matter if the spec is including it. We're only looking for matching values in target.
327
- return spec.prefixes.every(x => targetUsedPrefixes.has(x.prefix));
335
+ return spec.prefixes.every(x => targetUsedPrefixes.has(x));
328
336
  }
329
337
  // if !targetExcludeDefault, then !spec.excludeDefault, because we filtered out other cases earlier (so we don't need to handle spec.
330
338
 
331
339
  if (spec.prefixes.length !== target.prefixes.length) return false;
332
340
  // Otherwise, All of the prefixes have to be identical
333
341
  for (let prefix of spec.prefixes) {
334
- if (!targetUsedPrefixes.has(prefix.prefix)) return false;
342
+ if (!targetUsedPrefixes.has(prefix)) return false;
335
343
  }
336
344
  return true;
337
345
  }
@@ -432,7 +440,7 @@ export class PathRouter {
432
440
  let allSources = authorityLookup.getTopologySync();
433
441
  allSources = allSources.filter(x => !isOwnNodeId(x.nodeId));
434
442
  let nestedMatches = allSources.filter(x =>
435
- x.authoritySpec.prefixes.some(y => path.startsWith(y.prefix) && y.prefix !== path)
443
+ x.authoritySpec.prefixes.some(y => path.startsWith(y) && y !== path)
436
444
  && this.matchesAuthoritySpec(x.authoritySpec, path)
437
445
  );
438
446
  if (nestedMatches.length > 0) {
@@ -451,10 +459,7 @@ export class PathRouter {
451
459
  let fullSources = this.getAuthoritySources({
452
460
  target: {
453
461
  nodeId: "",
454
- prefixes: [{
455
- prefix: path,
456
- hashIndex: getPathDepth(path),
457
- }],
462
+ prefixes: [path],
458
463
  routeStart: 0,
459
464
  routeEnd: 1,
460
465
  excludeDefault: true,
@@ -62,7 +62,7 @@ export const hasPrefixHash = cacheLimited(1000 * 10,
62
62
  (config: { spec: AuthoritySpec, prefixHash: string }) => {
63
63
  let { spec, prefixHash } = config;
64
64
  for (let prefix of spec.prefixes) {
65
- if (getPrefixHash(prefix.prefix) === prefixHash) {
65
+ if (getPrefixHash(prefix) === prefixHash) {
66
66
  return true;
67
67
  }
68
68
  }
@@ -16,7 +16,12 @@ export async function getOurAuthoritySpec(defaultToAll?: boolean): Promise<Autho
16
16
 
17
17
  if (!range) {
18
18
  if (defaultToAll) {
19
- return getAllAuthoritySpec();
19
+ return {
20
+ nodeId: "",
21
+ routeStart: 0,
22
+ routeEnd: 1,
23
+ prefixes: prefixes,
24
+ };
20
25
  }
21
26
  return undefined;
22
27
  }
@@ -38,10 +43,7 @@ export async function getOurAuthoritySpec(defaultToAll?: boolean): Promise<Autho
38
43
  nodeId: "",
39
44
  routeStart: rangeStart,
40
45
  routeEnd: rangeEnd,
41
- prefixes: usePrefixes.map(prefix => ({
42
- prefix,
43
- hashIndex: getPathDepth(prefix),
44
- })),
46
+ prefixes: usePrefixes,
45
47
  excludeDefault: excludeDefault || undefined,
46
48
  };
47
49
  }
@@ -20,7 +20,7 @@ let isPrefix = cache((path: string) => {
20
20
  let spec = authorityLookup.getOurSpec();
21
21
  if (!spec) return false;
22
22
  for (let prefix of spec.prefixes) {
23
- if (path.startsWith(prefix.prefix)) return true;
23
+ if (path.startsWith(prefix)) return true;
24
24
  }
25
25
  return false;
26
26
  });
@@ -13,6 +13,7 @@ import { createArchiveLocker2 } from "./archiveLocks/ArchiveLocks2";
13
13
  import { devDebugbreak, isNoNetwork } from "../config";
14
14
  import { wrapArchivesWithCache } from "../-a-archives/archiveCache";
15
15
  import { AuthoritySpec, PathRouter } from "./PathRouter";
16
+ import { authorityLookup } from "./AuthorityLookup";
16
17
 
17
18
  export const archives = lazy(() => wrapArchivesWithCache(getArchives("path-values/")));
18
19
  export const archivesLocks = lazy(() => getArchives("path-values-locks/"));
@@ -161,7 +162,7 @@ export class PathValueArchives {
161
162
 
162
163
  @measureFnc
163
164
  public async archiveValues(values: PathValue[]) {
164
- let parts = PathRouter.getSelfPathIdentifierTargets(values);
165
+ let parts = PathRouter.getPathIdentifierTargets(values, authorityLookup.getOurSpec());
165
166
 
166
167
  for (let [pathIdentifier, values] of parts) {
167
168
  let encodedObj = await this.encodeValuePaths(values);
@@ -7,7 +7,7 @@ import { PathValue, VALUE_GC_THRESHOLD, FILE_VALUE_COUNT_LIMIT, FILE_SIZE_LIMIT,
7
7
  import { getSingleSizeEstimate } from "../5-diagnostics/shared";
8
8
  import { logNodeStats } from "../-0-hooks/hooks";
9
9
  import debugbreak from "debugbreak";
10
- import { AuthorityEntry } from "../0-path-value-core/AuthorityLookup";
10
+ import { AuthorityEntry, authorityLookup } from "../0-path-value-core/AuthorityLookup";
11
11
  import { AuthoritySpec, PathRouter } from "../0-path-value-core/PathRouter";
12
12
  import { getOurAuthoritySpec } from "../0-path-value-core/PathRouterServerAuthoritySpec";
13
13
 
@@ -249,7 +249,7 @@ export async function runArchiveMover(config: {
249
249
 
250
250
 
251
251
  for (let [key, values] of Object.entries(result.newValues)) {
252
- let targets = PathRouter.getSelfPathIdentifierTargets(values);
252
+ let targets = PathRouter.getPathIdentifierTargets(values, authority);
253
253
  for (let [target, values] of targets) {
254
254
  await addValues(target + "/", values, key);
255
255
  }
@@ -19,6 +19,7 @@ import { getStorageDir, getSubFolder } from "../fs";
19
19
  import fs from "fs";
20
20
  import { sha256 } from "js-sha256";
21
21
  import { AuthoritySpec, PathRouter } from "../0-path-value-core/PathRouter";
22
+ import { pathValueSerializer } from "../-h-path-value-serialize/PathValueSerializer";
22
23
 
23
24
 
24
25
  export interface AliveChecker<T = unknown> {
@@ -34,7 +35,7 @@ export interface AliveChecker<T = unknown> {
34
35
  * NOTE: The value might only have a single heavily nested value, not being a valid object. If the object
35
36
  * lookups broken, probably just return false, to clean up the straggling values.
36
37
  */
37
- isAlive: (value: T, path: string[]) => boolean | unknown;
38
+ isAlive: (value: T, path: string[], valuePath: PathValue | undefined) => boolean | unknown;
38
39
 
39
40
  /** Alias for lockBasedDelete & unsafeLiveReads */
40
41
  lock?: boolean;
@@ -254,7 +255,7 @@ function sandboxedIsAlive(config: {
254
255
  proxy = proxy[part];
255
256
  }
256
257
  try {
257
- return !!atomicRaw(checker.isAlive(proxy, path));
258
+ return !!atomicRaw(checker.isAlive(proxy, path, undefined));
258
259
  } catch {
259
260
  config.errors.count++;
260
261
  return true;
@@ -308,7 +309,7 @@ function sandboxedGetLocks(config: {
308
309
 
309
310
  let evalResult = getWatchEvaluator()(() => {
310
311
  let value = proxyWatcher.getCallbackPathValue(pathStr);
311
- return !!atomic(checker.isAlive(value, path));
312
+ return !!atomic(checker.isAlive(pathValueSerializer.getPathValue(value), path, value));
312
313
  }, { getReadLocks: true });
313
314
 
314
315
  return evalResult.readLocks!;
@@ -2,7 +2,7 @@ import fs from "fs";
2
2
  import { cache, lazy } from "socket-function/src/caching";
3
3
  import { getStringKeys, sort } from "socket-function/src/misc";
4
4
  import { Args } from "socket-function/src/types";
5
- import { appendToPathStr, getPathFromStr, getPathStr, rootPathStr } from "../path";
5
+ import { appendToPathStr, getPathFromStr, getPathStr, joinPathStres, rootPathStr } from "../path";
6
6
  import { writeFunctionCall } from "./PathFunctionHelpers";
7
7
  import { CallSpec, functionSchema } from "./PathFunctionRunner";
8
8
  import { getDomain, isLocal } from "../config";
@@ -323,6 +323,7 @@ export function syncSchema<Schema>(schema?: Schema2): SyncSchemaResult<Schema> {
323
323
  createObjectAliveChecker(data, path, gcDelay);
324
324
  }
325
325
 
326
+
326
327
  let undoChecks = Schema2Fncs.getAllUndoChecks(schema);
327
328
  for (let checkObj of undoChecks) {
328
329
  // Need at least one check
@@ -368,7 +369,10 @@ export function syncSchema<Schema>(schema?: Schema2): SyncSchemaResult<Schema> {
368
369
  }
369
370
 
370
371
  // Register all root lookups as prefixes.
371
- let paths = Schema2Fncs.getLookups(schema).map(x => getPathStr(x));
372
+ let dataRoot = getProxyPath(() => functionSchema()[getDomain()].PathFunctionRunner[moduleId].Data);
373
+ let paths = Schema2Fncs.getLookups(schema).map(x =>
374
+ joinPathStres(dataRoot, getPathStr(x))
375
+ );
372
376
  // NOTE: This will break with wildcards, but... we also take only the highest level lookups, so it shouldn't be an issue
373
377
  sort(paths, x => x.length);
374
378
  let rootLookups = new Set<string>();
@@ -16,6 +16,11 @@ import { lazy } from "socket-function/src/caching";
16
16
  import { Button } from "../library-components/Button";
17
17
  import { updateRootDiscoveryLocation } from "../-f-node-discovery/NodeDiscovery";
18
18
 
19
+ setImmediate(() => {
20
+ // Modal
21
+ import("../5-diagnostics/Modal");
22
+ });
23
+
19
24
  const SWITCH_SERVER_TIMEOUT = timeInSecond * 15;
20
25
  const MAX_DISPLAY_INTERVAL = timeInMinute * 5;
21
26
  const FINAL_DELAY = timeInSecond * 30;
@@ -2,23 +2,29 @@ import "../inject";
2
2
 
3
3
  import { logErrors } from "../errors";
4
4
  import { PathValueArchives, pathValueArchives } from "../0-path-value-core/pathValueArchives";
5
- import { FILE_SIZE_LIMIT, FILE_VALUE_COUNT_LIMIT, PathValue, VALUE_GC_THRESHOLD } from "../0-path-value-core/pathValueCore";
5
+ import { PathValue, VALUE_GC_THRESHOLD } from "../0-path-value-core/pathValueCore";
6
6
  import { runInfinitePollCallAtStart } from "socket-function/src/batching";
7
7
  import { measureBlock } from "socket-function/src/profiling/measure";
8
8
  import { pathValueSerializer } from "../-h-path-value-serialize/PathValueSerializer";
9
9
  import { ArchiveTransaction, FileInfo } from "../0-path-value-core/archiveLocks/ArchiveLocks";
10
10
  import { formatNumber } from "socket-function/src/formatting/format";
11
- import { sort } from "socket-function/src/misc";
11
+ import { isNodeTrue, sort } from "socket-function/src/misc";
12
12
  import { Querysub } from "../4-querysub/QuerysubController";
13
13
  import { magenta } from "socket-function/src/formatting/logColors";
14
14
  import { PathRouter } from "../0-path-value-core/PathRouter";
15
15
  import { disablePathAuditer } from "../diagnostics/pathAuditerCallback";
16
16
  import { getOurAuthoritySpec } from "../0-path-value-core/PathRouterServerAuthoritySpec";
17
+ import yargs from "yargs";
17
18
 
18
- async function runGenesisJoinIteration() {
19
+ let yargObj = isNodeTrue() && yargs(process.argv)
20
+ .option("watch", { type: "boolean", desc: "If true, we only join genesis values, but we join in a loop, otherwise we just join absolutely everything, and then exit." })
21
+ .argv || {}
22
+ ;
23
+
24
+
25
+ async function runGenesisJoinIteration(config?: { force?: boolean }) {
19
26
  let authoritySpec = await getOurAuthoritySpec(true);
20
27
  let locker = await pathValueArchives.getArchiveLocker();
21
- let maxAge = Date.now() - VALUE_GC_THRESHOLD;
22
28
 
23
29
  let readCache = new Map<string, Buffer>();
24
30
  let reread = true;
@@ -31,15 +37,16 @@ async function runGenesisJoinIteration() {
31
37
  // Merge the newest first, so if we hit a big file, we can just ignore it,
32
38
  // and next time merge the smaller files after it.
33
39
  sort(valueFiles, x => -x.createTime);
34
- valueFiles = valueFiles.filter(x => {
35
- let obj = pathValueArchives.decodeDataPath(x.file);
36
- if (!obj.minTime) return false;
37
- if (obj.sourceType !== "genesis") return false;
38
- return x.createTime >= maxAge;
39
- });
40
+ if (!config?.force) {
41
+ valueFiles = valueFiles.filter(x => {
42
+ let obj = pathValueArchives.decodeDataPath(x.file);
43
+ if (!obj.minTime) return false;
44
+ return obj.sourceType === "genesis";
45
+ });
46
+ }
40
47
  let withinTimeRangeCount = valueFiles.length;
41
48
 
42
- if (valueFiles.length < 3) return [];
49
+ if (valueFiles.length < 3 && !config?.force) return [];
43
50
 
44
51
  await measureBlock(async function locker_readFiles() {
45
52
  let remainingFiles = valueFiles.filter(x => !readCache.has(x.file));
@@ -65,28 +72,20 @@ async function runGenesisJoinIteration() {
65
72
  let allValues: PathValue[][] = [];
66
73
  for (let valueFile of valueFiles) {
67
74
  let buffer = readCache.get(valueFile.file)!;
68
- if (totalSize + buffer.length > FILE_SIZE_LIMIT) {
69
- break;
70
- }
71
75
  let newValues = await PathValueArchives.loadValuesFromBuffer({
72
76
  path: valueFile.file,
73
77
  data: buffer,
74
78
  });
75
79
 
76
80
  let readValues = newValues.values;
77
- if (readValues.length + totalCount > FILE_VALUE_COUNT_LIMIT) {
78
- break;
79
- }
80
81
  allValues.push(readValues);
81
82
  usedFiles.push(valueFile);
82
83
  totalSize += buffer.length;
83
84
  totalCount += readValues.length;
84
85
  }
85
- if (usedFiles.length <= 1) return [];
86
86
 
87
87
  let allCombinedValues = allValues.flat();
88
88
 
89
-
90
89
  let transaction: ArchiveTransaction = {
91
90
  createFiles: [],
92
91
  deleteFiles: [],
@@ -95,7 +94,7 @@ async function runGenesisJoinIteration() {
95
94
  transaction.deleteFiles.push(file);
96
95
  }
97
96
 
98
- let targets = PathRouter.getSelfPathIdentifierTargets(allCombinedValues);
97
+ let targets = PathRouter.getPathIdentifierTargets(allCombinedValues, authoritySpec);
99
98
  for (let [target, values] of targets) {
100
99
 
101
100
  let dataObj = await pathValueArchives.encodeValuePaths(values, {
@@ -123,7 +122,17 @@ async function main() {
123
122
  disablePathAuditer();
124
123
  await Querysub.hostService("join");
125
124
 
126
- await runInfinitePollCallAtStart(VALUE_GC_THRESHOLD * 0.8, runGenesisJoinIteration);
125
+ if (yargObj.watch) {
126
+ await runInfinitePollCallAtStart(VALUE_GC_THRESHOLD * 0.8, runGenesisJoinIteration);
127
+ } else {
128
+ try {
129
+ await runGenesisJoinIteration({ force: true });
130
+ } catch (error) {
131
+ console.error(error);
132
+ } finally {
133
+ process.exit();
134
+ }
135
+ }
127
136
  }
128
137
 
129
138
  main().catch(logErrors);
@@ -34,7 +34,10 @@ export function cacheAsyncLimited<Arg, Return>(limit: number, fnc: (arg: Arg) =>
34
34
  export const cacheAsyncSynced = cacheAsyncLimitedJSON;
35
35
  export function cacheAsyncLimitedJSON<Arg, Return>(
36
36
  limit: number,
37
- fnc: (arg: Arg) => Promise<Return>
37
+ fnc: (arg: Arg) => Promise<Return>,
38
+ config?: {
39
+ notReadyValue?: Return;
40
+ }
38
41
  ): ((arg: Arg) => Return | undefined) & {
39
42
  clear(): void;
40
43
  promise: (arg: Arg) => Promise<Return>;
@@ -71,6 +74,6 @@ export function cacheAsyncLimitedJSON<Arg, Return>(
71
74
  }
72
75
  );
73
76
  proxyWatcher.triggerOnPromiseFinish(promise, { waitReason: fnc.toString() });
74
- return undefined;
77
+ return config?.notReadyValue;
75
78
  };
76
79
  }
package/src/path.ts CHANGED
@@ -181,8 +181,12 @@ export function removePathLastPart(pathStr: string) {
181
181
  return pathStr.slice(0, getStartOfLastPart(pathStr));
182
182
  }
183
183
 
184
+ let lastPastPath = { path: "", depth: 0 };
184
185
  /** getPathDepth(pathStr) === getPathFromStr(pathStr).length */
185
186
  export function getPathDepth(pathStr: string): number {
187
+ if (lastPastPath.path === pathStr) {
188
+ return lastPastPath.depth;
189
+ }
186
190
  // Start at 1, to skip the first pathDelimit
187
191
  let index = 1;
188
192
  // Start at -1, as the last loop doesn't have pathDelimitEscaped, but is still incremented
@@ -191,6 +195,8 @@ export function getPathDepth(pathStr: string): number {
191
195
  count++;
192
196
  index = pathStr.indexOf(pathDelimitEscaped, index) + 1;
193
197
  }
198
+ lastPastPath.path = pathStr;
199
+ lastPastPath.depth = count;
194
200
  return count;
195
201
  }
196
202
 
package/tempnotes.txt CHANGED
@@ -12,8 +12,6 @@ BUGS:
12
12
 
13
13
 
14
14
 
15
- 4) deploy ON THE SITE, to deploy our prefixes
16
- - Do it via the site, to commit, publish, etc.
17
15
  3) gc, to split it into multiple directories
18
16
  4) Verify the site still works
19
17
 
package/test.ts CHANGED
@@ -3,6 +3,7 @@ chdir("D:/repos/qs-cyoa/");
3
3
 
4
4
  import "./inject";
5
5
 
6
+ import path from "path";
6
7
  import { list, sort } from "socket-function/src/misc";
7
8
  import { formatNumber } from "socket-function/src/formatting/format";
8
9
  import { PathValueArchives, archives, archivesLocks, archivesRecycleBin, pathValueArchives } from "./src/0-path-value-core/pathValueArchives";
@@ -19,26 +20,29 @@ import { unique } from "./src/misc";
19
20
  import { getAllAuthoritySpec, getOurAuthoritySpec } from "./src/0-path-value-core/PathRouterServerAuthoritySpec";
20
21
  import { PathRouter } from "./src/0-path-value-core/PathRouter";
21
22
  import { pathValueSerializer } from "./src/-h-path-value-serialize/PathValueSerializer";
23
+ import { getShardPrefixes } from "./src/0-path-value-core/ShardPrefixes";
22
24
 
23
25
  async function main() {
24
- let paths = await pathValueArchives.getValuePaths(getAllAuthoritySpec());
25
- paths = paths.filter(x => x.startsWith("P!REMAINING/"));
26
- for (let path of paths) {
27
- let data = await archives().get(path);
28
- if (!data) {
29
- console.log(`Missing data file: ${path}`);
30
- continue;
31
- }
32
- let loadObj = await PathValueArchives.loadValuesFromBuffer({ path, data });
26
+ let prefixes = await getShardPrefixes();
27
+ console.log(prefixes);
28
+ // let paths = await pathValueArchives.getValuePaths(getAllAuthoritySpec());
29
+ // paths = paths.filter(x => x.startsWith("P!REMAINING/"));
30
+ // for (let path of paths) {
31
+ // let data = await archives().get(path);
32
+ // if (!data) {
33
+ // console.log(`Missing data file: ${path}`);
34
+ // continue;
35
+ // }
36
+ // let loadObj = await PathValueArchives.loadValuesFromBuffer({ path, data });
33
37
 
34
- for (let value of loadObj.values) {
35
- if (!PathRouter.matchesAuthoritySpec(getAllAuthoritySpec(), value.path)) {
36
- throw new Error(`Value ${value.path} does not match authority spec`);
37
- }
38
- console.log(pathValueSerializer.getPathValue(value));
39
- }
40
- }
41
- return;
38
+ // for (let value of loadObj.values) {
39
+ // if (!PathRouter.matchesAuthoritySpec(getAllAuthoritySpec(), value.path)) {
40
+ // throw new Error(`Value ${value.path} does not match authority spec`);
41
+ // }
42
+ // console.log(pathValueSerializer.getPathValue(value));
43
+ // }
44
+ // }
45
+ // return;
42
46
 
43
47
  // let path = "P!REMAINING/data_37c5d323e0f9a083.1ed7a7340a015680.querysubtest.com:52026_1775097754671.579_3_1_337_1775097652829.5793_1775097652829.5793_genesis_.data";
44
48
  // let value = await archives().get(path);
@@ -54,6 +58,7 @@ async function main() {
54
58
  // return path.split("/").at(-1)!;
55
59
  // }
56
60
 
61
+ //*
57
62
  let allValuesObj = await pathValueArchives.loadValues({
58
63
  nodeId: "",
59
64
  prefixes: [],
@@ -62,6 +67,7 @@ async function main() {
62
67
  });
63
68
  let allValues = Object.values(allValuesObj.values).flat();
64
69
  console.log(`Total values loaded: ${formatNumber(allValues.length)}`);
70
+ //*/
65
71
 
66
72
  // const badDeleteTransaction = "transaction_ tSeqNum=887 tWriteTime=1774715868374.9143 thread=9dc0 cCount=14 dCount=3482 create=X,X,X,X,X,X,X,X,X,X,X,X,X,X delete=2@0977,3@0977,4@0977,1@0a2a,2@0a2a,3@0a2a,4@0a2... .transaction";
67
73
 
@@ -1,50 +0,0 @@
1
- import "../inject";
2
-
3
- import { Querysub } from "../4-querysub/QuerysubController";
4
- import { logErrors } from "../errors";
5
- import { timeInDay, timeInHour } from "socket-function/src/misc";
6
- import { runArchiveMover } from "../2-proxy/archiveMoveHarness";
7
- import { PathValue, compareTime } from "../0-path-value-core/pathValueCore";
8
- import { disablePathAuditer } from "../diagnostics/pathAuditerCallback";
9
-
10
- const MERGE_DELAY = timeInHour;
11
-
12
- async function mergeFiles() {
13
- await runArchiveMover({
14
- debugName: "merge",
15
- outputType: "merge",
16
- async runMover(config) {
17
- let values = config.values;
18
- let latestValues = new Map<string, PathValue>();
19
- for (let value of values) {
20
- let prev = latestValues.get(value.path);
21
- if (prev && compareTime(value.time, prev.time) < 0) continue;
22
- latestValues.set(value.path, value);
23
- }
24
-
25
- values = Array.from(latestValues.values());
26
- values = values.filter(x => !x.canGCValue);
27
-
28
- return {
29
- newValues: {
30
- "": values,
31
- },
32
- };
33
- },
34
- });
35
- }
36
-
37
- async function main() {
38
- disablePathAuditer();
39
- await Querysub.hostService("merge");
40
- await mergeFiles();
41
- process.exit();
42
-
43
- // while (true) {
44
- // await mergeFiles();
45
- // let delay = MERGE_DELAY * (1 + Math.random() * 0.1);
46
- // await new Promise(resolve => setTimeout(resolve, delay));
47
- // }
48
- }
49
-
50
- main().catch(logErrors);
@@ -1,64 +0,0 @@
1
- import { SocketFunctionClientHook } from "socket-function/SocketFunctionTypes";
2
- import { cache, lazy } from "socket-function/src/caching";
3
- import { decodeCborx, encodeCborx } from "../../misc/cloneHelpers";
4
- import { sha256 } from "js-sha256";
5
- import { errorToUndefined } from "../../errors";
6
- import { isNode } from "typesafecss";
7
-
8
- let getRootDirectory = lazy(async () => {
9
- await navigator.storage.persist();
10
- return navigator.storage.getDirectory();
11
- });
12
-
13
- export const getBrowserLargeFileCache = cache((name: string) => new BrowserLargeFileCache(name));
14
-
15
- export class BrowserLargeFileCache {
16
- constructor(private name: string) { }
17
-
18
- private getDir = lazy(async () => {
19
- let root = await getRootDirectory();
20
- return await root.getDirectoryHandle(this.name, { create: true });
21
- });
22
-
23
- public async set(key: string, value: Buffer) {
24
- let dir = await this.getDir();
25
- let file = await dir.getFileHandle(key, { create: true });
26
- let writable = await file.createWritable();
27
- await writable.write(value);
28
- await writable.close();
29
- }
30
- public async get(key: string): Promise<Buffer | undefined> {
31
- let dir = await this.getDir();
32
- try {
33
- let file = await dir.getFileHandle(key, { create: false });
34
- let readable = await file.getFile();
35
- return Buffer.from(await readable.arrayBuffer());
36
- } catch {
37
- return undefined;
38
- }
39
- }
40
- }
41
-
42
- /** Cache key = [args, functionName, classGuid]
43
- * - Not nodeId, as that change so frequently, so caching based on server is not usually useful.
44
- * - If you want to cache based on nodeId, pass it as an unused arg.
45
- *
46
- * Uses a file per key, and never cleans them up. So... basically, this sucks, but, it should
47
- * work for a few specific functions with large and consistent values.
48
- */
49
- export const cacheCalls: SocketFunctionClientHook = async config => {
50
- if (isNode()) return;
51
-
52
- let { args, functionName, classGuid } = config.call;
53
- let bucket = sha256(JSON.stringify({ functionName, classGuid }));
54
- let cache = getBrowserLargeFileCache(bucket);
55
- let key = sha256(encodeCborx(args));
56
- let cachedValue = await cache.get(key);
57
- if (cachedValue) {
58
- config.overrideResult = decodeCborx(cachedValue);
59
- return;
60
- }
61
- config.onResult.push(async (result) => {
62
- await errorToUndefined(cache.set(key, encodeCborx(result)));
63
- });
64
- };