cojson 0.13.11 → 0.13.13
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/CoValuesStore.d.ts +3 -1
- package/dist/CoValuesStore.d.ts.map +1 -1
- package/dist/CoValuesStore.js +7 -6
- package/dist/CoValuesStore.js.map +1 -1
- package/dist/PeerState.d.ts +0 -2
- package/dist/PeerState.d.ts.map +1 -1
- package/dist/PeerState.js +0 -1
- package/dist/PeerState.js.map +1 -1
- package/dist/SyncStateManager.js +2 -2
- package/dist/SyncStateManager.js.map +1 -1
- package/dist/coValueCore.js +2 -2
- package/dist/coValueCore.js.map +1 -1
- package/dist/coValueState.d.ts +21 -46
- package/dist/coValueState.d.ts.map +1 -1
- package/dist/coValueState.js +170 -246
- package/dist/coValueState.js.map +1 -1
- package/dist/coValues/group.js +2 -2
- package/dist/coValues/group.js.map +1 -1
- package/dist/exports.d.ts +2 -4
- package/dist/exports.d.ts.map +1 -1
- package/dist/exports.js +1 -2
- package/dist/exports.js.map +1 -1
- package/dist/localNode.d.ts.map +1 -1
- package/dist/localNode.js +20 -16
- package/dist/localNode.js.map +1 -1
- package/dist/sync.d.ts.map +1 -1
- package/dist/sync.js +32 -41
- package/dist/sync.js.map +1 -1
- package/dist/tests/coValueState.test.js +57 -104
- package/dist/tests/coValueState.test.js.map +1 -1
- package/dist/tests/group.test.js +1 -2
- package/dist/tests/group.test.js.map +1 -1
- package/dist/tests/messagesTestUtils.d.ts +4 -1
- package/dist/tests/messagesTestUtils.d.ts.map +1 -1
- package/dist/tests/messagesTestUtils.js +10 -0
- package/dist/tests/messagesTestUtils.js.map +1 -1
- package/dist/tests/sync.peerReconciliation.test.js +8 -8
- package/dist/tests/sync.peerReconciliation.test.js.map +1 -1
- package/dist/tests/sync.test.js +6 -4
- package/dist/tests/sync.test.js.map +1 -1
- package/package.json +1 -1
- package/src/CoValuesStore.ts +9 -6
- package/src/PeerState.ts +0 -2
- package/src/SyncStateManager.ts +2 -2
- package/src/coValueCore.ts +2 -2
- package/src/coValueState.ts +194 -316
- package/src/coValues/group.ts +2 -2
- package/src/exports.ts +0 -6
- package/src/localNode.ts +30 -21
- package/src/sync.ts +35 -43
- package/src/tests/coValueState.test.ts +55 -106
- package/src/tests/group.test.ts +2 -2
- package/src/tests/messagesTestUtils.ts +12 -1
- package/src/tests/sync.peerReconciliation.test.ts +8 -8
- package/src/tests/sync.test.ts +8 -23
- package/dist/storage/FileSystem.d.ts +0 -37
- package/dist/storage/FileSystem.d.ts.map +0 -1
- package/dist/storage/FileSystem.js +0 -48
- package/dist/storage/FileSystem.js.map +0 -1
- package/dist/storage/chunksAndKnownStates.d.ts +0 -7
- package/dist/storage/chunksAndKnownStates.d.ts.map +0 -1
- package/dist/storage/chunksAndKnownStates.js +0 -98
- package/dist/storage/chunksAndKnownStates.js.map +0 -1
- package/dist/storage/index.d.ts +0 -52
- package/dist/storage/index.d.ts.map +0 -1
- package/dist/storage/index.js +0 -335
- package/dist/storage/index.js.map +0 -1
- package/src/storage/FileSystem.ts +0 -113
- package/src/storage/chunksAndKnownStates.ts +0 -137
- package/src/storage/index.ts +0 -531
package/src/coValueState.ts
CHANGED
|
@@ -1,249 +1,205 @@
|
|
|
1
1
|
import { ValueType } from "@opentelemetry/api";
|
|
2
2
|
import { UpDownCounter, metrics } from "@opentelemetry/api";
|
|
3
3
|
import { PeerState } from "./PeerState.js";
|
|
4
|
-
import { CoValueCore } from "./coValueCore.js";
|
|
4
|
+
import { CoValueCore, TryAddTransactionsError } from "./coValueCore.js";
|
|
5
5
|
import { RawCoID } from "./ids.js";
|
|
6
6
|
import { logger } from "./logger.js";
|
|
7
|
-
import { PeerID } from "./sync.js";
|
|
7
|
+
import { PeerID, emptyKnownState } from "./sync.js";
|
|
8
8
|
|
|
9
9
|
export const CO_VALUE_LOADING_CONFIG = {
|
|
10
10
|
MAX_RETRIES: 2,
|
|
11
11
|
TIMEOUT: 30_000,
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
-
export class
|
|
15
|
-
type = "unknown" as const;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export class CoValueLoadingState {
|
|
19
|
-
type = "loading" as const;
|
|
14
|
+
export class CoValueState {
|
|
20
15
|
private peers = new Map<
|
|
21
16
|
PeerID,
|
|
22
|
-
|
|
17
|
+
| { type: "unknown" | "pending" | "available" | "unavailable" }
|
|
18
|
+
| {
|
|
19
|
+
type: "errored";
|
|
20
|
+
error: TryAddTransactionsError;
|
|
21
|
+
}
|
|
23
22
|
>();
|
|
24
|
-
private resolveResult: (value: CoValueCore | "unavailable") => void;
|
|
25
|
-
|
|
26
|
-
result: Promise<CoValueCore | "unavailable">;
|
|
27
|
-
|
|
28
|
-
constructor(peersIds: Iterable<PeerID>) {
|
|
29
|
-
this.peers = new Map();
|
|
30
|
-
|
|
31
|
-
for (const peerId of peersIds) {
|
|
32
|
-
this.peers.set(peerId, createResolvablePromise<void>());
|
|
33
|
-
}
|
|
34
23
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
>();
|
|
24
|
+
core: CoValueCore | null = null;
|
|
25
|
+
id: RawCoID;
|
|
38
26
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
markAsUnavailable(peerId: PeerID) {
|
|
44
|
-
const entry = this.peers.get(peerId);
|
|
27
|
+
private listeners: Set<(state: CoValueState) => void> = new Set();
|
|
28
|
+
private counter: UpDownCounter;
|
|
45
29
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
30
|
+
constructor(id: RawCoID) {
|
|
31
|
+
this.id = id;
|
|
49
32
|
|
|
50
|
-
this.
|
|
33
|
+
this.counter = metrics
|
|
34
|
+
.getMeter("cojson")
|
|
35
|
+
.createUpDownCounter("jazz.covalues.loaded", {
|
|
36
|
+
description: "The number of covalues in the system",
|
|
37
|
+
unit: "covalue",
|
|
38
|
+
valueType: ValueType.INT,
|
|
39
|
+
});
|
|
51
40
|
|
|
52
|
-
|
|
53
|
-
if (this.peers.size === 0) {
|
|
54
|
-
this.resolve("unavailable");
|
|
55
|
-
}
|
|
41
|
+
this.updateCounter(null);
|
|
56
42
|
}
|
|
57
43
|
|
|
58
|
-
|
|
59
|
-
this.
|
|
60
|
-
|
|
61
|
-
|
|
44
|
+
get highLevelState() {
|
|
45
|
+
if (this.core) {
|
|
46
|
+
return "available";
|
|
47
|
+
} else if (this.peers.size === 0) {
|
|
48
|
+
return "unknown";
|
|
62
49
|
}
|
|
63
|
-
this.peers.clear();
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Wait for a specific peer to have a known state
|
|
67
|
-
waitForPeer(peerId: PeerID) {
|
|
68
|
-
const entry = this.peers.get(peerId);
|
|
69
50
|
|
|
70
|
-
|
|
71
|
-
|
|
51
|
+
for (const peer of this.peers.values()) {
|
|
52
|
+
if (peer.type === "pending") {
|
|
53
|
+
return "loading";
|
|
54
|
+
} else if (peer.type === "unknown") {
|
|
55
|
+
return "unknown";
|
|
56
|
+
}
|
|
72
57
|
}
|
|
73
58
|
|
|
74
|
-
return
|
|
59
|
+
return "unavailable";
|
|
75
60
|
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export class CoValueAvailableState {
|
|
79
|
-
type = "available" as const;
|
|
80
|
-
|
|
81
|
-
constructor(public coValue: CoValueCore) {}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export class CoValueUnavailableState {
|
|
85
|
-
type = "unavailable" as const;
|
|
86
|
-
}
|
|
87
61
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
type: "load-requested";
|
|
91
|
-
peersIds: PeerID[];
|
|
92
|
-
}
|
|
93
|
-
| {
|
|
94
|
-
type: "not-found-in-peer";
|
|
95
|
-
peerId: PeerID;
|
|
96
|
-
}
|
|
97
|
-
| {
|
|
98
|
-
type: "available";
|
|
99
|
-
coValue: CoValueCore;
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
type CoValueStateType =
|
|
103
|
-
| CoValueUnknownState
|
|
104
|
-
| CoValueLoadingState
|
|
105
|
-
| CoValueAvailableState
|
|
106
|
-
| CoValueUnavailableState;
|
|
107
|
-
|
|
108
|
-
export class CoValueState {
|
|
109
|
-
promise?: Promise<CoValueCore | "unavailable">;
|
|
110
|
-
private resolve?: (value: CoValueCore | "unavailable") => void;
|
|
111
|
-
private counter: UpDownCounter;
|
|
112
|
-
|
|
113
|
-
constructor(
|
|
114
|
-
public id: RawCoID,
|
|
115
|
-
public state: CoValueStateType,
|
|
116
|
-
) {
|
|
117
|
-
this.counter = metrics
|
|
118
|
-
.getMeter("cojson")
|
|
119
|
-
.createUpDownCounter("jazz.covalues.loaded", {
|
|
120
|
-
description: "The number of covalues in the system",
|
|
121
|
-
unit: "covalue",
|
|
122
|
-
valueType: ValueType.INT,
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
this.counter.add(1, {
|
|
126
|
-
state: this.state.type,
|
|
127
|
-
});
|
|
62
|
+
isErroredInPeer(peerId: PeerID) {
|
|
63
|
+
return this.peers.get(peerId)?.type === "errored";
|
|
128
64
|
}
|
|
129
65
|
|
|
130
|
-
|
|
131
|
-
return
|
|
66
|
+
isAvailable(): this is { type: "available"; core: CoValueCore } {
|
|
67
|
+
return !!this.core;
|
|
132
68
|
}
|
|
133
69
|
|
|
134
|
-
|
|
135
|
-
|
|
70
|
+
addListener(listener: (state: CoValueState) => void) {
|
|
71
|
+
this.listeners.add(listener);
|
|
72
|
+
listener(this);
|
|
136
73
|
}
|
|
137
74
|
|
|
138
|
-
|
|
139
|
-
|
|
75
|
+
removeListener(listener: (state: CoValueState) => void) {
|
|
76
|
+
this.listeners.delete(listener);
|
|
140
77
|
}
|
|
141
78
|
|
|
142
|
-
|
|
143
|
-
|
|
79
|
+
private notifyListeners() {
|
|
80
|
+
for (const listener of this.listeners) {
|
|
81
|
+
listener(this);
|
|
82
|
+
}
|
|
144
83
|
}
|
|
145
84
|
|
|
146
85
|
async getCoValue() {
|
|
147
|
-
if (this.
|
|
148
|
-
return this.state.coValue;
|
|
149
|
-
}
|
|
150
|
-
if (this.state.type === "unavailable") {
|
|
86
|
+
if (this.highLevelState === "unavailable") {
|
|
151
87
|
return "unavailable";
|
|
152
88
|
}
|
|
153
89
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
this.promise = promise;
|
|
162
|
-
this.resolve = resolve;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
return this.promise;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
private moveToState(value: CoValueStateType) {
|
|
169
|
-
this.counter.add(-1, {
|
|
170
|
-
state: this.state.type,
|
|
171
|
-
});
|
|
172
|
-
this.state = value;
|
|
90
|
+
return new Promise<CoValueCore>((resolve) => {
|
|
91
|
+
const listener = (state: CoValueState) => {
|
|
92
|
+
if (state.core) {
|
|
93
|
+
resolve(state.core);
|
|
94
|
+
this.removeListener(listener);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
173
97
|
|
|
174
|
-
|
|
175
|
-
state: this.state.type,
|
|
98
|
+
this.addListener(listener);
|
|
176
99
|
});
|
|
100
|
+
}
|
|
177
101
|
|
|
178
|
-
|
|
102
|
+
async loadFromPeers(peers: PeerState[]) {
|
|
103
|
+
if (peers.length === 0) {
|
|
179
104
|
return;
|
|
180
105
|
}
|
|
181
106
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
this.clearPromise();
|
|
187
|
-
} else if (value.type === "unavailable") {
|
|
188
|
-
this.resolve("unavailable");
|
|
189
|
-
this.clearPromise();
|
|
190
|
-
}
|
|
191
|
-
}
|
|
107
|
+
const loadAttempt = async (peersToLoadFrom: PeerState[]) => {
|
|
108
|
+
const peersToActuallyLoadFrom = [];
|
|
109
|
+
for (const peer of peersToLoadFrom) {
|
|
110
|
+
const currentState = this.peers.get(peer.id);
|
|
192
111
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
112
|
+
if (currentState?.type === "available") {
|
|
113
|
+
continue;
|
|
114
|
+
}
|
|
197
115
|
|
|
198
|
-
|
|
199
|
-
|
|
116
|
+
if (currentState?.type === "errored") {
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
200
119
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
120
|
+
if (
|
|
121
|
+
currentState?.type === "unavailable" ||
|
|
122
|
+
currentState?.type === "pending"
|
|
123
|
+
) {
|
|
124
|
+
if (peer.shouldRetryUnavailableCoValues()) {
|
|
125
|
+
this.markPending(peer.id);
|
|
126
|
+
peersToActuallyLoadFrom.push(peer);
|
|
127
|
+
}
|
|
204
128
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
209
131
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
);
|
|
215
|
-
|
|
216
|
-
// If we are in the loading state we move to a new loading state
|
|
217
|
-
// to reset all the loading promises
|
|
218
|
-
if (
|
|
219
|
-
this.state.type === "loading" ||
|
|
220
|
-
this.state.type === "unknown" ||
|
|
221
|
-
this.state.type === "unavailable"
|
|
222
|
-
) {
|
|
223
|
-
this.moveToState(
|
|
224
|
-
new CoValueLoadingState(peersWithoutErrors.map((p) => p.id)),
|
|
225
|
-
);
|
|
132
|
+
if (!currentState || currentState?.type === "unknown") {
|
|
133
|
+
this.markPending(peer.id);
|
|
134
|
+
peersToActuallyLoadFrom.push(peer);
|
|
135
|
+
}
|
|
226
136
|
}
|
|
227
137
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
138
|
+
for (const peer of peersToActuallyLoadFrom) {
|
|
139
|
+
if (peer.closed) {
|
|
140
|
+
this.markNotFoundInPeer(peer.id);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
231
143
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
144
|
+
peer
|
|
145
|
+
.pushOutgoingMessage({
|
|
146
|
+
action: "load",
|
|
147
|
+
...(this.core ? this.core.knownState() : emptyKnownState(this.id)),
|
|
148
|
+
})
|
|
149
|
+
.catch((err) => {
|
|
150
|
+
logger.warn(`Failed to push load message to peer ${peer.id}`, {
|
|
151
|
+
err,
|
|
152
|
+
});
|
|
153
|
+
});
|
|
238
154
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
155
|
+
/**
|
|
156
|
+
* Use a very long timeout for storage peers, because under pressure
|
|
157
|
+
* they may take a long time to consume the messages queue
|
|
158
|
+
*
|
|
159
|
+
* TODO: Track errors on storage and do not rely on timeout
|
|
160
|
+
*/
|
|
161
|
+
const timeoutDuration =
|
|
162
|
+
peer.role === "storage"
|
|
163
|
+
? CO_VALUE_LOADING_CONFIG.TIMEOUT * 10
|
|
164
|
+
: CO_VALUE_LOADING_CONFIG.TIMEOUT;
|
|
165
|
+
|
|
166
|
+
const waitingForPeer = new Promise<void>((resolve) => {
|
|
167
|
+
const markNotFound = () => {
|
|
168
|
+
if (this.peers.get(peer.id)?.type === "pending") {
|
|
169
|
+
this.markNotFoundInPeer(peer.id);
|
|
170
|
+
}
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const timeout = setTimeout(markNotFound, timeoutDuration);
|
|
174
|
+
const removeCloseListener = peer.addCloseListener(markNotFound);
|
|
175
|
+
|
|
176
|
+
const listener = (state: CoValueState) => {
|
|
177
|
+
const peerState = state.peers.get(peer.id);
|
|
178
|
+
if (
|
|
179
|
+
state.isAvailable() || // might have become available from another peer e.g. through handleNewContent
|
|
180
|
+
peerState?.type === "available" ||
|
|
181
|
+
peerState?.type === "errored" ||
|
|
182
|
+
peerState?.type === "unavailable"
|
|
183
|
+
) {
|
|
184
|
+
state.removeListener(listener);
|
|
185
|
+
removeCloseListener();
|
|
186
|
+
clearTimeout(timeout);
|
|
187
|
+
resolve();
|
|
188
|
+
}
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
this.addListener(listener);
|
|
192
|
+
});
|
|
242
193
|
|
|
243
|
-
|
|
194
|
+
await waitingForPeer;
|
|
195
|
+
}
|
|
244
196
|
};
|
|
245
197
|
|
|
246
|
-
await
|
|
198
|
+
await loadAttempt(peers);
|
|
199
|
+
|
|
200
|
+
if (this.isAvailable()) {
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
247
203
|
|
|
248
204
|
// Retry loading from peers that have the retry flag enabled
|
|
249
205
|
const peersWithRetry = peers.filter((p) =>
|
|
@@ -251,129 +207,74 @@ export class CoValueState {
|
|
|
251
207
|
);
|
|
252
208
|
|
|
253
209
|
if (peersWithRetry.length > 0) {
|
|
210
|
+
const waitingForCoValue = new Promise<void>((resolve) => {
|
|
211
|
+
const listener = (state: CoValueState) => {
|
|
212
|
+
if (state.isAvailable()) {
|
|
213
|
+
resolve();
|
|
214
|
+
this.removeListener(listener);
|
|
215
|
+
}
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
this.addListener(listener);
|
|
219
|
+
});
|
|
220
|
+
|
|
254
221
|
// We want to exit early if the coValue becomes available in between the retries
|
|
255
222
|
await Promise.race([
|
|
256
|
-
|
|
223
|
+
waitingForCoValue,
|
|
257
224
|
runWithRetry(
|
|
258
|
-
() =>
|
|
225
|
+
() => loadAttempt(peersWithRetry),
|
|
259
226
|
CO_VALUE_LOADING_CONFIG.MAX_RETRIES,
|
|
260
227
|
),
|
|
261
228
|
]);
|
|
262
229
|
}
|
|
263
|
-
|
|
264
|
-
// If after the retries the coValue is still loading, we consider the load failed
|
|
265
|
-
if (this.state.type === "loading") {
|
|
266
|
-
this.moveToState(new CoValueUnavailableState());
|
|
267
|
-
}
|
|
268
230
|
}
|
|
269
231
|
|
|
270
|
-
|
|
271
|
-
const
|
|
272
|
-
|
|
273
|
-
switch (action.type) {
|
|
274
|
-
case "available":
|
|
275
|
-
if (currentState.type === "loading") {
|
|
276
|
-
currentState.resolve(action.coValue);
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
// It should be always possible to move to the available state
|
|
280
|
-
this.moveToState(new CoValueAvailableState(action.coValue));
|
|
232
|
+
private updateCounter(previousState: string | null) {
|
|
233
|
+
const newState = this.highLevelState;
|
|
281
234
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
break;
|
|
235
|
+
if (previousState !== newState) {
|
|
236
|
+
if (previousState) {
|
|
237
|
+
this.counter.add(-1, { state: previousState });
|
|
238
|
+
}
|
|
239
|
+
this.counter.add(1, { state: newState });
|
|
289
240
|
}
|
|
290
241
|
}
|
|
291
|
-
}
|
|
292
242
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
)
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
coValueEntry.dispatch({
|
|
300
|
-
type: "not-found-in-peer",
|
|
301
|
-
peerId: peer.id,
|
|
302
|
-
});
|
|
303
|
-
continue;
|
|
304
|
-
}
|
|
243
|
+
markNotFoundInPeer(peerId: PeerID) {
|
|
244
|
+
const previousState = this.highLevelState;
|
|
245
|
+
this.peers.set(peerId, { type: "unavailable" });
|
|
246
|
+
this.updateCounter(previousState);
|
|
247
|
+
this.notifyListeners();
|
|
248
|
+
}
|
|
305
249
|
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
.pushOutgoingMessage({
|
|
315
|
-
action: "load",
|
|
316
|
-
...coValueEntry.state.coValue.knownState(),
|
|
317
|
-
})
|
|
318
|
-
.catch((err) => {
|
|
319
|
-
logger.warn(`Failed to push load message to peer ${peer.id}`, {
|
|
320
|
-
err,
|
|
321
|
-
});
|
|
322
|
-
});
|
|
323
|
-
} else {
|
|
324
|
-
/**
|
|
325
|
-
* We only wait for the load state to be resolved.
|
|
326
|
-
*/
|
|
327
|
-
peer
|
|
328
|
-
.pushOutgoingMessage({
|
|
329
|
-
action: "load",
|
|
330
|
-
id: coValueEntry.id,
|
|
331
|
-
header: false,
|
|
332
|
-
sessions: {},
|
|
333
|
-
})
|
|
334
|
-
.catch((err) => {
|
|
335
|
-
logger.warn(`Failed to push load message to peer ${peer.id}`, {
|
|
336
|
-
err,
|
|
337
|
-
});
|
|
338
|
-
});
|
|
339
|
-
}
|
|
250
|
+
// TODO: rename to "provided"
|
|
251
|
+
markAvailable(coValue: CoValueCore, fromPeerId: PeerID) {
|
|
252
|
+
const previousState = this.highLevelState;
|
|
253
|
+
this.core = coValue;
|
|
254
|
+
this.peers.set(fromPeerId, { type: "available" });
|
|
255
|
+
this.updateCounter(previousState);
|
|
256
|
+
this.notifyListeners();
|
|
257
|
+
}
|
|
340
258
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
*
|
|
348
|
-
* TODO: Track errors on storage and do not rely on timeout
|
|
349
|
-
*/
|
|
350
|
-
const timeoutDuration =
|
|
351
|
-
peer.role === "storage"
|
|
352
|
-
? CO_VALUE_LOADING_CONFIG.TIMEOUT * 10
|
|
353
|
-
: CO_VALUE_LOADING_CONFIG.TIMEOUT;
|
|
354
|
-
|
|
355
|
-
const handleTimeoutOrClose = () => {
|
|
356
|
-
if (coValueEntry.state.type === "loading") {
|
|
357
|
-
logger.warn("Failed to load coValue from peer", {
|
|
358
|
-
coValueId: coValueEntry.id,
|
|
359
|
-
peerId: peer.id,
|
|
360
|
-
peerRole: peer.role,
|
|
361
|
-
});
|
|
362
|
-
coValueEntry.dispatch({
|
|
363
|
-
type: "not-found-in-peer",
|
|
364
|
-
peerId: peer.id,
|
|
365
|
-
});
|
|
366
|
-
resolve();
|
|
367
|
-
}
|
|
368
|
-
};
|
|
259
|
+
internalMarkMagicallyAvailable(coValue: CoValueCore) {
|
|
260
|
+
const previousState = this.highLevelState;
|
|
261
|
+
this.core = coValue;
|
|
262
|
+
this.updateCounter(previousState);
|
|
263
|
+
this.notifyListeners();
|
|
264
|
+
}
|
|
369
265
|
|
|
370
|
-
|
|
371
|
-
|
|
266
|
+
markErrored(peerId: PeerID, error: TryAddTransactionsError) {
|
|
267
|
+
const previousState = this.highLevelState;
|
|
268
|
+
this.peers.set(peerId, { type: "errored", error });
|
|
269
|
+
this.updateCounter(previousState);
|
|
270
|
+
this.notifyListeners();
|
|
271
|
+
}
|
|
372
272
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
273
|
+
private markPending(peerId: PeerID) {
|
|
274
|
+
const previousState = this.highLevelState;
|
|
275
|
+
this.peers.set(peerId, { type: "pending" });
|
|
276
|
+
this.updateCounter(previousState);
|
|
277
|
+
this.notifyListeners();
|
|
377
278
|
}
|
|
378
279
|
}
|
|
379
280
|
|
|
@@ -400,29 +301,6 @@ async function runWithRetry<T>(fn: () => Promise<T>, maxRetries: number) {
|
|
|
400
301
|
}
|
|
401
302
|
}
|
|
402
303
|
|
|
403
|
-
function createResolvablePromise<T>() {
|
|
404
|
-
let resolve!: (value: T) => void;
|
|
405
|
-
|
|
406
|
-
const promise = new Promise<T>((res) => {
|
|
407
|
-
resolve = res;
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
return { promise, resolve };
|
|
411
|
-
}
|
|
412
|
-
|
|
413
304
|
function sleep(ms: number) {
|
|
414
305
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
415
306
|
}
|
|
416
|
-
|
|
417
|
-
function getPeersWithoutErrors(peers: PeerState[], coValueId: RawCoID) {
|
|
418
|
-
return peers.filter((p) => {
|
|
419
|
-
if (p.erroredCoValues.has(coValueId)) {
|
|
420
|
-
logger.warn(
|
|
421
|
-
`Skipping load on errored coValue ${coValueId} from peer ${p.id}`,
|
|
422
|
-
);
|
|
423
|
-
return false;
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
return true;
|
|
427
|
-
});
|
|
428
|
-
}
|
package/src/coValues/group.ts
CHANGED
|
@@ -186,8 +186,8 @@ export class RawGroup<
|
|
|
186
186
|
const child = store.get(id);
|
|
187
187
|
|
|
188
188
|
if (
|
|
189
|
-
child.
|
|
190
|
-
child.
|
|
189
|
+
child.highLevelState === "unknown" ||
|
|
190
|
+
child.highLevelState === "unavailable"
|
|
191
191
|
) {
|
|
192
192
|
child.loadFromPeers(peers).catch(() => {
|
|
193
193
|
logger.error(`Failed to load child group ${id}`);
|
package/src/exports.ts
CHANGED
|
@@ -79,8 +79,6 @@ type Value = JsonValue | AnyRawCoValue;
|
|
|
79
79
|
import { CO_VALUE_LOADING_CONFIG } from "./coValueState.js";
|
|
80
80
|
import { logger } from "./logger.js";
|
|
81
81
|
import { getPriorityFromHeader } from "./priority.js";
|
|
82
|
-
import { FileSystem } from "./storage/FileSystem.js";
|
|
83
|
-
import { BlockFilename, LSMStorage, WalFilename } from "./storage/index.js";
|
|
84
82
|
|
|
85
83
|
/** @hidden */
|
|
86
84
|
export const cojsonInternals = {
|
|
@@ -143,7 +141,6 @@ export {
|
|
|
143
141
|
CryptoProvider,
|
|
144
142
|
SyncMessage,
|
|
145
143
|
isRawCoID,
|
|
146
|
-
LSMStorage,
|
|
147
144
|
emptyKnownState,
|
|
148
145
|
RawCoPlainText,
|
|
149
146
|
stringifyOpID,
|
|
@@ -154,9 +151,6 @@ export {
|
|
|
154
151
|
|
|
155
152
|
export type {
|
|
156
153
|
Value,
|
|
157
|
-
FileSystem,
|
|
158
|
-
BlockFilename,
|
|
159
|
-
WalFilename,
|
|
160
154
|
IncomingSyncStream,
|
|
161
155
|
OutgoingSyncQueue,
|
|
162
156
|
DisconnectedError,
|