cojson 0.8.11 → 0.8.16
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/CHANGELOG.md +94 -82
- package/dist/native/PeerKnownStates.js +1 -1
- package/dist/native/PeerKnownStates.js.map +1 -1
- package/dist/native/PeerState.js +4 -1
- package/dist/native/PeerState.js.map +1 -1
- package/dist/native/PriorityBasedMessageQueue.js +1 -10
- package/dist/native/PriorityBasedMessageQueue.js.map +1 -1
- package/dist/native/base64url.js.map +1 -1
- package/dist/native/base64url.test.js +1 -1
- package/dist/native/base64url.test.js.map +1 -1
- package/dist/native/coValue.js.map +1 -1
- package/dist/native/coValueCore.js +141 -149
- package/dist/native/coValueCore.js.map +1 -1
- package/dist/native/coValueState.js.map +1 -1
- package/dist/native/coValues/account.js +6 -6
- package/dist/native/coValues/account.js.map +1 -1
- package/dist/native/coValues/coList.js +2 -3
- package/dist/native/coValues/coList.js.map +1 -1
- package/dist/native/coValues/coMap.js +1 -1
- package/dist/native/coValues/coMap.js.map +1 -1
- package/dist/native/coValues/coStream.js +3 -5
- package/dist/native/coValues/coStream.js.map +1 -1
- package/dist/native/coValues/group.js +11 -11
- package/dist/native/coValues/group.js.map +1 -1
- package/dist/native/coreToCoValue.js +2 -2
- package/dist/native/coreToCoValue.js.map +1 -1
- package/dist/native/crypto/PureJSCrypto.js +4 -4
- package/dist/native/crypto/PureJSCrypto.js.map +1 -1
- package/dist/native/crypto/crypto.js.map +1 -1
- package/dist/native/exports.js +12 -12
- package/dist/native/exports.js.map +1 -1
- package/dist/native/ids.js.map +1 -1
- package/dist/native/jsonStringify.js.map +1 -1
- package/dist/native/localNode.js +6 -8
- package/dist/native/localNode.js.map +1 -1
- package/dist/native/permissions.js +4 -7
- package/dist/native/permissions.js.map +1 -1
- package/dist/native/priority.js.map +1 -1
- package/dist/native/storage/FileSystem.js.map +1 -1
- package/dist/native/storage/chunksAndKnownStates.js +2 -4
- package/dist/native/storage/chunksAndKnownStates.js.map +1 -1
- package/dist/native/storage/index.js +7 -16
- package/dist/native/storage/index.js.map +1 -1
- package/dist/native/streamUtils.js.map +1 -1
- package/dist/native/sync.js +6 -8
- package/dist/native/sync.js.map +1 -1
- package/dist/native/typeUtils/accountOrAgentIDfromSessionID.js.map +1 -1
- package/dist/native/typeUtils/expectGroup.js.map +1 -1
- package/dist/native/typeUtils/isAccountID.js.map +1 -1
- package/dist/native/typeUtils/isCoValue.js +1 -1
- package/dist/native/typeUtils/isCoValue.js.map +1 -1
- package/dist/web/PeerKnownStates.js +1 -1
- package/dist/web/PeerKnownStates.js.map +1 -1
- package/dist/web/PeerState.js +4 -1
- package/dist/web/PeerState.js.map +1 -1
- package/dist/web/PriorityBasedMessageQueue.js +1 -10
- package/dist/web/PriorityBasedMessageQueue.js.map +1 -1
- package/dist/web/base64url.js.map +1 -1
- package/dist/web/base64url.test.js +1 -1
- package/dist/web/base64url.test.js.map +1 -1
- package/dist/web/coValue.js.map +1 -1
- package/dist/web/coValueCore.js +141 -149
- package/dist/web/coValueCore.js.map +1 -1
- package/dist/web/coValueState.js.map +1 -1
- package/dist/web/coValues/account.js +6 -6
- package/dist/web/coValues/account.js.map +1 -1
- package/dist/web/coValues/coList.js +2 -3
- package/dist/web/coValues/coList.js.map +1 -1
- package/dist/web/coValues/coMap.js +1 -1
- package/dist/web/coValues/coMap.js.map +1 -1
- package/dist/web/coValues/coStream.js +3 -5
- package/dist/web/coValues/coStream.js.map +1 -1
- package/dist/web/coValues/group.js +11 -11
- package/dist/web/coValues/group.js.map +1 -1
- package/dist/web/coreToCoValue.js +2 -2
- package/dist/web/coreToCoValue.js.map +1 -1
- package/dist/web/crypto/PureJSCrypto.js +4 -4
- package/dist/web/crypto/PureJSCrypto.js.map +1 -1
- package/dist/web/crypto/WasmCrypto.js +5 -5
- package/dist/web/crypto/WasmCrypto.js.map +1 -1
- package/dist/web/crypto/crypto.js.map +1 -1
- package/dist/web/exports.js +12 -12
- package/dist/web/exports.js.map +1 -1
- package/dist/web/ids.js.map +1 -1
- package/dist/web/jsonStringify.js.map +1 -1
- package/dist/web/localNode.js +6 -8
- package/dist/web/localNode.js.map +1 -1
- package/dist/web/permissions.js +4 -7
- package/dist/web/permissions.js.map +1 -1
- package/dist/web/priority.js.map +1 -1
- package/dist/web/storage/FileSystem.js.map +1 -1
- package/dist/web/storage/chunksAndKnownStates.js +2 -4
- package/dist/web/storage/chunksAndKnownStates.js.map +1 -1
- package/dist/web/storage/index.js +7 -16
- package/dist/web/storage/index.js.map +1 -1
- package/dist/web/streamUtils.js.map +1 -1
- package/dist/web/sync.js +6 -8
- package/dist/web/sync.js.map +1 -1
- package/dist/web/typeUtils/accountOrAgentIDfromSessionID.js.map +1 -1
- package/dist/web/typeUtils/expectGroup.js.map +1 -1
- package/dist/web/typeUtils/isAccountID.js.map +1 -1
- package/dist/web/typeUtils/isCoValue.js +1 -1
- package/dist/web/typeUtils/isCoValue.js.map +1 -1
- package/package.json +4 -14
- package/src/PeerKnownStates.ts +91 -89
- package/src/PeerState.ts +72 -69
- package/src/PriorityBasedMessageQueue.ts +42 -49
- package/src/base64url.test.ts +24 -24
- package/src/base64url.ts +44 -45
- package/src/coValue.ts +45 -45
- package/src/coValueCore.ts +746 -785
- package/src/coValueState.ts +82 -72
- package/src/coValues/account.ts +143 -150
- package/src/coValues/coList.ts +520 -522
- package/src/coValues/coMap.ts +283 -285
- package/src/coValues/coStream.ts +320 -324
- package/src/coValues/group.ts +306 -305
- package/src/coreToCoValue.ts +28 -31
- package/src/crypto/PureJSCrypto.ts +188 -194
- package/src/crypto/WasmCrypto.ts +236 -254
- package/src/crypto/crypto.ts +302 -309
- package/src/exports.ts +116 -116
- package/src/ids.ts +9 -9
- package/src/jsonStringify.ts +46 -46
- package/src/jsonValue.ts +24 -10
- package/src/localNode.ts +635 -660
- package/src/media.ts +3 -3
- package/src/permissions.ts +272 -278
- package/src/priority.ts +21 -19
- package/src/storage/FileSystem.ts +91 -99
- package/src/storage/chunksAndKnownStates.ts +110 -115
- package/src/storage/index.ts +466 -497
- package/src/streamUtils.ts +60 -60
- package/src/sync.ts +593 -615
- package/src/tests/PeerKnownStates.test.ts +38 -34
- package/src/tests/PeerState.test.ts +101 -64
- package/src/tests/PriorityBasedMessageQueue.test.ts +91 -91
- package/src/tests/account.test.ts +59 -59
- package/src/tests/coList.test.ts +65 -65
- package/src/tests/coMap.test.ts +137 -137
- package/src/tests/coStream.test.ts +254 -257
- package/src/tests/coValueCore.test.ts +153 -156
- package/src/tests/crypto.test.ts +136 -144
- package/src/tests/cryptoImpl.test.ts +205 -197
- package/src/tests/group.test.ts +24 -24
- package/src/tests/permissions.test.ts +1306 -1371
- package/src/tests/priority.test.ts +65 -82
- package/src/tests/sync.test.ts +1300 -1291
- package/src/tests/testUtils.ts +52 -53
- package/src/typeUtils/accountOrAgentIDfromSessionID.ts +4 -4
- package/src/typeUtils/expectGroup.ts +9 -9
- package/src/typeUtils/isAccountID.ts +1 -1
- package/src/typeUtils/isCoValue.ts +9 -9
- package/tsconfig.json +4 -6
- package/tsconfig.native.json +9 -11
- package/tsconfig.web.json +4 -10
- package/.eslintrc.cjs +0 -25
- package/.prettierrc.js +0 -9
package/src/storage/index.ts
CHANGED
|
@@ -1,574 +1,543 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { CoID, RawCoValue } from "../coValue.js";
|
|
2
2
|
import { CoValueHeader, Transaction } from "../coValueCore.js";
|
|
3
3
|
import { Signature } from "../crypto/crypto.js";
|
|
4
|
-
import {
|
|
5
|
-
CoValueKnownState,
|
|
6
|
-
IncomingSyncStream,
|
|
7
|
-
NewContentMessage,
|
|
8
|
-
OutgoingSyncQueue,
|
|
9
|
-
Peer,
|
|
10
|
-
} from "../sync.js";
|
|
11
|
-
import { CoID, RawCoValue } from "../coValue.js";
|
|
4
|
+
import { RawCoID } from "../ids.js";
|
|
12
5
|
import { connectedPeers } from "../streamUtils.js";
|
|
13
6
|
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
7
|
+
CoValueKnownState,
|
|
8
|
+
IncomingSyncStream,
|
|
9
|
+
NewContentMessage,
|
|
10
|
+
OutgoingSyncQueue,
|
|
11
|
+
Peer,
|
|
12
|
+
} from "../sync.js";
|
|
18
13
|
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
14
|
+
BlockFilename,
|
|
15
|
+
FileSystem,
|
|
16
|
+
WalEntry,
|
|
17
|
+
WalFilename,
|
|
18
|
+
readChunk,
|
|
19
|
+
readHeader,
|
|
20
|
+
textDecoder,
|
|
21
|
+
writeBlock,
|
|
22
|
+
writeToWal,
|
|
28
23
|
} from "./FileSystem.js";
|
|
24
|
+
import {
|
|
25
|
+
chunkToKnownState,
|
|
26
|
+
contentSinceChunk,
|
|
27
|
+
mergeChunks,
|
|
28
|
+
} from "./chunksAndKnownStates.js";
|
|
29
29
|
export type { BlockFilename, WalFilename } from "./FileSystem.js";
|
|
30
30
|
|
|
31
31
|
const MAX_N_LEVELS = 3;
|
|
32
32
|
|
|
33
33
|
export type CoValueChunk = {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
34
|
+
header?: CoValueHeader;
|
|
35
|
+
sessionEntries: {
|
|
36
|
+
[sessionID: string]: {
|
|
37
|
+
after: number;
|
|
38
|
+
lastSignature: Signature;
|
|
39
|
+
transactions: Transaction[];
|
|
40
|
+
}[];
|
|
41
|
+
};
|
|
42
42
|
};
|
|
43
43
|
|
|
44
44
|
export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
currentWal: WH | undefined;
|
|
46
|
+
coValues: {
|
|
47
|
+
[id: RawCoID]: CoValueChunk | undefined;
|
|
48
|
+
};
|
|
49
|
+
fileCache: string[] | undefined;
|
|
50
|
+
headerCache = new Map<
|
|
51
|
+
BlockFilename,
|
|
52
|
+
{ [id: RawCoID]: { start: number; length: number } }
|
|
53
|
+
>();
|
|
54
|
+
blockFileHandles = new Map<
|
|
55
|
+
BlockFilename,
|
|
56
|
+
Promise<{ handle: RH; size: number }>
|
|
57
|
+
>();
|
|
58
|
+
|
|
59
|
+
constructor(
|
|
60
|
+
public fs: FS,
|
|
61
|
+
public fromLocalNode: IncomingSyncStream,
|
|
62
|
+
public toLocalNode: OutgoingSyncQueue,
|
|
63
|
+
) {
|
|
64
|
+
this.coValues = {};
|
|
65
|
+
this.currentWal = undefined;
|
|
66
|
+
|
|
67
|
+
let nMsg = 0;
|
|
68
|
+
|
|
69
|
+
const processMessages = async () => {
|
|
70
|
+
for await (const msg of fromLocalNode) {
|
|
71
|
+
console.log("Storage msg start", nMsg);
|
|
72
|
+
try {
|
|
73
|
+
if (msg === "Disconnected" || msg === "PingTimeout") {
|
|
74
|
+
throw new Error("Unexpected Disconnected message");
|
|
75
|
+
}
|
|
76
|
+
if (msg.action === "done") {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (msg.action === "content") {
|
|
81
|
+
await this.handleNewContent(msg);
|
|
82
|
+
} else if (msg.action === "load" || msg.action === "known") {
|
|
83
|
+
await this.sendNewContent(msg.id, msg, undefined);
|
|
84
|
+
}
|
|
85
|
+
} catch (e) {
|
|
86
|
+
console.error(
|
|
87
|
+
new Error(
|
|
88
|
+
`Error reading from localNode, handling msg\n\n${JSON.stringify(
|
|
89
|
+
msg,
|
|
90
|
+
(k, v) =>
|
|
91
|
+
k === "changes" || k === "encryptedChanges"
|
|
92
|
+
? v.slice(0, 20) + "..."
|
|
93
|
+
: v,
|
|
94
|
+
)}`,
|
|
95
|
+
{ cause: e },
|
|
96
|
+
),
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
console.log("Storage msg end", nMsg);
|
|
100
|
+
nMsg++;
|
|
101
|
+
}
|
|
48
102
|
};
|
|
49
|
-
fileCache: string[] | undefined;
|
|
50
|
-
headerCache = new Map<
|
|
51
|
-
BlockFilename,
|
|
52
|
-
{ [id: RawCoID]: { start: number; length: number } }
|
|
53
|
-
>();
|
|
54
|
-
blockFileHandles = new Map<
|
|
55
|
-
BlockFilename,
|
|
56
|
-
Promise<{ handle: RH; size: number }>
|
|
57
|
-
>();
|
|
58
|
-
|
|
59
|
-
constructor(
|
|
60
|
-
public fs: FS,
|
|
61
|
-
public fromLocalNode: IncomingSyncStream,
|
|
62
|
-
public toLocalNode: OutgoingSyncQueue,
|
|
63
|
-
) {
|
|
64
|
-
this.coValues = {};
|
|
65
|
-
this.currentWal = undefined;
|
|
66
|
-
|
|
67
|
-
let nMsg = 0;
|
|
68
|
-
|
|
69
|
-
const processMessages = async () => {
|
|
70
|
-
for await (const msg of fromLocalNode) {
|
|
71
|
-
console.log("Storage msg start", nMsg);
|
|
72
|
-
try {
|
|
73
|
-
if (msg === "Disconnected" || msg === "PingTimeout") {
|
|
74
|
-
throw new Error("Unexpected Disconnected message");
|
|
75
|
-
}
|
|
76
|
-
if (msg.action === "done") {
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (msg.action === "content") {
|
|
81
|
-
await this.handleNewContent(msg);
|
|
82
|
-
} else if (msg.action === 'load' || msg.action === 'known') {
|
|
83
|
-
await this.sendNewContent(msg.id, msg, undefined);
|
|
84
|
-
}
|
|
85
|
-
} catch (e) {
|
|
86
|
-
console.error(
|
|
87
|
-
new Error(
|
|
88
|
-
`Error reading from localNode, handling msg\n\n${JSON.stringify(
|
|
89
|
-
msg,
|
|
90
|
-
(k, v) =>
|
|
91
|
-
k === "changes" || k === "encryptedChanges"
|
|
92
|
-
? v.slice(0, 20) + "..."
|
|
93
|
-
: v,
|
|
94
|
-
)}`,
|
|
95
|
-
{ cause: e },
|
|
96
|
-
),
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
console.log("Storage msg end", nMsg);
|
|
100
|
-
nMsg++;
|
|
101
|
-
}
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
processMessages().catch((e) =>
|
|
105
|
-
console.error("Error in processMessages in storage", e),
|
|
106
|
-
);
|
|
107
103
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
)
|
|
104
|
+
processMessages().catch((e) =>
|
|
105
|
+
console.error("Error in processMessages in storage", e),
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
setTimeout(
|
|
109
|
+
() =>
|
|
110
|
+
this.compact().catch((e) => {
|
|
111
|
+
console.error("Error while compacting", e);
|
|
112
|
+
}),
|
|
113
|
+
20000,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
async sendNewContent(
|
|
118
|
+
id: RawCoID,
|
|
119
|
+
known: CoValueKnownState | undefined,
|
|
120
|
+
asDependencyOf: RawCoID | undefined,
|
|
121
|
+
) {
|
|
122
|
+
let coValue = this.coValues[id];
|
|
123
|
+
|
|
124
|
+
if (!coValue) {
|
|
125
|
+
coValue = await this.loadCoValue(id, this.fs);
|
|
115
126
|
}
|
|
116
127
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
.push({
|
|
131
|
-
id: id,
|
|
132
|
-
action: "known",
|
|
133
|
-
header: false,
|
|
134
|
-
sessions: {},
|
|
135
|
-
asDependencyOf,
|
|
136
|
-
})
|
|
137
|
-
.catch((e) => console.error("Error while pushing known", e));
|
|
138
|
-
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
128
|
+
if (!coValue) {
|
|
129
|
+
this.toLocalNode
|
|
130
|
+
.push({
|
|
131
|
+
id: id,
|
|
132
|
+
action: "known",
|
|
133
|
+
header: false,
|
|
134
|
+
sessions: {},
|
|
135
|
+
asDependencyOf,
|
|
136
|
+
})
|
|
137
|
+
.catch((e) => console.error("Error while pushing known", e));
|
|
138
|
+
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
141
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
change.key.startsWith("co_")
|
|
159
|
-
) {
|
|
160
|
-
dependedOnAccounts.add(change.key);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
142
|
+
if (!known?.header && coValue.header?.ruleset.type === "ownedByGroup") {
|
|
143
|
+
await this.sendNewContent(
|
|
144
|
+
coValue.header.ruleset.group,
|
|
145
|
+
undefined,
|
|
146
|
+
asDependencyOf || id,
|
|
147
|
+
);
|
|
148
|
+
} else if (!known?.header && coValue.header?.ruleset.type === "group") {
|
|
149
|
+
const dependedOnAccounts = new Set();
|
|
150
|
+
for (const session of Object.values(coValue.sessionEntries)) {
|
|
151
|
+
for (const entry of session) {
|
|
152
|
+
for (const tx of entry.transactions) {
|
|
153
|
+
if (tx.privacy === "trusting") {
|
|
154
|
+
const parsedChanges = JSON.parse(tx.changes);
|
|
155
|
+
for (const change of parsedChanges) {
|
|
156
|
+
if (change.op === "set" && change.key.startsWith("co_")) {
|
|
157
|
+
dependedOnAccounts.add(change.key);
|
|
165
158
|
}
|
|
159
|
+
}
|
|
166
160
|
}
|
|
167
|
-
|
|
168
|
-
await this.sendNewContent(
|
|
169
|
-
account as CoID<RawCoValue>,
|
|
170
|
-
undefined,
|
|
171
|
-
asDependencyOf || id,
|
|
172
|
-
);
|
|
173
|
-
}
|
|
161
|
+
}
|
|
174
162
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
163
|
+
}
|
|
164
|
+
for (const account of dependedOnAccounts) {
|
|
165
|
+
await this.sendNewContent(
|
|
166
|
+
account as CoID<RawCoValue>,
|
|
167
|
+
undefined,
|
|
168
|
+
asDependencyOf || id,
|
|
178
169
|
);
|
|
179
|
-
|
|
180
|
-
const ourKnown: CoValueKnownState = chunkToKnownState(id, coValue);
|
|
181
|
-
|
|
182
|
-
this.toLocalNode
|
|
183
|
-
.push({
|
|
184
|
-
action: "known",
|
|
185
|
-
...ourKnown,
|
|
186
|
-
asDependencyOf,
|
|
187
|
-
})
|
|
188
|
-
.catch((e) => console.error("Error while pushing known", e));
|
|
189
|
-
|
|
190
|
-
for (const message of newContentMessages) {
|
|
191
|
-
if (Object.keys(message.new).length === 0) continue;
|
|
192
|
-
this.toLocalNode
|
|
193
|
-
.push(message)
|
|
194
|
-
.catch((e) =>
|
|
195
|
-
console.error("Error while pushing new content", e),
|
|
196
|
-
);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
this.coValues[id] = coValue;
|
|
170
|
+
}
|
|
200
171
|
}
|
|
201
172
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
173
|
+
const newContentMessages = contentSinceChunk(id, coValue, known).map(
|
|
174
|
+
(message) => ({ ...message, asDependencyOf }),
|
|
175
|
+
);
|
|
176
|
+
|
|
177
|
+
const ourKnown: CoValueKnownState = chunkToKnownState(id, coValue);
|
|
178
|
+
|
|
179
|
+
this.toLocalNode
|
|
180
|
+
.push({
|
|
181
|
+
action: "known",
|
|
182
|
+
...ourKnown,
|
|
183
|
+
asDependencyOf,
|
|
184
|
+
})
|
|
185
|
+
.catch((e) => console.error("Error while pushing known", e));
|
|
186
|
+
|
|
187
|
+
for (const message of newContentMessages) {
|
|
188
|
+
if (Object.keys(message.new).length === 0) continue;
|
|
189
|
+
this.toLocalNode
|
|
190
|
+
.push(message)
|
|
191
|
+
.catch((e) => console.error("Error while pushing new content", e));
|
|
211
192
|
}
|
|
212
193
|
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
const newContentAsChunk: CoValueChunk = {
|
|
217
|
-
header: newContent.header,
|
|
218
|
-
sessionEntries: Object.fromEntries(
|
|
219
|
-
Object.entries(newContent.new).map(
|
|
220
|
-
([sessionID, newInSession]) => [
|
|
221
|
-
sessionID,
|
|
222
|
-
[
|
|
223
|
-
{
|
|
224
|
-
after: newInSession.after,
|
|
225
|
-
lastSignature: newInSession.lastSignature,
|
|
226
|
-
transactions: newInSession.newTransactions,
|
|
227
|
-
},
|
|
228
|
-
],
|
|
229
|
-
],
|
|
230
|
-
),
|
|
231
|
-
),
|
|
232
|
-
};
|
|
194
|
+
this.coValues[id] = coValue;
|
|
195
|
+
}
|
|
233
196
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
197
|
+
async withWAL(handler: (wal: WH) => Promise<void>) {
|
|
198
|
+
if (!this.currentWal) {
|
|
199
|
+
this.currentWal = await this.fs.createFile(
|
|
200
|
+
`wal-${Date.now()}-${Math.random().toString(36).slice(2)}.jsonl`,
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
await handler(this.currentWal);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async handleNewContent(newContent: NewContentMessage) {
|
|
207
|
+
const coValue = this.coValues[newContent.id];
|
|
208
|
+
|
|
209
|
+
const newContentAsChunk: CoValueChunk = {
|
|
210
|
+
header: newContent.header,
|
|
211
|
+
sessionEntries: Object.fromEntries(
|
|
212
|
+
Object.entries(newContent.new).map(([sessionID, newInSession]) => [
|
|
213
|
+
sessionID,
|
|
214
|
+
[
|
|
215
|
+
{
|
|
216
|
+
after: newInSession.after,
|
|
217
|
+
lastSignature: newInSession.lastSignature,
|
|
218
|
+
transactions: newInSession.newTransactions,
|
|
219
|
+
},
|
|
220
|
+
],
|
|
221
|
+
]),
|
|
222
|
+
),
|
|
223
|
+
};
|
|
240
224
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
}
|
|
248
|
-
} else {
|
|
249
|
-
const merged = mergeChunks(coValue, newContentAsChunk);
|
|
250
|
-
if (merged === "nonContigous") {
|
|
251
|
-
console.warn(
|
|
252
|
-
"Non-contigous new content for " + newContent.id,
|
|
253
|
-
Object.entries(coValue.sessionEntries).map(
|
|
254
|
-
([session, entries]) =>
|
|
255
|
-
entries.map((entry) => ({
|
|
256
|
-
session: session,
|
|
257
|
-
after: entry.after,
|
|
258
|
-
length: entry.transactions.length,
|
|
259
|
-
})),
|
|
260
|
-
),
|
|
261
|
-
Object.entries(newContentAsChunk.sessionEntries).map(
|
|
262
|
-
([session, entries]) =>
|
|
263
|
-
entries.map((entry) => ({
|
|
264
|
-
session: session,
|
|
265
|
-
after: entry.after,
|
|
266
|
-
length: entry.transactions.length,
|
|
267
|
-
})),
|
|
268
|
-
),
|
|
269
|
-
);
|
|
270
|
-
} else {
|
|
271
|
-
// console.log("Appending to WAL", newContent.id);
|
|
272
|
-
await this.withWAL((wal) =>
|
|
273
|
-
writeToWal(wal, this.fs, newContent.id, newContentAsChunk),
|
|
274
|
-
);
|
|
225
|
+
if (!coValue) {
|
|
226
|
+
if (newContent.header) {
|
|
227
|
+
// console.log("Creating in WAL", newContent.id);
|
|
228
|
+
await this.withWAL((wal) =>
|
|
229
|
+
writeToWal(wal, this.fs, newContent.id, newContentAsChunk),
|
|
230
|
+
);
|
|
275
231
|
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
232
|
+
this.coValues[newContent.id] = newContentAsChunk;
|
|
233
|
+
} else {
|
|
234
|
+
console.warn("Incontiguous incoming update for " + newContent.id);
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
} else {
|
|
238
|
+
const merged = mergeChunks(coValue, newContentAsChunk);
|
|
239
|
+
if (merged === "nonContigous") {
|
|
240
|
+
console.warn(
|
|
241
|
+
"Non-contigous new content for " + newContent.id,
|
|
242
|
+
Object.entries(coValue.sessionEntries).map(([session, entries]) =>
|
|
243
|
+
entries.map((entry) => ({
|
|
244
|
+
session: session,
|
|
245
|
+
after: entry.after,
|
|
246
|
+
length: entry.transactions.length,
|
|
247
|
+
})),
|
|
248
|
+
),
|
|
249
|
+
Object.entries(newContentAsChunk.sessionEntries).map(
|
|
250
|
+
([session, entries]) =>
|
|
251
|
+
entries.map((entry) => ({
|
|
252
|
+
session: session,
|
|
253
|
+
after: entry.after,
|
|
254
|
+
length: entry.transactions.length,
|
|
255
|
+
})),
|
|
256
|
+
),
|
|
257
|
+
);
|
|
258
|
+
} else {
|
|
259
|
+
// console.log("Appending to WAL", newContent.id);
|
|
260
|
+
await this.withWAL((wal) =>
|
|
261
|
+
writeToWal(wal, this.fs, newContent.id, newContentAsChunk),
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
this.coValues[newContent.id] = merged;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
async getBlockHandle(
|
|
270
|
+
blockFile: BlockFilename,
|
|
271
|
+
fs: FS,
|
|
272
|
+
): Promise<{ handle: RH; size: number }> {
|
|
273
|
+
if (!this.blockFileHandles.has(blockFile)) {
|
|
274
|
+
this.blockFileHandles.set(blockFile, fs.openToRead(blockFile));
|
|
279
275
|
}
|
|
280
276
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
fs: FS,
|
|
284
|
-
): Promise<{ handle: RH; size: number }> {
|
|
285
|
-
if (!this.blockFileHandles.has(blockFile)) {
|
|
286
|
-
this.blockFileHandles.set(blockFile, fs.openToRead(blockFile));
|
|
287
|
-
}
|
|
277
|
+
return this.blockFileHandles.get(blockFile)!;
|
|
278
|
+
}
|
|
288
279
|
|
|
289
|
-
|
|
290
|
-
|
|
280
|
+
async loadCoValue(id: RawCoID, fs: FS): Promise<CoValueChunk | undefined> {
|
|
281
|
+
const files = this.fileCache || (await fs.listFiles());
|
|
282
|
+
this.fileCache = files;
|
|
283
|
+
const blockFiles = (
|
|
284
|
+
files.filter((name) => name.startsWith("L")) as BlockFilename[]
|
|
285
|
+
).sort();
|
|
291
286
|
|
|
292
|
-
|
|
293
|
-
const files = this.fileCache || (await fs.listFiles());
|
|
294
|
-
this.fileCache = files;
|
|
295
|
-
const blockFiles = (
|
|
296
|
-
files.filter((name) => name.startsWith("L")) as BlockFilename[]
|
|
297
|
-
).sort();
|
|
287
|
+
let result;
|
|
298
288
|
|
|
299
|
-
|
|
289
|
+
for (const blockFile of blockFiles) {
|
|
290
|
+
let cachedHeader:
|
|
291
|
+
| { [id: RawCoID]: { start: number; length: number } }
|
|
292
|
+
| undefined = this.headerCache.get(blockFile);
|
|
300
293
|
|
|
301
|
-
|
|
302
|
-
let cachedHeader:
|
|
303
|
-
| { [id: RawCoID]: { start: number; length: number } }
|
|
304
|
-
| undefined = this.headerCache.get(blockFile);
|
|
294
|
+
const { handle, size } = await this.getBlockHandle(blockFile, fs);
|
|
305
295
|
|
|
306
|
-
|
|
296
|
+
// console.log("Attempting to load", id, blockFile);
|
|
307
297
|
|
|
308
|
-
|
|
298
|
+
if (!cachedHeader) {
|
|
299
|
+
cachedHeader = {};
|
|
300
|
+
const header = await readHeader(blockFile, handle, size, fs);
|
|
301
|
+
for (const entry of header) {
|
|
302
|
+
cachedHeader[entry.id] = {
|
|
303
|
+
start: entry.start,
|
|
304
|
+
length: entry.length,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
309
307
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
for (const entry of header) {
|
|
314
|
-
cachedHeader[entry.id] = {
|
|
315
|
-
start: entry.start,
|
|
316
|
-
length: entry.length,
|
|
317
|
-
};
|
|
318
|
-
}
|
|
308
|
+
this.headerCache.set(blockFile, cachedHeader);
|
|
309
|
+
}
|
|
310
|
+
const headerEntry = cachedHeader[id];
|
|
319
311
|
|
|
320
|
-
|
|
321
|
-
}
|
|
322
|
-
const headerEntry = cachedHeader[id];
|
|
323
|
-
|
|
324
|
-
// console.log("Header entry", id, headerEntry);
|
|
325
|
-
|
|
326
|
-
if (headerEntry) {
|
|
327
|
-
const nextChunk = await readChunk(handle, headerEntry, fs);
|
|
328
|
-
if (result) {
|
|
329
|
-
const merged = mergeChunks(result, nextChunk);
|
|
330
|
-
|
|
331
|
-
if (merged === "nonContigous") {
|
|
332
|
-
console.warn(
|
|
333
|
-
"Non-contigous chunks while loading " + id,
|
|
334
|
-
result,
|
|
335
|
-
nextChunk,
|
|
336
|
-
);
|
|
337
|
-
} else {
|
|
338
|
-
result = merged;
|
|
339
|
-
}
|
|
340
|
-
} else {
|
|
341
|
-
result = nextChunk;
|
|
342
|
-
}
|
|
343
|
-
}
|
|
312
|
+
// console.log("Header entry", id, headerEntry);
|
|
344
313
|
|
|
345
|
-
|
|
314
|
+
if (headerEntry) {
|
|
315
|
+
const nextChunk = await readChunk(handle, headerEntry, fs);
|
|
316
|
+
if (result) {
|
|
317
|
+
const merged = mergeChunks(result, nextChunk);
|
|
318
|
+
|
|
319
|
+
if (merged === "nonContigous") {
|
|
320
|
+
console.warn(
|
|
321
|
+
"Non-contigous chunks while loading " + id,
|
|
322
|
+
result,
|
|
323
|
+
nextChunk,
|
|
324
|
+
);
|
|
325
|
+
} else {
|
|
326
|
+
result = merged;
|
|
327
|
+
}
|
|
328
|
+
} else {
|
|
329
|
+
result = nextChunk;
|
|
346
330
|
}
|
|
331
|
+
}
|
|
347
332
|
|
|
348
|
-
|
|
333
|
+
// await fs.close(handle);
|
|
349
334
|
}
|
|
350
335
|
|
|
351
|
-
|
|
352
|
-
|
|
336
|
+
return result;
|
|
337
|
+
}
|
|
353
338
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
) as WalFilename[];
|
|
357
|
-
walFiles.sort();
|
|
339
|
+
async compact() {
|
|
340
|
+
const fileNames = await this.fs.listFiles();
|
|
358
341
|
|
|
359
|
-
|
|
342
|
+
const walFiles = fileNames.filter((name) =>
|
|
343
|
+
name.startsWith("wal-"),
|
|
344
|
+
) as WalFilename[];
|
|
345
|
+
walFiles.sort();
|
|
360
346
|
|
|
361
|
-
|
|
362
|
-
if (walFiles.length === 0) return;
|
|
347
|
+
const coValues = new Map<RawCoID, CoValueChunk>();
|
|
363
348
|
|
|
364
|
-
|
|
365
|
-
|
|
349
|
+
console.log("Compacting WAL files", walFiles);
|
|
350
|
+
if (walFiles.length === 0) return;
|
|
351
|
+
|
|
352
|
+
const oldWal = this.currentWal;
|
|
353
|
+
this.currentWal = undefined;
|
|
354
|
+
|
|
355
|
+
if (oldWal) {
|
|
356
|
+
await this.fs.close(oldWal);
|
|
357
|
+
}
|
|
366
358
|
|
|
367
|
-
|
|
368
|
-
|
|
359
|
+
for (const fileName of walFiles) {
|
|
360
|
+
const { handle, size }: { handle: RH; size: number } =
|
|
361
|
+
await this.fs.openToRead(fileName);
|
|
362
|
+
if (size === 0) {
|
|
363
|
+
await this.fs.close(handle);
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
const bytes = await this.fs.read(handle, 0, size);
|
|
367
|
+
|
|
368
|
+
const decoded = textDecoder.decode(bytes);
|
|
369
|
+
const lines = decoded.split("\n");
|
|
370
|
+
|
|
371
|
+
for (const line of lines) {
|
|
372
|
+
if (line.length === 0) continue;
|
|
373
|
+
const chunk = JSON.parse(line) as WalEntry;
|
|
374
|
+
|
|
375
|
+
const existingChunk = coValues.get(chunk.id);
|
|
376
|
+
|
|
377
|
+
if (existingChunk) {
|
|
378
|
+
const merged = mergeChunks(existingChunk, chunk);
|
|
379
|
+
if (merged === "nonContigous") {
|
|
380
|
+
console.log(
|
|
381
|
+
"Non-contigous chunks in " + chunk.id + ", " + fileName,
|
|
382
|
+
existingChunk,
|
|
383
|
+
chunk,
|
|
384
|
+
);
|
|
385
|
+
} else {
|
|
386
|
+
coValues.set(chunk.id, merged);
|
|
387
|
+
}
|
|
388
|
+
} else {
|
|
389
|
+
coValues.set(chunk.id, chunk);
|
|
369
390
|
}
|
|
391
|
+
}
|
|
370
392
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
await this.fs.openToRead(fileName);
|
|
374
|
-
if (size === 0) {
|
|
375
|
-
await this.fs.close(handle);
|
|
376
|
-
continue;
|
|
377
|
-
}
|
|
378
|
-
const bytes = await this.fs.read(handle, 0, size);
|
|
379
|
-
|
|
380
|
-
const decoded = textDecoder.decode(bytes);
|
|
381
|
-
const lines = decoded.split("\n");
|
|
382
|
-
|
|
383
|
-
for (const line of lines) {
|
|
384
|
-
if (line.length === 0) continue;
|
|
385
|
-
const chunk = JSON.parse(line) as WalEntry;
|
|
386
|
-
|
|
387
|
-
const existingChunk = coValues.get(chunk.id);
|
|
388
|
-
|
|
389
|
-
if (existingChunk) {
|
|
390
|
-
const merged = mergeChunks(existingChunk, chunk);
|
|
391
|
-
if (merged === "nonContigous") {
|
|
392
|
-
console.log(
|
|
393
|
-
"Non-contigous chunks in " +
|
|
394
|
-
chunk.id +
|
|
395
|
-
", " +
|
|
396
|
-
fileName,
|
|
397
|
-
existingChunk,
|
|
398
|
-
chunk,
|
|
399
|
-
);
|
|
400
|
-
} else {
|
|
401
|
-
coValues.set(chunk.id, merged);
|
|
402
|
-
}
|
|
403
|
-
} else {
|
|
404
|
-
coValues.set(chunk.id, chunk);
|
|
405
|
-
}
|
|
406
|
-
}
|
|
393
|
+
await this.fs.close(handle);
|
|
394
|
+
}
|
|
407
395
|
|
|
408
|
-
|
|
396
|
+
const highestBlockNumber = fileNames.reduce((acc, name) => {
|
|
397
|
+
if (name.startsWith("L" + MAX_N_LEVELS)) {
|
|
398
|
+
const num = parseInt(name.split("-")[1]!);
|
|
399
|
+
if (num > acc) {
|
|
400
|
+
return num;
|
|
409
401
|
}
|
|
402
|
+
}
|
|
403
|
+
return acc;
|
|
404
|
+
}, 0);
|
|
410
405
|
|
|
411
|
-
|
|
412
|
-
if (name.startsWith("L" + MAX_N_LEVELS)) {
|
|
413
|
-
const num = parseInt(name.split("-")[1]!);
|
|
414
|
-
if (num > acc) {
|
|
415
|
-
return num;
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
return acc;
|
|
419
|
-
}, 0);
|
|
406
|
+
console.log([...coValues.keys()], fileNames, highestBlockNumber);
|
|
420
407
|
|
|
421
|
-
|
|
408
|
+
await writeBlock(coValues, MAX_N_LEVELS, highestBlockNumber + 1, this.fs);
|
|
422
409
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
this.fs,
|
|
428
|
-
);
|
|
410
|
+
for (const walFile of walFiles) {
|
|
411
|
+
await this.fs.removeFile(walFile);
|
|
412
|
+
}
|
|
413
|
+
this.fileCache = undefined;
|
|
429
414
|
|
|
430
|
-
|
|
431
|
-
await this.fs.removeFile(walFile);
|
|
432
|
-
}
|
|
433
|
-
this.fileCache = undefined;
|
|
415
|
+
const fileNames2 = await this.fs.listFiles();
|
|
434
416
|
|
|
435
|
-
|
|
417
|
+
const blockFiles = (
|
|
418
|
+
fileNames2.filter((name) => name.startsWith("L")) as BlockFilename[]
|
|
419
|
+
).sort();
|
|
436
420
|
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
421
|
+
const blockFilesByLevelInOrder: {
|
|
422
|
+
[level: number]: BlockFilename[];
|
|
423
|
+
} = {};
|
|
440
424
|
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
425
|
+
for (const blockFile of blockFiles) {
|
|
426
|
+
const level = parseInt(blockFile.split("-")[0]!.slice(1));
|
|
427
|
+
if (!blockFilesByLevelInOrder[level]) {
|
|
428
|
+
blockFilesByLevelInOrder[level] = [];
|
|
429
|
+
}
|
|
430
|
+
blockFilesByLevelInOrder[level]!.push(blockFile);
|
|
431
|
+
}
|
|
444
432
|
|
|
445
|
-
|
|
446
|
-
const level = parseInt(blockFile.split("-")[0]!.slice(1));
|
|
447
|
-
if (!blockFilesByLevelInOrder[level]) {
|
|
448
|
-
blockFilesByLevelInOrder[level] = [];
|
|
449
|
-
}
|
|
450
|
-
blockFilesByLevelInOrder[level]!.push(blockFile);
|
|
451
|
-
}
|
|
433
|
+
console.log(blockFilesByLevelInOrder);
|
|
452
434
|
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
const nBlocksDesired = Math.pow(2, level);
|
|
457
|
-
const blocksInLevel = blockFilesByLevelInOrder[level];
|
|
458
|
-
|
|
459
|
-
if (blocksInLevel && blocksInLevel.length > nBlocksDesired) {
|
|
460
|
-
console.log("Compacting blocks in level", level, blocksInLevel);
|
|
461
|
-
|
|
462
|
-
const coValues = new Map<RawCoID, CoValueChunk>();
|
|
463
|
-
|
|
464
|
-
for (const blockFile of blocksInLevel) {
|
|
465
|
-
const { handle, size }: { handle: RH; size: number } =
|
|
466
|
-
await this.getBlockHandle(blockFile, this.fs);
|
|
467
|
-
|
|
468
|
-
if (size === 0) {
|
|
469
|
-
continue;
|
|
470
|
-
}
|
|
471
|
-
const header = await readHeader(
|
|
472
|
-
blockFile,
|
|
473
|
-
handle,
|
|
474
|
-
size,
|
|
475
|
-
this.fs,
|
|
476
|
-
);
|
|
477
|
-
for (const entry of header) {
|
|
478
|
-
const chunk = await readChunk(handle, entry, this.fs);
|
|
479
|
-
|
|
480
|
-
const existingChunk = coValues.get(entry.id);
|
|
481
|
-
|
|
482
|
-
if (existingChunk) {
|
|
483
|
-
const merged = mergeChunks(existingChunk, chunk);
|
|
484
|
-
if (merged === "nonContigous") {
|
|
485
|
-
console.log(
|
|
486
|
-
"Non-contigous chunks in " +
|
|
487
|
-
entry.id +
|
|
488
|
-
", " +
|
|
489
|
-
blockFile,
|
|
490
|
-
existingChunk,
|
|
491
|
-
chunk,
|
|
492
|
-
);
|
|
493
|
-
} else {
|
|
494
|
-
coValues.set(entry.id, merged);
|
|
495
|
-
}
|
|
496
|
-
} else {
|
|
497
|
-
coValues.set(entry.id, chunk);
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
}
|
|
435
|
+
for (let level = MAX_N_LEVELS; level > 0; level--) {
|
|
436
|
+
const nBlocksDesired = Math.pow(2, level);
|
|
437
|
+
const blocksInLevel = blockFilesByLevelInOrder[level];
|
|
501
438
|
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
levelBelow = [];
|
|
505
|
-
blockFilesByLevelInOrder[level - 1] = levelBelow;
|
|
506
|
-
}
|
|
439
|
+
if (blocksInLevel && blocksInLevel.length > nBlocksDesired) {
|
|
440
|
+
console.log("Compacting blocks in level", level, blocksInLevel);
|
|
507
441
|
|
|
508
|
-
|
|
509
|
-
(acc, name) => {
|
|
510
|
-
const num = parseInt(name.split("-")[1]!);
|
|
511
|
-
if (num > acc) {
|
|
512
|
-
return num;
|
|
513
|
-
}
|
|
514
|
-
return acc;
|
|
515
|
-
},
|
|
516
|
-
0,
|
|
517
|
-
);
|
|
442
|
+
const coValues = new Map<RawCoID, CoValueChunk>();
|
|
518
443
|
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
444
|
+
for (const blockFile of blocksInLevel) {
|
|
445
|
+
const { handle, size }: { handle: RH; size: number } =
|
|
446
|
+
await this.getBlockHandle(blockFile, this.fs);
|
|
447
|
+
|
|
448
|
+
if (size === 0) {
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
451
|
+
const header = await readHeader(blockFile, handle, size, this.fs);
|
|
452
|
+
for (const entry of header) {
|
|
453
|
+
const chunk = await readChunk(handle, entry, this.fs);
|
|
454
|
+
|
|
455
|
+
const existingChunk = coValues.get(entry.id);
|
|
456
|
+
|
|
457
|
+
if (existingChunk) {
|
|
458
|
+
const merged = mergeChunks(existingChunk, chunk);
|
|
459
|
+
if (merged === "nonContigous") {
|
|
460
|
+
console.log(
|
|
461
|
+
"Non-contigous chunks in " + entry.id + ", " + blockFile,
|
|
462
|
+
existingChunk,
|
|
463
|
+
chunk,
|
|
524
464
|
);
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
blockFile,
|
|
531
|
-
this.fs,
|
|
532
|
-
);
|
|
533
|
-
await this.fs.close(handle.handle);
|
|
534
|
-
await this.fs.removeFile(blockFile);
|
|
535
|
-
this.blockFileHandles.delete(blockFile);
|
|
536
|
-
}
|
|
465
|
+
} else {
|
|
466
|
+
coValues.set(entry.id, merged);
|
|
467
|
+
}
|
|
468
|
+
} else {
|
|
469
|
+
coValues.set(entry.id, chunk);
|
|
537
470
|
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
let levelBelow = blockFilesByLevelInOrder[level - 1];
|
|
475
|
+
if (!levelBelow) {
|
|
476
|
+
levelBelow = [];
|
|
477
|
+
blockFilesByLevelInOrder[level - 1] = levelBelow;
|
|
538
478
|
}
|
|
539
479
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
480
|
+
const highestBlockNumberInLevelBelow = levelBelow.reduce(
|
|
481
|
+
(acc, name) => {
|
|
482
|
+
const num = parseInt(name.split("-")[1]!);
|
|
483
|
+
if (num > acc) {
|
|
484
|
+
return num;
|
|
485
|
+
}
|
|
486
|
+
return acc;
|
|
487
|
+
},
|
|
488
|
+
0,
|
|
546
489
|
);
|
|
490
|
+
|
|
491
|
+
const newBlockName = await writeBlock(
|
|
492
|
+
coValues,
|
|
493
|
+
level - 1,
|
|
494
|
+
highestBlockNumberInLevelBelow + 1,
|
|
495
|
+
this.fs,
|
|
496
|
+
);
|
|
497
|
+
levelBelow.push(newBlockName);
|
|
498
|
+
|
|
499
|
+
// delete blocks that went into this one
|
|
500
|
+
for (const blockFile of blocksInLevel) {
|
|
501
|
+
const handle = await this.getBlockHandle(blockFile, this.fs);
|
|
502
|
+
await this.fs.close(handle.handle);
|
|
503
|
+
await this.fs.removeFile(blockFile);
|
|
504
|
+
this.blockFileHandles.delete(blockFile);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
547
507
|
}
|
|
548
508
|
|
|
549
|
-
|
|
550
|
-
|
|
509
|
+
setTimeout(
|
|
510
|
+
() =>
|
|
511
|
+
this.compact().catch((e) => {
|
|
512
|
+
console.error("Error while compacting", e);
|
|
513
|
+
}),
|
|
514
|
+
5000,
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
static asPeer<WH, RH, FS extends FileSystem<WH, RH>>({
|
|
519
|
+
fs,
|
|
520
|
+
trace,
|
|
521
|
+
localNodeName = "local",
|
|
522
|
+
}: {
|
|
523
|
+
fs: FS;
|
|
524
|
+
trace?: boolean;
|
|
525
|
+
localNodeName?: string;
|
|
526
|
+
}): Peer {
|
|
527
|
+
const [localNodeAsPeer, storageAsPeer] = connectedPeers(
|
|
528
|
+
localNodeName,
|
|
529
|
+
"storage",
|
|
530
|
+
{
|
|
531
|
+
peer1role: "client",
|
|
532
|
+
peer2role: "storage",
|
|
551
533
|
trace,
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
trace?: boolean;
|
|
556
|
-
localNodeName?: string;
|
|
557
|
-
}): Peer {
|
|
558
|
-
const [localNodeAsPeer, storageAsPeer] = connectedPeers(
|
|
559
|
-
localNodeName,
|
|
560
|
-
"storage",
|
|
561
|
-
{
|
|
562
|
-
peer1role: "client",
|
|
563
|
-
peer2role: "server",
|
|
564
|
-
trace,
|
|
565
|
-
crashOnClose: true,
|
|
566
|
-
},
|
|
567
|
-
);
|
|
534
|
+
crashOnClose: true,
|
|
535
|
+
},
|
|
536
|
+
);
|
|
568
537
|
|
|
569
|
-
|
|
538
|
+
new LSMStorage(fs, localNodeAsPeer.incoming, localNodeAsPeer.outgoing);
|
|
570
539
|
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
540
|
+
// return { ...storageAsPeer, priority: 200 };
|
|
541
|
+
return storageAsPeer;
|
|
542
|
+
}
|
|
574
543
|
}
|