querysub 0.405.0 → 0.407.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/deploy-prefixes.js +7 -0
- package/package.json +2 -1
- package/src/-f-node-discovery/NodeDiscovery.ts +0 -2
- package/src/0-path-value-core/AuthorityLookup.ts +7 -2
- package/src/0-path-value-core/PathRouter.ts +170 -84
- package/src/0-path-value-core/PathRouterRouteOverride.ts +2 -2
- package/src/0-path-value-core/PathRouterServerAuthoritySpec.tsx +2 -8
- package/src/0-path-value-core/PathValueCommitter.ts +65 -30
- package/src/0-path-value-core/PathValueController.ts +2 -4
- package/src/0-path-value-core/PathWatcher.ts +7 -4
- package/src/0-path-value-core/ShardPrefixes.ts +4 -0
- package/src/0-path-value-core/hackedPackedPathParentFiltering.ts +12 -31
- package/src/0-path-value-core/pathValueArchives.ts +3 -3
- package/src/0-path-value-core/pathValueCore.ts +32 -24
- package/src/0-path-value-core/startupAuthority.ts +9 -9
- package/src/1-path-client/RemoteWatcher.ts +188 -170
- package/src/1-path-client/pathValueClientWatcher.ts +6 -11
- package/src/2-proxy/garbageCollection.ts +4 -3
- package/src/2-proxy/pathValueProxy.ts +2 -3
- package/src/3-path-functions/PathFunctionRunner.ts +3 -1
- package/src/3-path-functions/syncSchema.ts +6 -2
- package/src/4-deploy/deployGetFunctionsInner.ts +1 -1
- package/src/4-deploy/deployPrefixes.ts +14 -0
- package/src/4-deploy/edgeNodes.ts +1 -1
- package/src/diagnostics/SyncTestPage.tsx +19 -8
- package/src/functional/promiseCache.ts +5 -2
- package/src/path.ts +15 -2
- package/src/rangeMath.ts +41 -0
- package/tempnotes.txt +12 -26
- package/test.ts +8 -4
- package/src/diagnostics/logs/BrowserLargeFileCache.ts +0 -64
|
@@ -21,6 +21,7 @@ import { getSpecFromModule } from "./pathFunctionLoader";
|
|
|
21
21
|
import { isNode } from "typesafecss";
|
|
22
22
|
import { LOCAL_DOMAIN } from "../0-path-value-core/PathRouter";
|
|
23
23
|
import { createRoutingOverrideKey } from "../0-path-value-core/PathRouterRouteOverride";
|
|
24
|
+
import { delay } from "socket-function/src/batching";
|
|
24
25
|
|
|
25
26
|
// This is the the function id which should be used when creating the FunctionSpec (in order to load the module),
|
|
26
27
|
// to access the permissions in the schema.
|
|
@@ -286,7 +287,9 @@ type SyncSchemaResult<Schema> = {
|
|
|
286
287
|
|
|
287
288
|
let prefixes: string[] = [];
|
|
288
289
|
// ONLY used by the deploy process. Path value server is pretty much the only one who needs this, and they aren't including client-side code, and they definitely aren't updating client-side code, so it doesn't make sense to use this directly.
|
|
289
|
-
export function getPrefixesForDeploy(): string[] {
|
|
290
|
+
export async function getPrefixesForDeploy(): Promise<string[]> {
|
|
291
|
+
await new Promise(r => setImmediate(r));
|
|
292
|
+
await new Promise(r => setImmediate(r));
|
|
290
293
|
return prefixes;
|
|
291
294
|
}
|
|
292
295
|
// Used for some old non syncSchema schema cases
|
|
@@ -302,7 +305,8 @@ export function syncSchema<Schema>(schema?: Schema2): SyncSchemaResult<Schema> {
|
|
|
302
305
|
const domainName = config.domainName ?? getDomain();
|
|
303
306
|
|
|
304
307
|
const data = () => functionSchema()[domainName].PathFunctionRunner[moduleId].Data;
|
|
305
|
-
|
|
308
|
+
// NOTE: We used to cache this, but then they would cache the current database that's inside of the schema, which is dynamically set. So I don't think caching this really makes sense. We might be able to do some kind of intelligent cache where we check what the current database is and only reuse the cache if that's the same.
|
|
309
|
+
let dataCached = (() => data()) as () => Schema;
|
|
306
310
|
|
|
307
311
|
let fncs = { ...functions };
|
|
308
312
|
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { setShardPrefixes } from "../0-path-value-core/ShardPrefixes";
|
|
2
|
+
import { getPrefixesForDeploy } from "../3-path-functions/syncSchema";
|
|
3
|
+
import path from "path";
|
|
4
|
+
|
|
5
|
+
async function main() {
|
|
6
|
+
let deployPath = path.resolve("./deploy.ts");
|
|
7
|
+
await import(deployPath);
|
|
8
|
+
|
|
9
|
+
let prefixes = await getPrefixesForDeploy();
|
|
10
|
+
await setShardPrefixes(prefixes);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
main().catch(console.error).finally(() => process.exit());
|
|
14
|
+
|
|
@@ -273,7 +273,7 @@ async function updateEdgeNodesFile() {
|
|
|
273
273
|
let liveHash = "";
|
|
274
274
|
if (!noSyncing()) {
|
|
275
275
|
liveHash = await Querysub.commitSynced(() => {
|
|
276
|
-
return deploySchema()[getDomain()].deploy.live.hash;
|
|
276
|
+
return String(deploySchema()[getDomain()].deploy.live.hash);
|
|
277
277
|
});
|
|
278
278
|
}
|
|
279
279
|
|
|
@@ -17,11 +17,9 @@ import { PathAuditerController } from "./pathAuditer";
|
|
|
17
17
|
import { t } from "../2-proxy/schema2";
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
let { data, functions } = Querysub.createSchema
|
|
21
|
-
values:
|
|
22
|
-
|
|
23
|
-
};
|
|
24
|
-
}>()({
|
|
20
|
+
let { data, functions } = Querysub.createSchema({
|
|
21
|
+
values: t.lookup(t.number)
|
|
22
|
+
})({
|
|
25
23
|
functions: {
|
|
26
24
|
setValue(key: string, value: number) {
|
|
27
25
|
data().values[key] = value;
|
|
@@ -90,6 +88,14 @@ export class SyncTestPage extends qreact.Component {
|
|
|
90
88
|
allThreads = allThreads.slice();
|
|
91
89
|
sort(allThreads, x => x.entrypoint);
|
|
92
90
|
|
|
91
|
+
function refreshDelayed() {
|
|
92
|
+
Querysub.onCommitFinished(() => {
|
|
93
|
+
setTimeout(() => {
|
|
94
|
+
syncTestController.refreshAll();
|
|
95
|
+
}, 500);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
|
|
93
99
|
return (
|
|
94
100
|
<div className={css.pad2(20).vbox(40)}>
|
|
95
101
|
<h1>Sync Test Page</h1>
|
|
@@ -125,8 +131,11 @@ export class SyncTestPage extends qreact.Component {
|
|
|
125
131
|
<div className={css.hbox(10)}>
|
|
126
132
|
<span class={css.boldStyle}>{key}</span>
|
|
127
133
|
<span>{value}</span>
|
|
128
|
-
<Button onClick={() => functions.incrementValue(key)}>Increment</Button>
|
|
129
|
-
<InputLabel value={value} number onChangeValue={value =>
|
|
134
|
+
<Button onClick={() => { functions.incrementValue(key); refreshDelayed(); }}>Increment</Button>
|
|
135
|
+
<InputLabel value={value} number onChangeValue={value => {
|
|
136
|
+
functions.setValue(key, +value);
|
|
137
|
+
refreshDelayed();
|
|
138
|
+
}} />
|
|
130
139
|
</div>
|
|
131
140
|
<div className={css.hbox(10, 2).wrap.marginLeft(20)}>
|
|
132
141
|
{allThreads.map(thread => {
|
|
@@ -149,6 +158,7 @@ export class SyncTestPage extends qreact.Component {
|
|
|
149
158
|
path: path,
|
|
150
159
|
nodeId: thread.nodeId
|
|
151
160
|
});
|
|
161
|
+
refreshDelayed();
|
|
152
162
|
}}>
|
|
153
163
|
Delete Value
|
|
154
164
|
</Button>
|
|
@@ -168,6 +178,7 @@ export class SyncTestPage extends qreact.Component {
|
|
|
168
178
|
label="Insert new key"
|
|
169
179
|
onChangeValue={value => {
|
|
170
180
|
functions.setValue(value, 0);
|
|
181
|
+
refreshDelayed();
|
|
171
182
|
}}
|
|
172
183
|
/>
|
|
173
184
|
<Button onClick={() => {
|
|
@@ -205,7 +216,7 @@ class SyncTestControllerBase {
|
|
|
205
216
|
}
|
|
206
217
|
|
|
207
218
|
public async getValueAndTime(path: string) {
|
|
208
|
-
let pathValue = authorityStorage.getValueAtTime(path);
|
|
219
|
+
let pathValue = authorityStorage.getValueAtTime(path, undefined, false, "noAudit");
|
|
209
220
|
let value = pathValueSerializer.getPathValue(pathValue);
|
|
210
221
|
let time = pathValue?.time.time;
|
|
211
222
|
return { value, time };
|
|
@@ -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
|
|
77
|
+
return config?.notReadyValue;
|
|
75
78
|
};
|
|
76
79
|
}
|
package/src/path.ts
CHANGED
|
@@ -171,9 +171,16 @@ export function getParentPathStr(pathStr: string) {
|
|
|
171
171
|
return pathStr.slice(0, getStartOfLastPart(pathStr));
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
/** === getPathFromStr(pathStr).slice(-1)[0] */
|
|
174
|
+
/** === getPathFromStr(pathStr).slice(-1)[0] || "" */
|
|
175
175
|
export function getLastPathPart(pathStr: string) {
|
|
176
|
-
|
|
176
|
+
let lastPartIndex = pathStr.lastIndexOf(pathDelimitEscaped);
|
|
177
|
+
if (lastPartIndex < 0) return "";
|
|
178
|
+
return unescapePathPart(
|
|
179
|
+
pathStr.slice(
|
|
180
|
+
getStartOfLastPart(pathStr),
|
|
181
|
+
lastPartIndex,
|
|
182
|
+
)
|
|
183
|
+
);
|
|
177
184
|
}
|
|
178
185
|
|
|
179
186
|
/** === getPathStr(getPathFromStr(pathStr).slice(0, -1)) */
|
|
@@ -181,8 +188,12 @@ export function removePathLastPart(pathStr: string) {
|
|
|
181
188
|
return pathStr.slice(0, getStartOfLastPart(pathStr));
|
|
182
189
|
}
|
|
183
190
|
|
|
191
|
+
let lastPastPath = { path: "", depth: 0 };
|
|
184
192
|
/** getPathDepth(pathStr) === getPathFromStr(pathStr).length */
|
|
185
193
|
export function getPathDepth(pathStr: string): number {
|
|
194
|
+
if (lastPastPath.path === pathStr) {
|
|
195
|
+
return lastPastPath.depth;
|
|
196
|
+
}
|
|
186
197
|
// Start at 1, to skip the first pathDelimit
|
|
187
198
|
let index = 1;
|
|
188
199
|
// Start at -1, as the last loop doesn't have pathDelimitEscaped, but is still incremented
|
|
@@ -191,6 +202,8 @@ export function getPathDepth(pathStr: string): number {
|
|
|
191
202
|
count++;
|
|
192
203
|
index = pathStr.indexOf(pathDelimitEscaped, index) + 1;
|
|
193
204
|
}
|
|
205
|
+
lastPastPath.path = pathStr;
|
|
206
|
+
lastPastPath.depth = count;
|
|
194
207
|
return count;
|
|
195
208
|
}
|
|
196
209
|
|
package/src/rangeMath.ts
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export type Range = {
|
|
2
|
+
start: number;
|
|
3
|
+
end: number;
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
export type Ranges = Range[];
|
|
7
|
+
|
|
8
|
+
export function rangesOverlap(a: Range, b: Range): boolean {
|
|
9
|
+
return a.start < b.end && a.end > b.start;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export function removeRange(ranges: Ranges, range: Range): {
|
|
13
|
+
removedRanges: Range[];
|
|
14
|
+
} {
|
|
15
|
+
let s = range.start;
|
|
16
|
+
let e = range.end;
|
|
17
|
+
let removedRanges: Range[] = [];
|
|
18
|
+
for (let i = ranges.length - 1; i >= 0; i--) {
|
|
19
|
+
let rangeInRanges = ranges[i];
|
|
20
|
+
if (s >= rangeInRanges.end || e <= rangeInRanges.start) continue;
|
|
21
|
+
let startTaken = Math.max(rangeInRanges.start, s);
|
|
22
|
+
let endTaken = Math.min(rangeInRanges.end, e);
|
|
23
|
+
removedRanges.push({ start: startTaken, end: endTaken });
|
|
24
|
+
ranges.splice(i, 1);
|
|
25
|
+
// Add back the parts we didn't overlap
|
|
26
|
+
if (rangeInRanges.start < s) {
|
|
27
|
+
ranges.push({
|
|
28
|
+
start: rangeInRanges.start,
|
|
29
|
+
end: s,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
if (rangeInRanges.end > e) {
|
|
33
|
+
ranges.push({
|
|
34
|
+
start: e,
|
|
35
|
+
end: rangeInRanges.end,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
}
|
|
40
|
+
return { removedRanges };
|
|
41
|
+
}
|
package/tempnotes.txt
CHANGED
|
@@ -1,42 +1,24 @@
|
|
|
1
1
|
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
FIXED? The CYOA server keeps locking up
|
|
5
|
-
█ PathValueSerializer() 46.34% ( 66.6s = 75.3K * 765us + 550K * 16.3us )
|
|
6
|
-
- The values keep syncing to both servers.
|
|
7
|
-
FIXED? The CYOA and the function server keep failing the audits as well. We might need to look at the logs to see how this could happen. It'll probably be obvious once we look at the logs for those paths.
|
|
8
|
-
- Is it because we're unwatching it? That would make a lot of sense.
|
|
9
|
-
- I guess we can just step in and see if its watched
|
|
10
|
-
pathWatcher.watchers.get(path)
|
|
11
|
-
remoteWatcher.remoteWatchPaths.get(path)
|
|
2
|
+
2) Change the test page lookup to be an actual lookup (and make it actually use the type safe schema)
|
|
3
|
+
- And... deploy, we need to deploy the function too...
|
|
12
4
|
|
|
5
|
+
3) After making it an actual lookup, it should match with the function sharding. And so this should get rid of the cases when one update goes to both servers (Which happens because without the lookup both the data values and the function values could resolve to be on different servers, meaning both servers will know about the write).
|
|
13
6
|
|
|
14
|
-
|
|
15
|
-
3) gc, to split it into multiple directories
|
|
16
|
-
4) Verify the site still works
|
|
17
|
-
|
|
18
|
-
5) Verify that the sharded startup only loads in the paths it needs and isn't wasteful
|
|
19
|
-
- With 100 granularity the waste should be quite low. Unless we get REALLY unlucky? Hmm...
|
|
20
|
-
|
|
21
|
-
6) Record path waste per PathValueServer, and add an admin warn component that requests this, then shows a warning for PathValueServers that have a high waste load %
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
3) Test sharding again
|
|
25
7
|
4) Verify the test page works well, and shards
|
|
26
8
|
- We have the function sharding, so the values shouldn't be sent to both servers because the function calls themselves should be sharded.
|
|
27
9
|
|
|
28
10
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
11
|
+
3) add code so that client-side we can get a breakdown of How many paths we have per authority. The goal is to have it heavily biased, not balanced. We want to only use a single server. (But then, of course, for different users, we should be using a different server)
|
|
12
|
+
3.1) AND Give a count of how many writes per authority and how many reads, as in how many changes per authority.
|
|
13
|
+
3.2) Let it popup and show which paths are on each server
|
|
33
14
|
|
|
34
|
-
3) add code so that client-side we can get a breakdown of How many paths we have per authority. The goal is to just have most the paths come from a single authority. For reading, it's actually more efficient to talk to a single server rather than many servers, as it's not the amount of data, it's really just the latency overhead of talking to the server. And for writing, same thing, it's a lot easier to just write to one server. And if every single user is reading from every single server, it's just not feasible. It no longer scales well.
|
|
35
15
|
|
|
36
16
|
4) Get the AI to go through and add function remapping code. Basically, the idea is that we take the most accessed lookup that we're accessing and we use the key for that as our hash. For simple functions, we could actually automate this, but I'll see how the AI can do.
|
|
37
17
|
|
|
38
18
|
5) Verify we are mostly accessing paths on one authority
|
|
39
19
|
|
|
20
|
+
3.3) Do more testing with having a partial shard range being down. Realistically, we should be able to set some kind of range, maybe just a very small range in the middle of the two main servers that isn't covered, and still be able to use the site. Previously, we weren't able to. Maybe this was because the transactions just never resolved because we couldn't synchronize some of the values?
|
|
21
|
+
|
|
40
22
|
|
|
41
23
|
7) Test 4 servers, sharded and redundant as the same time
|
|
42
24
|
8) Verify on the test page
|
|
@@ -45,6 +27,10 @@ BUGS:
|
|
|
45
27
|
- Now is as good a time as any to deal with any of the sharding issues. And it should work easily enough...
|
|
46
28
|
- Verify the secondary run backup code works too.
|
|
47
29
|
|
|
30
|
+
9) Do some tests with adding new modules and new functions in development
|
|
31
|
+
- I think we need to have the git prefix code. Get some extra prefixes in development.
|
|
32
|
+
- And likely import the deploy.ts code...
|
|
33
|
+
- Some kind of "getDevelopmentPrefixes" should be fine, which we dynamically call when is not public in getShardPrefixes
|
|
48
34
|
|
|
49
35
|
2) Add disk verification service
|
|
50
36
|
- pick a random authority => read the same we would as startup => do an extra backblaze getKeys and getInfo to make sure our cache is accurate => load in the data into memory => verify all old paths with that authority?
|
package/test.ts
CHANGED
|
@@ -21,12 +21,16 @@ import { getAllAuthoritySpec, getOurAuthoritySpec } from "./src/0-path-value-cor
|
|
|
21
21
|
import { PathRouter } from "./src/0-path-value-core/PathRouter";
|
|
22
22
|
import { pathValueSerializer } from "./src/-h-path-value-serialize/PathValueSerializer";
|
|
23
23
|
import { getShardPrefixes } from "./src/0-path-value-core/ShardPrefixes";
|
|
24
|
+
import { getPrefixesForDeploy } from "./src/3-path-functions/syncSchema";
|
|
24
25
|
|
|
25
26
|
async function main() {
|
|
26
|
-
let prefixes = await getShardPrefixes();
|
|
27
|
-
console.log(prefixes);
|
|
28
27
|
let deployPath = path.resolve("./deploy.ts");
|
|
29
28
|
await import(deployPath);
|
|
29
|
+
let prefixes = await getPrefixesForDeploy();
|
|
30
|
+
console.log(prefixes);
|
|
31
|
+
// let prefixes = await getShardPrefixes();
|
|
32
|
+
// console.log(prefixes);
|
|
33
|
+
return;
|
|
30
34
|
// let paths = await pathValueArchives.getValuePaths(getAllAuthoritySpec());
|
|
31
35
|
// paths = paths.filter(x => x.startsWith("P!REMAINING/"));
|
|
32
36
|
// for (let path of paths) {
|
|
@@ -60,7 +64,7 @@ async function main() {
|
|
|
60
64
|
// return path.split("/").at(-1)!;
|
|
61
65
|
// }
|
|
62
66
|
|
|
63
|
-
|
|
67
|
+
//*
|
|
64
68
|
let allValuesObj = await pathValueArchives.loadValues({
|
|
65
69
|
nodeId: "",
|
|
66
70
|
prefixes: [],
|
|
@@ -69,7 +73,7 @@ async function main() {
|
|
|
69
73
|
});
|
|
70
74
|
let allValues = Object.values(allValuesObj.values).flat();
|
|
71
75
|
console.log(`Total values loaded: ${formatNumber(allValues.length)}`);
|
|
72
|
-
|
|
76
|
+
//*/
|
|
73
77
|
|
|
74
78
|
// 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";
|
|
75
79
|
|
|
@@ -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
|
-
};
|