querysub 0.181.0 → 0.182.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/spec.txt +0 -4
- package/src/0-path-value-core/archiveLocks/ArchiveLocks2.ts +5 -5
- package/src/0-path-value-core/pathValueArchives.ts +3 -1
- package/src/4-querysub/Querysub.ts +7 -2
- package/src/archiveapps/archiveJoinEntry.ts +4 -1
- package/src/config.ts +3 -2
- package/src/misc/rawParams.ts +189 -0
package/package.json
CHANGED
package/spec.txt
CHANGED
|
@@ -51,10 +51,6 @@ More corruption resistance files
|
|
|
51
51
|
- Value types will be string, float64, byte, Buffer[]
|
|
52
52
|
- and... we might as well add support for short, int, float, and uint (uint is a good way to store a guid, via storing 8 uint variables).
|
|
53
53
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
54
|
Schema/binary PathValues accesses
|
|
59
55
|
|
|
60
56
|
Base code
|
|
@@ -683,19 +683,19 @@ class TransactionLocker {
|
|
|
683
683
|
{
|
|
684
684
|
let oldThreshold = readTime - DEAD_CREATE_THRESHOLD;
|
|
685
685
|
let veryOldFiles = dataState.rawDataFiles.filter(x => x.file.endsWith(".locked")).filter(a => a.createTime < oldThreshold);
|
|
686
|
-
let confirmedLookup = new Set(dataState.confirmedDataFiles.map(a => a));
|
|
687
|
-
let unconfirmedOldFiles = veryOldFiles.filter(a => !confirmedLookup.has(a));
|
|
686
|
+
let confirmedLookup = new Set(dataState.confirmedDataFiles.map(a => a.file));
|
|
687
|
+
let unconfirmedOldFiles = veryOldFiles.filter(a => !confirmedLookup.has(a.file));
|
|
688
688
|
|
|
689
689
|
if (unconfirmedOldFiles.length > 0) {
|
|
690
690
|
let doubleCheck = await this.readDataState();
|
|
691
|
-
let doubleCheckLookup = new Set(doubleCheck.confirmedDataFiles.map(a => a));
|
|
691
|
+
let doubleCheckLookup = new Set(doubleCheck.confirmedDataFiles.map(a => a.file));
|
|
692
692
|
let doubleCheckDataFiles = new Set(doubleCheck.rawDataFiles.map(a => a.file));
|
|
693
693
|
// IMPORTANT! Make sure they still aren't confirmed AND that the rawDataFiles exist. This helps prevent cases
|
|
694
694
|
// where backblaze returns no files? Which it might be doing, as multiple times this code has
|
|
695
695
|
// triggered (without this check), and deletes all of our files...
|
|
696
|
-
let unconfirmedOldFiles2 =
|
|
696
|
+
let unconfirmedOldFiles2 = unconfirmedOldFiles.filter(a => !doubleCheckLookup.has(a.file) && doubleCheckDataFiles.has(a.file));
|
|
697
697
|
console.warn(red(`Deleted ${unconfirmedOldFiles2.length} very old unconfirmed files`), { files: unconfirmedOldFiles2.map(x => x.file) });
|
|
698
|
-
|
|
698
|
+
logNodeStats(`archives|TΔ Delete Old Rejected File`, formatNumber, unconfirmedOldFiles2.length);
|
|
699
699
|
// At the point the file was very old when we started reading, not part of the active transaction.
|
|
700
700
|
for (let file of unconfirmedOldFiles2) {
|
|
701
701
|
await this.deleteDataFile(file.file, `old unconfirmed file (${getOwnNodeId()}, ${process.argv[1]})`);
|
|
@@ -185,7 +185,9 @@ export class PathValueArchives {
|
|
|
185
185
|
if (time < oldestTime) oldestTime = time;
|
|
186
186
|
}
|
|
187
187
|
let slowestFileWriteTime = oldestTime + ARCHIVE_FLUSH_LIMIT;
|
|
188
|
-
|
|
188
|
+
let fullPath = authorityPath + file;
|
|
189
|
+
console.log(`Write archive file ${fullPath}, with size ${formatNumber(data.byteLength)}B, and count ${formatNumber(values.length)}`);
|
|
190
|
+
await archives().set(fullPath, data);
|
|
189
191
|
|
|
190
192
|
let fileInfo = await archives().getInfo(authorityPath + file);
|
|
191
193
|
// NOTE: If no fileInfo... then our file was merged? Which... is BAD, as it means we took
|
|
@@ -45,6 +45,7 @@ import { devDebugbreak, getDomain, isDynamicallyLoading, isPublic, noSyncing } f
|
|
|
45
45
|
import { Schema2, Schema2T, t } from "../2-proxy/schema2";
|
|
46
46
|
import { CALL_PERMISSIONS_KEY } from "./permissionsShared";
|
|
47
47
|
import yargs from "yargs";
|
|
48
|
+
import { parseArgsFactory } from "../misc/rawParams";
|
|
48
49
|
|
|
49
50
|
import * as typesafecss from "typesafecss";
|
|
50
51
|
typesafecss.setMeasureBlock(measureBlock);
|
|
@@ -55,7 +56,7 @@ typesafecss.setDelayFnc(async fnc => {
|
|
|
55
56
|
|
|
56
57
|
export { t };
|
|
57
58
|
|
|
58
|
-
let yargObj =
|
|
59
|
+
let yargObj = parseArgsFactory()
|
|
59
60
|
.option("verboseall", { type: "boolean", desc: "Log all everything (except network)" })
|
|
60
61
|
.option("verbose", { type: "boolean", desc: "Log all writes and reads" })
|
|
61
62
|
.option("verbosewrites", { type: "boolean", desc: "Log all writes" })
|
|
@@ -63,7 +64,8 @@ let yargObj = isNodeTrue() && yargs(process.argv)
|
|
|
63
64
|
.option("verbosecalls", { type: "boolean", desc: "Log all calls" })
|
|
64
65
|
.option("verbosenetwork", { type: "boolean", desc: "Log all network activity" })
|
|
65
66
|
.option("verboseframework", { type: "boolean", desc: "Log internal SocketFunction framework" })
|
|
66
|
-
.
|
|
67
|
+
.option("nodelay", { type: "boolean", desc: "Don't delay committing functions, even ones that are marked to be delayed." })
|
|
68
|
+
.argv
|
|
67
69
|
;
|
|
68
70
|
setImmediate(() => {
|
|
69
71
|
if (yargObj.verbose) {
|
|
@@ -92,6 +94,9 @@ setImmediate(() => {
|
|
|
92
94
|
if (yargObj.verboseframework) {
|
|
93
95
|
SocketFunction.silent = false;
|
|
94
96
|
}
|
|
97
|
+
if (yargObj.nodelay) {
|
|
98
|
+
Querysub.DELAY_COMMIT_DELAY = 0;
|
|
99
|
+
}
|
|
95
100
|
});
|
|
96
101
|
|
|
97
102
|
if (!registerGetCompressNetwork) {
|
|
@@ -27,7 +27,9 @@ async function runGenesisJoinIteration() {
|
|
|
27
27
|
while (reread) {
|
|
28
28
|
reread = false;
|
|
29
29
|
let result = await locker.atomicSwapFiles({}, async (valueFiles, readFiles) => {
|
|
30
|
+
let originalCount = valueFiles.length;
|
|
30
31
|
valueFiles = valueFiles.filter(x => matchedDirs.some(y => x.file.startsWith(y)));
|
|
32
|
+
let underPathCount = valueFiles.length;
|
|
31
33
|
// Merge the newest first, so if we hit a big file, we can just ignore it,
|
|
32
34
|
// and next time merge the smaller files after it.
|
|
33
35
|
sort(valueFiles, x => -x.createTime);
|
|
@@ -37,6 +39,7 @@ async function runGenesisJoinIteration() {
|
|
|
37
39
|
if (obj.sourceType !== "genesis") return false;
|
|
38
40
|
return x.createTime >= maxAge;
|
|
39
41
|
});
|
|
42
|
+
let withinTimeRangeCount = valueFiles.length;
|
|
40
43
|
|
|
41
44
|
if (valueFiles.length < 3) return [];
|
|
42
45
|
|
|
@@ -105,7 +108,7 @@ async function runGenesisJoinIteration() {
|
|
|
105
108
|
});
|
|
106
109
|
|
|
107
110
|
|
|
108
|
-
console.log(magenta(`Joining ${formatNumber(usedFiles.length)} files with ${formatNumber(allCombinedValues.length)} values in ${formatNumber(totalSize)} bytes`));
|
|
111
|
+
console.log(magenta(`Joining ${formatNumber(usedFiles.length)} files with ${formatNumber(allCombinedValues.length)} values in ${formatNumber(totalSize)} bytes. Original count: ${formatNumber(originalCount)}, under path count: ${formatNumber(underPathCount)}, within time range count: ${formatNumber(withinTimeRangeCount)}`));
|
|
109
112
|
|
|
110
113
|
return [transaction];
|
|
111
114
|
});
|
package/src/config.ts
CHANGED
|
@@ -2,10 +2,11 @@ import { isNode, isNodeTrue } from "socket-function/src/misc";
|
|
|
2
2
|
import yargs from "yargs";
|
|
3
3
|
import debugbreak from "debugbreak";
|
|
4
4
|
import { MaybePromise } from "socket-function/src/types";
|
|
5
|
+
import { parseArgsFactory } from "./misc/rawParams";
|
|
5
6
|
|
|
6
7
|
export const serverPort = 11748;
|
|
7
8
|
|
|
8
|
-
let yargObj =
|
|
9
|
+
let yargObj = parseArgsFactory()
|
|
9
10
|
.option("nonetwork", { type: "boolean", desc: `Disables all network requirements. Reduces security, as this means we cannot use real certificates.` })
|
|
10
11
|
.option("domain", { type: "string", desc: `Sets the domain` })
|
|
11
12
|
.option("client", { type: "boolean", desc: `Drops permissions, acting as an unauthenticated node` })
|
|
@@ -16,7 +17,7 @@ let yargObj = isNodeTrue() && yargs(process.argv)
|
|
|
16
17
|
.option("local", { type: "boolean", desc: `If true, uses the local directory instead of the remote git repo. Also hotreloads from disk. Determines the repo to replace through the package.json "repository" property.` })
|
|
17
18
|
.option("nosyncing", { type: "boolean", desc: `Disabled any syncing code, just using HTTP. Still manages routing via backblaze though.` })
|
|
18
19
|
.option("recovery", { type: "boolean", desc: `Allows any localhost connections to act like a superuser (and a trusted node), to help recover the database (as you need permission to access the snapshot page).` })
|
|
19
|
-
.argv
|
|
20
|
+
.argv
|
|
20
21
|
;
|
|
21
22
|
|
|
22
23
|
if (!isNode()) {
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { setFlag } from "socket-function/require/compileFlags";
|
|
2
|
+
import { isNode } from "typesafecss";
|
|
3
|
+
import yargs from "yargs";
|
|
4
|
+
|
|
5
|
+
type Arguments<T = {}> = T & {
|
|
6
|
+
/** Non-option arguments */
|
|
7
|
+
_: Array<string | number>;
|
|
8
|
+
/** The script name or node command */
|
|
9
|
+
$0: string;
|
|
10
|
+
/** All remaining options */
|
|
11
|
+
[argName: string]: unknown;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
interface Options {
|
|
15
|
+
desc?: string;
|
|
16
|
+
required?: boolean;
|
|
17
|
+
type?: "array" | "boolean" | "number" | "string" | undefined;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type InferredOptionTypeBase<O extends Options> = (
|
|
21
|
+
O extends { type: "array" } ? string[]
|
|
22
|
+
: O extends { type: "boolean" } ? boolean
|
|
23
|
+
: O extends { type: "number" } ? number
|
|
24
|
+
: O extends { type: "string" } ? string : never
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
type InferredOptionType<O extends Options> = (
|
|
28
|
+
O extends { required: true } ? InferredOptionTypeBase<O> : InferredOptionTypeBase<O> | undefined
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
interface Argv<T = {}> {
|
|
32
|
+
(): { [key in keyof Arguments<T>]: Arguments<T>[key] };
|
|
33
|
+
(args: readonly string[], cwd?: string): Argv<T>;
|
|
34
|
+
|
|
35
|
+
option<K extends string, O extends Options>(
|
|
36
|
+
key: K,
|
|
37
|
+
options: O,
|
|
38
|
+
): Argv<T & { [key in K]: InferredOptionType<O> }>;
|
|
39
|
+
|
|
40
|
+
argv: T;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function getRawParams(): string[] {
|
|
44
|
+
if (isNode()) return process.argv;
|
|
45
|
+
let searchParams = new URLSearchParams(location.search);
|
|
46
|
+
let result: string[] = [];
|
|
47
|
+
for (let [key, value] of searchParams.entries()) {
|
|
48
|
+
if (value === "") {
|
|
49
|
+
result.push(`--${key}`);
|
|
50
|
+
} else {
|
|
51
|
+
result.push(`--${key}=${value}`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function parseArgsFactory(): Argv {
|
|
58
|
+
type OptionCall = {
|
|
59
|
+
key: string;
|
|
60
|
+
options: Options;
|
|
61
|
+
};
|
|
62
|
+
type CurrentConfig = {
|
|
63
|
+
options: OptionCall[];
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
function finish(config: CurrentConfig) {
|
|
67
|
+
let params = getRawParams();
|
|
68
|
+
let result: Record<string, unknown> = {};
|
|
69
|
+
|
|
70
|
+
params = params.map(param => param.replace(/^-+/, ""));
|
|
71
|
+
|
|
72
|
+
if (params.includes("help")) {
|
|
73
|
+
console.log("Available options:");
|
|
74
|
+
for (let option of config.options) {
|
|
75
|
+
let type = option.options.type || "string";
|
|
76
|
+
let required = option.options.required ? " (required)" : "";
|
|
77
|
+
let desc = option.options.desc ? ` - ${option.options.desc}` : "";
|
|
78
|
+
console.log(` --${option.key} (${type})${required}${desc}`);
|
|
79
|
+
}
|
|
80
|
+
process.exit(0);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Second pass: join split parameters
|
|
84
|
+
let processedParams: string[] = [];
|
|
85
|
+
for (let i = 0; i < params.length; i++) {
|
|
86
|
+
let key = params[i];
|
|
87
|
+
|
|
88
|
+
// Only treat as option if it's in the options list
|
|
89
|
+
let optionConfig = config.options.find(opt => opt.key === key);
|
|
90
|
+
if (optionConfig && optionConfig.options.type !== "boolean") {
|
|
91
|
+
let nextParam = params[i + 1]; // Use original for value
|
|
92
|
+
// Join them: x 5 becomes x=5
|
|
93
|
+
processedParams.push(`${key}=${nextParam}`);
|
|
94
|
+
i++; // Skip the next parameter
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
processedParams.push(key);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Second pass: process the parameters
|
|
102
|
+
for (let param of processedParams) {
|
|
103
|
+
// Check if it's a key=value format
|
|
104
|
+
if (param.includes("=")) {
|
|
105
|
+
let [optionKey, ...valueParts] = param.split("=");
|
|
106
|
+
let value = valueParts.join("=");
|
|
107
|
+
let optionConfig = config.options.find(opt => opt.key === optionKey);
|
|
108
|
+
|
|
109
|
+
if (optionConfig) {
|
|
110
|
+
let { type } = optionConfig.options;
|
|
111
|
+
if (type === "boolean") {
|
|
112
|
+
result[optionKey] = value === "true";
|
|
113
|
+
} else if (type === "number") {
|
|
114
|
+
result[optionKey] = Number(value);
|
|
115
|
+
} else if (type === "array") {
|
|
116
|
+
if (!result[optionKey]) result[optionKey] = [];
|
|
117
|
+
(result[optionKey] as unknown[]).push(value);
|
|
118
|
+
} else {
|
|
119
|
+
result[optionKey] = value;
|
|
120
|
+
}
|
|
121
|
+
} else {
|
|
122
|
+
result[optionKey] = value;
|
|
123
|
+
}
|
|
124
|
+
} else {
|
|
125
|
+
// Regular option (boolean)
|
|
126
|
+
let optionConfig = config.options.find(opt => opt.key === param);
|
|
127
|
+
|
|
128
|
+
if (optionConfig) {
|
|
129
|
+
let { type } = optionConfig.options;
|
|
130
|
+
|
|
131
|
+
if (type === "boolean") {
|
|
132
|
+
result[param] = true;
|
|
133
|
+
} else {
|
|
134
|
+
// Other types should have been joined in first pass
|
|
135
|
+
// Set default if somehow we get here
|
|
136
|
+
if (type === "number") {
|
|
137
|
+
result[param] = 0;
|
|
138
|
+
} else if (type === "array") {
|
|
139
|
+
result[param] = [];
|
|
140
|
+
} else {
|
|
141
|
+
result[param] = "";
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
// Unknown option, treat as boolean
|
|
146
|
+
result[param] = true;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Set default values for required options that weren't provided
|
|
152
|
+
for (let option of config.options) {
|
|
153
|
+
if (option.options.required && result[option.key] === undefined) {
|
|
154
|
+
if (option.options.type === "boolean") {
|
|
155
|
+
result[option.key] = false;
|
|
156
|
+
} else if (option.options.type === "array") {
|
|
157
|
+
result[option.key] = [];
|
|
158
|
+
} else if (option.options.type === "number") {
|
|
159
|
+
result[option.key] = 0;
|
|
160
|
+
} else {
|
|
161
|
+
result[option.key] = "";
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return result;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function createCallback(config: CurrentConfig): Argv {
|
|
170
|
+
const callback = {
|
|
171
|
+
option: (key: string, options: Options) => {
|
|
172
|
+
config.options.push({ key, options });
|
|
173
|
+
return createCallback(config) as any;
|
|
174
|
+
},
|
|
175
|
+
get argv() {
|
|
176
|
+
return finish(config);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
// Make the callback callable
|
|
181
|
+
const fn = (() => callback.argv) as any;
|
|
182
|
+
fn.option = callback.option;
|
|
183
|
+
fn.argv = callback.argv;
|
|
184
|
+
|
|
185
|
+
return fn;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return createCallback({ options: [] });
|
|
189
|
+
}
|