cojson 0.13.12 → 0.13.14
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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +12 -0
- package/dist/PeerState.d.ts +4 -6
- package/dist/PeerState.d.ts.map +1 -1
- package/dist/PeerState.js +30 -26
- package/dist/PeerState.js.map +1 -1
- package/dist/PriorityBasedMessageQueue.d.ts +2 -8
- package/dist/PriorityBasedMessageQueue.d.ts.map +1 -1
- package/dist/PriorityBasedMessageQueue.js +1 -17
- package/dist/PriorityBasedMessageQueue.js.map +1 -1
- package/dist/coValueCore.js +1 -1
- package/dist/coValueCore.js.map +1 -1
- package/dist/coValueState.d.ts.map +1 -1
- package/dist/coValueState.js +8 -19
- package/dist/coValueState.js.map +1 -1
- package/dist/localNode.d.ts.map +1 -1
- package/dist/localNode.js +2 -2
- package/dist/localNode.js.map +1 -1
- package/dist/streamUtils.d.ts.map +1 -1
- package/dist/streamUtils.js +1 -1
- package/dist/streamUtils.js.map +1 -1
- package/dist/sync.d.ts +11 -17
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +49 -91
- package/dist/sync.js.map +1 -1
- package/dist/tests/PeerState.test.js +27 -14
- package/dist/tests/PeerState.test.js.map +1 -1
- package/dist/tests/PriorityBasedMessageQueue.test.js +5 -33
- package/dist/tests/PriorityBasedMessageQueue.test.js.map +1 -1
- package/dist/tests/SyncStateManager.test.js +5 -9
- package/dist/tests/SyncStateManager.test.js.map +1 -1
- package/dist/tests/sync.load.test.js +21 -17
- package/dist/tests/sync.load.test.js.map +1 -1
- package/dist/tests/sync.mesh.test.js +46 -18
- package/dist/tests/sync.mesh.test.js.map +1 -1
- package/dist/tests/sync.peerReconciliation.test.js +13 -15
- package/dist/tests/sync.peerReconciliation.test.js.map +1 -1
- package/dist/tests/sync.storage.test.js +13 -9
- package/dist/tests/sync.storage.test.js.map +1 -1
- package/dist/tests/sync.test.js +16 -28
- package/dist/tests/sync.test.js.map +1 -1
- package/dist/tests/sync.upload.test.js +13 -13
- package/dist/tests/testUtils.d.ts +1 -0
- package/dist/tests/testUtils.d.ts.map +1 -1
- package/dist/tests/testUtils.js +1 -0
- package/dist/tests/testUtils.js.map +1 -1
- package/package.json +1 -1
- package/src/PeerState.ts +33 -37
- package/src/PriorityBasedMessageQueue.ts +2 -30
- package/src/coValueCore.ts +1 -1
- package/src/coValueState.ts +13 -21
- package/src/localNode.ts +4 -2
- package/src/streamUtils.ts +2 -2
- package/src/sync.ts +58 -103
- package/src/tests/PeerState.test.ts +28 -14
- package/src/tests/PriorityBasedMessageQueue.test.ts +5 -39
- package/src/tests/SyncStateManager.test.ts +4 -12
- package/src/tests/sync.load.test.ts +21 -17
- package/src/tests/sync.mesh.test.ts +46 -18
- package/src/tests/sync.peerReconciliation.test.ts +13 -25
- package/src/tests/sync.storage.test.ts +13 -9
- package/src/tests/sync.test.ts +16 -30
- package/src/tests/sync.upload.test.ts +13 -13
- package/src/tests/testUtils.ts +1 -0
package/src/PeerState.ts
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
import { PeerKnownStates, ReadonlyPeerKnownStates } from "./PeerKnownStates.js";
|
|
2
|
-
import {
|
|
3
|
-
PriorityBasedMessageQueue,
|
|
4
|
-
QueueEntry,
|
|
5
|
-
} from "./PriorityBasedMessageQueue.js";
|
|
6
|
-
import { TryAddTransactionsError } from "./coValueCore.js";
|
|
2
|
+
import { PriorityBasedMessageQueue } from "./PriorityBasedMessageQueue.js";
|
|
7
3
|
import { RawCoID, SessionID } from "./ids.js";
|
|
8
4
|
import { logger } from "./logger.js";
|
|
9
5
|
import { CO_VALUE_PRIORITY } from "./priority.js";
|
|
@@ -12,9 +8,6 @@ import { CoValueKnownState, Peer, SyncMessage } from "./sync.js";
|
|
|
12
8
|
export class PeerState {
|
|
13
9
|
private queue: PriorityBasedMessageQueue;
|
|
14
10
|
|
|
15
|
-
incomingMessagesProcessingPromise: Promise<void> | undefined;
|
|
16
|
-
nextPeer: Peer | undefined;
|
|
17
|
-
|
|
18
11
|
constructor(
|
|
19
12
|
private peer: Peer,
|
|
20
13
|
knownStates: ReadonlyPeerKnownStates | undefined,
|
|
@@ -156,15 +149,26 @@ export class PeerState {
|
|
|
156
149
|
|
|
157
150
|
this.processing = true;
|
|
158
151
|
|
|
159
|
-
let
|
|
160
|
-
while ((
|
|
152
|
+
let msg: SyncMessage | undefined;
|
|
153
|
+
while ((msg = this.queue.pull())) {
|
|
154
|
+
if (this.closed) {
|
|
155
|
+
break;
|
|
156
|
+
}
|
|
157
|
+
|
|
161
158
|
// Awaiting the push to send one message at a time
|
|
162
159
|
// This way when the peer is "under pressure" we can enqueue all
|
|
163
160
|
// the coming messages and organize them by priority
|
|
164
|
-
|
|
165
|
-
.push(
|
|
166
|
-
|
|
167
|
-
.
|
|
161
|
+
try {
|
|
162
|
+
await this.peer.outgoing.push(msg);
|
|
163
|
+
} catch (e) {
|
|
164
|
+
logger.error("Error sending message", {
|
|
165
|
+
err: e,
|
|
166
|
+
action: msg.action,
|
|
167
|
+
id: msg.id,
|
|
168
|
+
peerId: this.id,
|
|
169
|
+
peerRole: this.role,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
168
172
|
}
|
|
169
173
|
|
|
170
174
|
this.processing = false;
|
|
@@ -172,14 +176,16 @@ export class PeerState {
|
|
|
172
176
|
|
|
173
177
|
pushOutgoingMessage(msg: SyncMessage) {
|
|
174
178
|
if (this.closed) {
|
|
175
|
-
return
|
|
179
|
+
return;
|
|
176
180
|
}
|
|
177
181
|
|
|
178
|
-
|
|
182
|
+
this.queue.push(msg);
|
|
179
183
|
|
|
180
184
|
void this.processQueue();
|
|
185
|
+
}
|
|
181
186
|
|
|
182
|
-
|
|
187
|
+
isProcessing() {
|
|
188
|
+
return this.processing;
|
|
183
189
|
}
|
|
184
190
|
|
|
185
191
|
get incoming() {
|
|
@@ -192,14 +198,6 @@ export class PeerState {
|
|
|
192
198
|
return this.peer.incoming;
|
|
193
199
|
}
|
|
194
200
|
|
|
195
|
-
private closeQueue() {
|
|
196
|
-
let entry: QueueEntry | undefined;
|
|
197
|
-
while ((entry = this.queue.pull())) {
|
|
198
|
-
// Using resolve here to avoid unnecessary noise in the logs
|
|
199
|
-
entry.resolve();
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
201
|
closeListeners = new Set<() => void>();
|
|
204
202
|
|
|
205
203
|
addCloseListener(listener: () => void) {
|
|
@@ -228,40 +226,38 @@ export class PeerState {
|
|
|
228
226
|
peerId: this.id,
|
|
229
227
|
peerRole: this.role,
|
|
230
228
|
});
|
|
231
|
-
this.closeQueue();
|
|
232
229
|
this.peer.outgoing.close();
|
|
233
230
|
this.closed = true;
|
|
234
231
|
this.emitClose();
|
|
235
232
|
}
|
|
236
233
|
|
|
237
|
-
async processIncomingMessages(callback: (msg: SyncMessage) =>
|
|
234
|
+
async processIncomingMessages(callback: (msg: SyncMessage) => void) {
|
|
238
235
|
if (this.closed) {
|
|
239
236
|
throw new Error("Peer is closed");
|
|
240
237
|
}
|
|
241
238
|
|
|
242
|
-
if (this.incomingMessagesProcessingPromise) {
|
|
243
|
-
throw new Error("Incoming messages processing already in progress");
|
|
244
|
-
}
|
|
245
|
-
|
|
246
239
|
const processIncomingMessages = async () => {
|
|
247
240
|
for await (const msg of this.incoming) {
|
|
241
|
+
if (this.closed) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
248
245
|
if (msg === "Disconnected") {
|
|
249
|
-
|
|
246
|
+
return;
|
|
250
247
|
}
|
|
248
|
+
|
|
251
249
|
if (msg === "PingTimeout") {
|
|
252
250
|
logger.error("Ping timeout from peer", {
|
|
253
251
|
peerId: this.id,
|
|
254
252
|
peerRole: this.role,
|
|
255
253
|
});
|
|
256
|
-
|
|
254
|
+
return;
|
|
257
255
|
}
|
|
258
256
|
|
|
259
|
-
|
|
257
|
+
callback(msg);
|
|
260
258
|
}
|
|
261
259
|
};
|
|
262
260
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
return this.incomingMessagesProcessingPromise;
|
|
261
|
+
return processIncomingMessages();
|
|
266
262
|
}
|
|
267
263
|
}
|
|
@@ -2,29 +2,6 @@ import { Counter, ValueType, metrics } from "@opentelemetry/api";
|
|
|
2
2
|
import { CO_VALUE_PRIORITY, type CoValuePriority } from "./priority.js";
|
|
3
3
|
import type { SyncMessage } from "./sync.js";
|
|
4
4
|
|
|
5
|
-
function promiseWithResolvers<R>() {
|
|
6
|
-
let resolve = (_: R) => {};
|
|
7
|
-
let reject = (_: unknown) => {};
|
|
8
|
-
|
|
9
|
-
const promise = new Promise<R>((_resolve, _reject) => {
|
|
10
|
-
resolve = _resolve;
|
|
11
|
-
reject = _reject;
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
return {
|
|
15
|
-
promise,
|
|
16
|
-
resolve,
|
|
17
|
-
reject,
|
|
18
|
-
};
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export type QueueEntry = {
|
|
22
|
-
msg: SyncMessage;
|
|
23
|
-
promise: Promise<void>;
|
|
24
|
-
resolve: () => void;
|
|
25
|
-
reject: (_: unknown) => void;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
5
|
/**
|
|
29
6
|
* Since we have a fixed range of priority values (0-7) we can create a fixed array of queues.
|
|
30
7
|
*/
|
|
@@ -34,7 +11,7 @@ type Tuple<T, N extends number, A extends unknown[] = []> = A extends {
|
|
|
34
11
|
? A
|
|
35
12
|
: Tuple<T, N, [...A, T]>;
|
|
36
13
|
|
|
37
|
-
type QueueTuple = Tuple<LinkedList<
|
|
14
|
+
type QueueTuple = Tuple<LinkedList<SyncMessage>, 3>;
|
|
38
15
|
|
|
39
16
|
type LinkedListNode<T> = {
|
|
40
17
|
value: T;
|
|
@@ -164,14 +141,9 @@ export class PriorityBasedMessageQueue {
|
|
|
164
141
|
}
|
|
165
142
|
|
|
166
143
|
public push(msg: SyncMessage) {
|
|
167
|
-
const { promise, resolve, reject } = promiseWithResolvers<void>();
|
|
168
|
-
const entry: QueueEntry = { msg, promise, resolve, reject };
|
|
169
|
-
|
|
170
144
|
const priority = "priority" in msg ? msg.priority : this.defaultPriority;
|
|
171
145
|
|
|
172
|
-
this.getQueue(priority).push(
|
|
173
|
-
|
|
174
|
-
return promise;
|
|
146
|
+
this.getQueue(priority).push(msg);
|
|
175
147
|
}
|
|
176
148
|
|
|
177
149
|
public pull() {
|
package/src/coValueCore.ts
CHANGED
|
@@ -488,7 +488,7 @@ export class CoValueCore {
|
|
|
488
488
|
|
|
489
489
|
if (success) {
|
|
490
490
|
this.node.syncManager.recordTransactionsSize([transaction], "local");
|
|
491
|
-
void this.node.syncManager.
|
|
491
|
+
void this.node.syncManager.requestCoValueSync(this);
|
|
492
492
|
}
|
|
493
493
|
|
|
494
494
|
return success;
|
package/src/coValueState.ts
CHANGED
|
@@ -44,21 +44,19 @@ export class CoValueState {
|
|
|
44
44
|
get highLevelState() {
|
|
45
45
|
if (this.core) {
|
|
46
46
|
return "available";
|
|
47
|
-
} else {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
.every((p) => p.type === "unavailable" || p.type === "errored")
|
|
54
|
-
) {
|
|
55
|
-
return "unavailable";
|
|
56
|
-
} else if (this.peers.values().some((p) => p.type === "pending")) {
|
|
47
|
+
} else if (this.peers.size === 0) {
|
|
48
|
+
return "unknown";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
for (const peer of this.peers.values()) {
|
|
52
|
+
if (peer.type === "pending") {
|
|
57
53
|
return "loading";
|
|
58
|
-
} else {
|
|
54
|
+
} else if (peer.type === "unknown") {
|
|
59
55
|
return "unknown";
|
|
60
56
|
}
|
|
61
57
|
}
|
|
58
|
+
|
|
59
|
+
return "unavailable";
|
|
62
60
|
}
|
|
63
61
|
|
|
64
62
|
isErroredInPeer(peerId: PeerID) {
|
|
@@ -143,16 +141,10 @@ export class CoValueState {
|
|
|
143
141
|
continue;
|
|
144
142
|
}
|
|
145
143
|
|
|
146
|
-
peer
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
})
|
|
151
|
-
.catch((err) => {
|
|
152
|
-
logger.warn(`Failed to push load message to peer ${peer.id}`, {
|
|
153
|
-
err,
|
|
154
|
-
});
|
|
155
|
-
});
|
|
144
|
+
peer.pushOutgoingMessage({
|
|
145
|
+
action: "load",
|
|
146
|
+
...(this.core ? this.core.knownState() : emptyKnownState(this.id)),
|
|
147
|
+
});
|
|
156
148
|
|
|
157
149
|
/**
|
|
158
150
|
* Use a very long timeout for storage peers, because under pressure
|
package/src/localNode.ts
CHANGED
|
@@ -140,7 +140,9 @@ export class LocalNode {
|
|
|
140
140
|
function syncAllCoValuesAfterCreateAccount() {
|
|
141
141
|
for (const coValueEntry of nodeWithAccount.coValuesStore.getValues()) {
|
|
142
142
|
if (coValueEntry.isAvailable()) {
|
|
143
|
-
void nodeWithAccount.syncManager.
|
|
143
|
+
void nodeWithAccount.syncManager.requestCoValueSync(
|
|
144
|
+
coValueEntry.core,
|
|
145
|
+
);
|
|
144
146
|
}
|
|
145
147
|
}
|
|
146
148
|
}
|
|
@@ -248,7 +250,7 @@ export class LocalNode {
|
|
|
248
250
|
const coValue = new CoValueCore(header, this);
|
|
249
251
|
this.coValuesStore.internalMarkMagicallyAvailable(coValue.id, coValue);
|
|
250
252
|
|
|
251
|
-
void this.syncManager.
|
|
253
|
+
void this.syncManager.requestCoValueSync(coValue);
|
|
252
254
|
|
|
253
255
|
return coValue;
|
|
254
256
|
}
|
package/src/streamUtils.ts
CHANGED
package/src/sync.ts
CHANGED
|
@@ -78,7 +78,7 @@ export interface Peer {
|
|
|
78
78
|
id: PeerID;
|
|
79
79
|
incoming: IncomingSyncStream;
|
|
80
80
|
outgoing: OutgoingSyncQueue;
|
|
81
|
-
role: "
|
|
81
|
+
role: "server" | "client" | "storage";
|
|
82
82
|
priority?: number;
|
|
83
83
|
crashOnClose: boolean;
|
|
84
84
|
deletePeerStateOnClose?: boolean;
|
|
@@ -112,11 +112,6 @@ export function combinedKnownStates(
|
|
|
112
112
|
export class SyncManager {
|
|
113
113
|
peers: { [key: PeerID]: PeerState } = {};
|
|
114
114
|
local: LocalNode;
|
|
115
|
-
requestedSyncs: {
|
|
116
|
-
[id: RawCoID]:
|
|
117
|
-
| { done: Promise<void>; nRequestsThisTick: number }
|
|
118
|
-
| undefined;
|
|
119
|
-
} = {};
|
|
120
115
|
|
|
121
116
|
peersCounter = metrics.getMeter("cojson").createUpDownCounter("jazz.peers", {
|
|
122
117
|
description: "Amount of connected peers",
|
|
@@ -162,7 +157,7 @@ export class SyncManager {
|
|
|
162
157
|
);
|
|
163
158
|
}
|
|
164
159
|
|
|
165
|
-
|
|
160
|
+
handleSyncMessage(msg: SyncMessage, peer: PeerState) {
|
|
166
161
|
if (this.local.coValuesStore.get(msg.id).isErroredInPeer(peer.id)) {
|
|
167
162
|
logger.warn(
|
|
168
163
|
`Skipping message ${msg.action} on errored coValue ${msg.id} from peer ${peer.id}`,
|
|
@@ -183,18 +178,17 @@ export class SyncManager {
|
|
|
183
178
|
// TODO: validate
|
|
184
179
|
switch (msg.action) {
|
|
185
180
|
case "load":
|
|
186
|
-
return
|
|
181
|
+
return this.handleLoad(msg, peer);
|
|
187
182
|
case "known":
|
|
188
183
|
if (msg.isCorrection) {
|
|
189
|
-
return
|
|
184
|
+
return this.handleCorrection(msg, peer);
|
|
190
185
|
} else {
|
|
191
|
-
return
|
|
186
|
+
return this.handleKnownState(msg, peer);
|
|
192
187
|
}
|
|
193
188
|
case "content":
|
|
194
|
-
|
|
195
|
-
return await this.handleNewContent(msg, peer);
|
|
189
|
+
return this.handleNewContent(msg, peer);
|
|
196
190
|
case "done":
|
|
197
|
-
return
|
|
191
|
+
return this.handleUnsubscribe(msg);
|
|
198
192
|
default:
|
|
199
193
|
throw new Error(
|
|
200
194
|
`Unknown message type ${(msg as { action: "string" }).action}`,
|
|
@@ -202,14 +196,12 @@ export class SyncManager {
|
|
|
202
196
|
}
|
|
203
197
|
}
|
|
204
198
|
|
|
205
|
-
|
|
199
|
+
sendNewContentIncludingDependencies(id: RawCoID, peer: PeerState) {
|
|
206
200
|
const coValue = this.local.expectCoValueLoaded(id);
|
|
207
201
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
.map((id) => this.sendNewContentIncludingDependencies(id, peer)),
|
|
212
|
-
);
|
|
202
|
+
coValue
|
|
203
|
+
.getDependedOnCoValues()
|
|
204
|
+
.map((id) => this.sendNewContentIncludingDependencies(id, peer));
|
|
213
205
|
|
|
214
206
|
const newContentPieces = coValue.newContentSince(
|
|
215
207
|
peer.optimisticKnownStates.get(id),
|
|
@@ -217,9 +209,7 @@ export class SyncManager {
|
|
|
217
209
|
|
|
218
210
|
if (newContentPieces) {
|
|
219
211
|
for (const piece of newContentPieces) {
|
|
220
|
-
this.trySendToPeer(peer, piece)
|
|
221
|
-
logger.error("Error sending content piece", { err: e });
|
|
222
|
-
});
|
|
212
|
+
this.trySendToPeer(peer, piece);
|
|
223
213
|
}
|
|
224
214
|
|
|
225
215
|
peer.toldKnownState.add(id);
|
|
@@ -228,15 +218,13 @@ export class SyncManager {
|
|
|
228
218
|
this.trySendToPeer(peer, {
|
|
229
219
|
action: "known",
|
|
230
220
|
...coValue.knownState(),
|
|
231
|
-
}).catch((e: unknown) => {
|
|
232
|
-
logger.error("Error sending known state", { err: e });
|
|
233
221
|
});
|
|
234
222
|
|
|
235
223
|
peer.toldKnownState.add(id);
|
|
236
224
|
}
|
|
237
225
|
}
|
|
238
226
|
|
|
239
|
-
|
|
227
|
+
startPeerReconciliation(peer: PeerState) {
|
|
240
228
|
const coValuesOrderedByDependency: CoValueCore[] = [];
|
|
241
229
|
|
|
242
230
|
const gathered = new Set<string>();
|
|
@@ -264,8 +252,12 @@ export class SyncManager {
|
|
|
264
252
|
// If the coValue is unavailable and we never tried this peer
|
|
265
253
|
// we try to load it from the peer
|
|
266
254
|
if (!peer.toldKnownState.has(entry.id)) {
|
|
267
|
-
|
|
268
|
-
|
|
255
|
+
peer.toldKnownState.add(entry.id);
|
|
256
|
+
this.trySendToPeer(peer, {
|
|
257
|
+
action: "load",
|
|
258
|
+
header: false,
|
|
259
|
+
id: entry.id,
|
|
260
|
+
sessions: {},
|
|
269
261
|
});
|
|
270
262
|
}
|
|
271
263
|
} else {
|
|
@@ -293,33 +285,15 @@ export class SyncManager {
|
|
|
293
285
|
this.trySendToPeer(peer, {
|
|
294
286
|
action: "load",
|
|
295
287
|
...coValue.knownState(),
|
|
296
|
-
}).catch((e: unknown) => {
|
|
297
|
-
logger.error("Error sending load", { err: e });
|
|
298
288
|
});
|
|
299
289
|
}
|
|
300
290
|
}
|
|
301
291
|
|
|
302
|
-
nextPeer: Map<PeerID, Peer> = new Map();
|
|
303
|
-
|
|
304
292
|
async addPeer(peer: Peer) {
|
|
305
293
|
const prevPeer = this.peers[peer.id];
|
|
306
294
|
|
|
307
|
-
if (prevPeer) {
|
|
308
|
-
|
|
309
|
-
prevPeer.nextPeer = peer;
|
|
310
|
-
|
|
311
|
-
if (!prevPeer.closed) {
|
|
312
|
-
prevPeer.gracefulShutdown();
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
// Wait for the previous peer to finish processing the incoming messages
|
|
316
|
-
await prevPeer.incomingMessagesProcessingPromise?.catch((e) => {});
|
|
317
|
-
|
|
318
|
-
// If another peer was added in the meantime, we close this peer
|
|
319
|
-
if (prevPeer.nextPeer !== peer) {
|
|
320
|
-
peer.outgoing.close();
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
295
|
+
if (prevPeer && !prevPeer.closed) {
|
|
296
|
+
prevPeer.gracefulShutdown();
|
|
323
297
|
}
|
|
324
298
|
|
|
325
299
|
const peerState = new PeerState(peer, prevPeer?.knownStates);
|
|
@@ -338,8 +312,8 @@ export class SyncManager {
|
|
|
338
312
|
}
|
|
339
313
|
|
|
340
314
|
peerState
|
|
341
|
-
.processIncomingMessages(
|
|
342
|
-
|
|
315
|
+
.processIncomingMessages((msg) => {
|
|
316
|
+
this.handleSyncMessage(msg, peerState);
|
|
343
317
|
})
|
|
344
318
|
.then(() => {
|
|
345
319
|
if (peer.crashOnClose) {
|
|
@@ -386,7 +360,7 @@ export class SyncManager {
|
|
|
386
360
|
* - The peer known state is stored as-is instead of being merged
|
|
387
361
|
* - The load message always replies with a known state message
|
|
388
362
|
*/
|
|
389
|
-
|
|
363
|
+
handleLoad(msg: LoadMessage, peer: PeerState) {
|
|
390
364
|
/**
|
|
391
365
|
* We use the msg sessions as source of truth for the known states
|
|
392
366
|
*
|
|
@@ -413,8 +387,6 @@ export class SyncManager {
|
|
|
413
387
|
id: msg.id,
|
|
414
388
|
header: false,
|
|
415
389
|
sessions: {},
|
|
416
|
-
}).catch((e) => {
|
|
417
|
-
logger.error("Error sending known state back", { err: e });
|
|
418
390
|
});
|
|
419
391
|
|
|
420
392
|
return;
|
|
@@ -442,14 +414,12 @@ export class SyncManager {
|
|
|
442
414
|
id: msg.id,
|
|
443
415
|
header: false,
|
|
444
416
|
sessions: {},
|
|
445
|
-
}).catch((e) => {
|
|
446
|
-
logger.error("Error sending known state back", { err: e });
|
|
447
417
|
});
|
|
448
418
|
|
|
449
419
|
return;
|
|
450
420
|
}
|
|
451
421
|
|
|
452
|
-
|
|
422
|
+
this.sendNewContentIncludingDependencies(msg.id, peer);
|
|
453
423
|
})
|
|
454
424
|
.catch((e) => {
|
|
455
425
|
logger.error("Error loading coValue in handleLoad loading state", {
|
|
@@ -457,7 +427,7 @@ export class SyncManager {
|
|
|
457
427
|
});
|
|
458
428
|
});
|
|
459
429
|
} else if (entry.isAvailable()) {
|
|
460
|
-
|
|
430
|
+
this.sendNewContentIncludingDependencies(msg.id, peer);
|
|
461
431
|
} else {
|
|
462
432
|
this.trySendToPeer(peer, {
|
|
463
433
|
action: "known",
|
|
@@ -468,7 +438,7 @@ export class SyncManager {
|
|
|
468
438
|
}
|
|
469
439
|
}
|
|
470
440
|
|
|
471
|
-
|
|
441
|
+
handleKnownState(msg: KnownStateMessage, peer: PeerState) {
|
|
472
442
|
const entry = this.local.coValuesStore.get(msg.id);
|
|
473
443
|
|
|
474
444
|
peer.combineWith(msg.id, knownStateIn(msg));
|
|
@@ -482,7 +452,7 @@ export class SyncManager {
|
|
|
482
452
|
}
|
|
483
453
|
|
|
484
454
|
if (entry.isAvailable()) {
|
|
485
|
-
|
|
455
|
+
this.sendNewContentIncludingDependencies(msg.id, peer);
|
|
486
456
|
}
|
|
487
457
|
}
|
|
488
458
|
|
|
@@ -499,7 +469,7 @@ export class SyncManager {
|
|
|
499
469
|
}
|
|
500
470
|
}
|
|
501
471
|
|
|
502
|
-
|
|
472
|
+
handleNewContent(msg: NewContentMessage, peer: PeerState) {
|
|
503
473
|
const entry = this.local.coValuesStore.get(msg.id);
|
|
504
474
|
|
|
505
475
|
let coValue: CoValueCore;
|
|
@@ -512,12 +482,6 @@ export class SyncManager {
|
|
|
512
482
|
id: msg.id,
|
|
513
483
|
header: false,
|
|
514
484
|
sessions: {},
|
|
515
|
-
}).catch((e) => {
|
|
516
|
-
logger.error("Error sending known state correction", {
|
|
517
|
-
peerId: peer.id,
|
|
518
|
-
peerRole: peer.role,
|
|
519
|
-
err: e,
|
|
520
|
-
});
|
|
521
485
|
});
|
|
522
486
|
return;
|
|
523
487
|
}
|
|
@@ -590,12 +554,6 @@ export class SyncManager {
|
|
|
590
554
|
action: "known",
|
|
591
555
|
isCorrection: true,
|
|
592
556
|
...coValue.knownState(),
|
|
593
|
-
}).catch((e) => {
|
|
594
|
-
logger.error("Error sending known state correction", {
|
|
595
|
-
peerId: peer.id,
|
|
596
|
-
peerRole: peer.role,
|
|
597
|
-
err: e,
|
|
598
|
-
});
|
|
599
557
|
});
|
|
600
558
|
peer.toldKnownState.add(msg.id);
|
|
601
559
|
} else {
|
|
@@ -609,12 +567,6 @@ export class SyncManager {
|
|
|
609
567
|
this.trySendToPeer(peer, {
|
|
610
568
|
action: "known",
|
|
611
569
|
...coValue.knownState(),
|
|
612
|
-
}).catch((e: unknown) => {
|
|
613
|
-
logger.error("Error sending known state", {
|
|
614
|
-
peerId: peer.id,
|
|
615
|
-
peerRole: peer.role,
|
|
616
|
-
err: e,
|
|
617
|
-
});
|
|
618
570
|
});
|
|
619
571
|
peer.toldKnownState.add(msg.id);
|
|
620
572
|
}
|
|
@@ -624,51 +576,54 @@ export class SyncManager {
|
|
|
624
576
|
* response to the peers that are waiting for confirmation that a coValue is
|
|
625
577
|
* fully synced
|
|
626
578
|
*/
|
|
627
|
-
this.
|
|
579
|
+
this.requestCoValueSync(coValue);
|
|
628
580
|
}
|
|
629
581
|
|
|
630
|
-
|
|
582
|
+
handleCorrection(msg: KnownStateMessage, peer: PeerState) {
|
|
631
583
|
peer.setKnownState(msg.id, knownStateIn(msg));
|
|
632
584
|
|
|
633
585
|
return this.sendNewContentIncludingDependencies(msg.id, peer);
|
|
634
586
|
}
|
|
635
587
|
|
|
636
|
-
handleUnsubscribe(_msg: DoneMessage) {
|
|
637
|
-
throw new Error("Method not implemented.");
|
|
638
|
-
}
|
|
588
|
+
handleUnsubscribe(_msg: DoneMessage) {}
|
|
639
589
|
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
590
|
+
requestedSyncs = new Map<RawCoID, Promise<void>>();
|
|
591
|
+
|
|
592
|
+
async requestCoValueSync(coValue: CoValueCore) {
|
|
593
|
+
const promise = this.requestedSyncs.get(coValue.id);
|
|
594
|
+
|
|
595
|
+
if (promise) {
|
|
596
|
+
return promise;
|
|
644
597
|
} else {
|
|
645
|
-
const
|
|
646
|
-
queueMicrotask(
|
|
647
|
-
|
|
648
|
-
|
|
598
|
+
const promise = new Promise<void>((resolve) => {
|
|
599
|
+
queueMicrotask(() => {
|
|
600
|
+
this.requestedSyncs.delete(coValue.id);
|
|
601
|
+
this.syncCoValue(coValue);
|
|
649
602
|
resolve();
|
|
650
603
|
});
|
|
651
604
|
});
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
};
|
|
656
|
-
this.requestedSyncs[coValue.id] = entry;
|
|
657
|
-
return done;
|
|
605
|
+
|
|
606
|
+
this.requestedSyncs.set(coValue.id, promise);
|
|
607
|
+
return promise;
|
|
658
608
|
}
|
|
659
609
|
}
|
|
660
610
|
|
|
661
|
-
async
|
|
611
|
+
async syncCoValue(coValue: CoValueCore) {
|
|
612
|
+
const entry = this.local.coValuesStore.get(coValue.id);
|
|
613
|
+
|
|
662
614
|
for (const peer of this.peersInPriorityOrder()) {
|
|
663
615
|
if (peer.closed) continue;
|
|
664
|
-
if (
|
|
665
|
-
continue;
|
|
616
|
+
if (entry.isErroredInPeer(peer.id)) continue;
|
|
666
617
|
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
618
|
+
// Only subscribed CoValues are synced to clients
|
|
619
|
+
if (
|
|
620
|
+
peer.role === "client" &&
|
|
621
|
+
!peer.optimisticKnownStates.has(coValue.id)
|
|
622
|
+
) {
|
|
623
|
+
continue;
|
|
671
624
|
}
|
|
625
|
+
|
|
626
|
+
this.sendNewContentIncludingDependencies(coValue.id, peer);
|
|
672
627
|
}
|
|
673
628
|
|
|
674
629
|
for (const peer of this.getPeers()) {
|