cojson 0.13.11 → 0.13.12
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 +6 -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 +174 -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 +197 -317
- 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,119 +1,35 @@
|
|
|
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
|
-
|
|
35
|
-
const { resolve, promise } = createResolvablePromise<
|
|
36
|
-
CoValueCore | "unavailable"
|
|
37
|
-
>();
|
|
38
|
-
|
|
39
|
-
this.result = promise;
|
|
40
|
-
this.resolveResult = resolve;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
markAsUnavailable(peerId: PeerID) {
|
|
44
|
-
const entry = this.peers.get(peerId);
|
|
45
|
-
|
|
46
|
-
if (entry) {
|
|
47
|
-
entry.resolve();
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
this.peers.delete(peerId);
|
|
51
23
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
this.resolve("unavailable");
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
resolve(value: CoValueCore | "unavailable") {
|
|
59
|
-
this.resolveResult(value);
|
|
60
|
-
for (const entry of this.peers.values()) {
|
|
61
|
-
entry.resolve();
|
|
62
|
-
}
|
|
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
|
-
|
|
70
|
-
if (!entry) {
|
|
71
|
-
return Promise.resolve();
|
|
72
|
-
}
|
|
24
|
+
core: CoValueCore | null = null;
|
|
25
|
+
id: RawCoID;
|
|
73
26
|
|
|
74
|
-
|
|
75
|
-
}
|
|
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
|
-
|
|
88
|
-
type CoValueStateAction =
|
|
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;
|
|
27
|
+
private listeners: Set<(state: CoValueState) => void> = new Set();
|
|
111
28
|
private counter: UpDownCounter;
|
|
112
29
|
|
|
113
|
-
constructor(
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
) {
|
|
30
|
+
constructor(id: RawCoID) {
|
|
31
|
+
this.id = id;
|
|
32
|
+
|
|
117
33
|
this.counter = metrics
|
|
118
34
|
.getMeter("cojson")
|
|
119
35
|
.createUpDownCounter("jazz.covalues.loaded", {
|
|
@@ -122,128 +38,170 @@ export class CoValueState {
|
|
|
122
38
|
valueType: ValueType.INT,
|
|
123
39
|
});
|
|
124
40
|
|
|
125
|
-
this.
|
|
126
|
-
state: this.state.type,
|
|
127
|
-
});
|
|
41
|
+
this.updateCounter(null);
|
|
128
42
|
}
|
|
129
43
|
|
|
130
|
-
|
|
131
|
-
|
|
44
|
+
get highLevelState() {
|
|
45
|
+
if (this.core) {
|
|
46
|
+
return "available";
|
|
47
|
+
} else {
|
|
48
|
+
if (this.peers.size === 0) {
|
|
49
|
+
return "unknown";
|
|
50
|
+
} else if (
|
|
51
|
+
this.peers
|
|
52
|
+
.values()
|
|
53
|
+
.every((p) => p.type === "unavailable" || p.type === "errored")
|
|
54
|
+
) {
|
|
55
|
+
return "unavailable";
|
|
56
|
+
} else if (this.peers.values().some((p) => p.type === "pending")) {
|
|
57
|
+
return "loading";
|
|
58
|
+
} else {
|
|
59
|
+
return "unknown";
|
|
60
|
+
}
|
|
61
|
+
}
|
|
132
62
|
}
|
|
133
63
|
|
|
134
|
-
|
|
135
|
-
return
|
|
64
|
+
isErroredInPeer(peerId: PeerID) {
|
|
65
|
+
return this.peers.get(peerId)?.type === "errored";
|
|
136
66
|
}
|
|
137
67
|
|
|
138
|
-
|
|
139
|
-
return
|
|
68
|
+
isAvailable(): this is { type: "available"; core: CoValueCore } {
|
|
69
|
+
return !!this.core;
|
|
140
70
|
}
|
|
141
71
|
|
|
142
|
-
|
|
143
|
-
|
|
72
|
+
addListener(listener: (state: CoValueState) => void) {
|
|
73
|
+
this.listeners.add(listener);
|
|
74
|
+
listener(this);
|
|
144
75
|
}
|
|
145
76
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
if (this.state.type === "unavailable") {
|
|
151
|
-
return "unavailable";
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// If we don't have a resolved state we return a new promise
|
|
155
|
-
// that will be resolved when the state will move to available or unavailable
|
|
156
|
-
if (!this.promise) {
|
|
157
|
-
const { promise, resolve } = createResolvablePromise<
|
|
158
|
-
CoValueCore | "unavailable"
|
|
159
|
-
>();
|
|
77
|
+
removeListener(listener: (state: CoValueState) => void) {
|
|
78
|
+
this.listeners.delete(listener);
|
|
79
|
+
}
|
|
160
80
|
|
|
161
|
-
|
|
162
|
-
|
|
81
|
+
private notifyListeners() {
|
|
82
|
+
for (const listener of this.listeners) {
|
|
83
|
+
listener(this);
|
|
163
84
|
}
|
|
164
|
-
|
|
165
|
-
return this.promise;
|
|
166
85
|
}
|
|
167
86
|
|
|
168
|
-
|
|
169
|
-
this.
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
this.state = value;
|
|
87
|
+
async getCoValue() {
|
|
88
|
+
if (this.highLevelState === "unavailable") {
|
|
89
|
+
return "unavailable";
|
|
90
|
+
}
|
|
173
91
|
|
|
174
|
-
|
|
175
|
-
state:
|
|
92
|
+
return new Promise<CoValueCore>((resolve) => {
|
|
93
|
+
const listener = (state: CoValueState) => {
|
|
94
|
+
if (state.core) {
|
|
95
|
+
resolve(state.core);
|
|
96
|
+
this.removeListener(listener);
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
this.addListener(listener);
|
|
176
101
|
});
|
|
102
|
+
}
|
|
177
103
|
|
|
178
|
-
|
|
104
|
+
async loadFromPeers(peers: PeerState[]) {
|
|
105
|
+
if (peers.length === 0) {
|
|
179
106
|
return;
|
|
180
107
|
}
|
|
181
108
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
this.clearPromise();
|
|
187
|
-
} else if (value.type === "unavailable") {
|
|
188
|
-
this.resolve("unavailable");
|
|
189
|
-
this.clearPromise();
|
|
190
|
-
}
|
|
191
|
-
}
|
|
109
|
+
const loadAttempt = async (peersToLoadFrom: PeerState[]) => {
|
|
110
|
+
const peersToActuallyLoadFrom = [];
|
|
111
|
+
for (const peer of peersToLoadFrom) {
|
|
112
|
+
const currentState = this.peers.get(peer.id);
|
|
192
113
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
}
|
|
114
|
+
if (currentState?.type === "available") {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
197
117
|
|
|
198
|
-
|
|
199
|
-
|
|
118
|
+
if (currentState?.type === "errored") {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
200
121
|
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
122
|
+
if (
|
|
123
|
+
currentState?.type === "unavailable" ||
|
|
124
|
+
currentState?.type === "pending"
|
|
125
|
+
) {
|
|
126
|
+
if (peer.shouldRetryUnavailableCoValues()) {
|
|
127
|
+
this.markPending(peer.id);
|
|
128
|
+
peersToActuallyLoadFrom.push(peer);
|
|
129
|
+
}
|
|
204
130
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
209
133
|
|
|
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
|
-
);
|
|
134
|
+
if (!currentState || currentState?.type === "unknown") {
|
|
135
|
+
this.markPending(peer.id);
|
|
136
|
+
peersToActuallyLoadFrom.push(peer);
|
|
137
|
+
}
|
|
226
138
|
}
|
|
227
139
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
140
|
+
for (const peer of peersToActuallyLoadFrom) {
|
|
141
|
+
if (peer.closed) {
|
|
142
|
+
this.markNotFoundInPeer(peer.id);
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
231
145
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
146
|
+
peer
|
|
147
|
+
.pushOutgoingMessage({
|
|
148
|
+
action: "load",
|
|
149
|
+
...(this.core ? this.core.knownState() : emptyKnownState(this.id)),
|
|
150
|
+
})
|
|
151
|
+
.catch((err) => {
|
|
152
|
+
logger.warn(`Failed to push load message to peer ${peer.id}`, {
|
|
153
|
+
err,
|
|
154
|
+
});
|
|
155
|
+
});
|
|
238
156
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
157
|
+
/**
|
|
158
|
+
* Use a very long timeout for storage peers, because under pressure
|
|
159
|
+
* they may take a long time to consume the messages queue
|
|
160
|
+
*
|
|
161
|
+
* TODO: Track errors on storage and do not rely on timeout
|
|
162
|
+
*/
|
|
163
|
+
const timeoutDuration =
|
|
164
|
+
peer.role === "storage"
|
|
165
|
+
? CO_VALUE_LOADING_CONFIG.TIMEOUT * 10
|
|
166
|
+
: CO_VALUE_LOADING_CONFIG.TIMEOUT;
|
|
167
|
+
|
|
168
|
+
const waitingForPeer = new Promise<void>((resolve) => {
|
|
169
|
+
const markNotFound = () => {
|
|
170
|
+
if (this.peers.get(peer.id)?.type === "pending") {
|
|
171
|
+
this.markNotFoundInPeer(peer.id);
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const timeout = setTimeout(markNotFound, timeoutDuration);
|
|
176
|
+
const removeCloseListener = peer.addCloseListener(markNotFound);
|
|
177
|
+
|
|
178
|
+
const listener = (state: CoValueState) => {
|
|
179
|
+
const peerState = state.peers.get(peer.id);
|
|
180
|
+
if (
|
|
181
|
+
state.isAvailable() || // might have become available from another peer e.g. through handleNewContent
|
|
182
|
+
peerState?.type === "available" ||
|
|
183
|
+
peerState?.type === "errored" ||
|
|
184
|
+
peerState?.type === "unavailable"
|
|
185
|
+
) {
|
|
186
|
+
state.removeListener(listener);
|
|
187
|
+
removeCloseListener();
|
|
188
|
+
clearTimeout(timeout);
|
|
189
|
+
resolve();
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
this.addListener(listener);
|
|
194
|
+
});
|
|
242
195
|
|
|
243
|
-
|
|
196
|
+
await waitingForPeer;
|
|
197
|
+
}
|
|
244
198
|
};
|
|
245
199
|
|
|
246
|
-
await
|
|
200
|
+
await loadAttempt(peers);
|
|
201
|
+
|
|
202
|
+
if (this.isAvailable()) {
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
247
205
|
|
|
248
206
|
// Retry loading from peers that have the retry flag enabled
|
|
249
207
|
const peersWithRetry = peers.filter((p) =>
|
|
@@ -251,129 +209,74 @@ export class CoValueState {
|
|
|
251
209
|
);
|
|
252
210
|
|
|
253
211
|
if (peersWithRetry.length > 0) {
|
|
212
|
+
const waitingForCoValue = new Promise<void>((resolve) => {
|
|
213
|
+
const listener = (state: CoValueState) => {
|
|
214
|
+
if (state.isAvailable()) {
|
|
215
|
+
resolve();
|
|
216
|
+
this.removeListener(listener);
|
|
217
|
+
}
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
this.addListener(listener);
|
|
221
|
+
});
|
|
222
|
+
|
|
254
223
|
// We want to exit early if the coValue becomes available in between the retries
|
|
255
224
|
await Promise.race([
|
|
256
|
-
|
|
225
|
+
waitingForCoValue,
|
|
257
226
|
runWithRetry(
|
|
258
|
-
() =>
|
|
227
|
+
() => loadAttempt(peersWithRetry),
|
|
259
228
|
CO_VALUE_LOADING_CONFIG.MAX_RETRIES,
|
|
260
229
|
),
|
|
261
230
|
]);
|
|
262
231
|
}
|
|
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
232
|
}
|
|
269
233
|
|
|
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));
|
|
234
|
+
private updateCounter(previousState: string | null) {
|
|
235
|
+
const newState = this.highLevelState;
|
|
281
236
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
break;
|
|
237
|
+
if (previousState !== newState) {
|
|
238
|
+
if (previousState) {
|
|
239
|
+
this.counter.add(-1, { state: previousState });
|
|
240
|
+
}
|
|
241
|
+
this.counter.add(1, { state: newState });
|
|
289
242
|
}
|
|
290
243
|
}
|
|
291
|
-
}
|
|
292
244
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
)
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
coValueEntry.dispatch({
|
|
300
|
-
type: "not-found-in-peer",
|
|
301
|
-
peerId: peer.id,
|
|
302
|
-
});
|
|
303
|
-
continue;
|
|
304
|
-
}
|
|
245
|
+
markNotFoundInPeer(peerId: PeerID) {
|
|
246
|
+
const previousState = this.highLevelState;
|
|
247
|
+
this.peers.set(peerId, { type: "unavailable" });
|
|
248
|
+
this.updateCounter(previousState);
|
|
249
|
+
this.notifyListeners();
|
|
250
|
+
}
|
|
305
251
|
|
|
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
|
-
}
|
|
252
|
+
// TODO: rename to "provided"
|
|
253
|
+
markAvailable(coValue: CoValueCore, fromPeerId: PeerID) {
|
|
254
|
+
const previousState = this.highLevelState;
|
|
255
|
+
this.core = coValue;
|
|
256
|
+
this.peers.set(fromPeerId, { type: "available" });
|
|
257
|
+
this.updateCounter(previousState);
|
|
258
|
+
this.notifyListeners();
|
|
259
|
+
}
|
|
340
260
|
|
|
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
|
-
};
|
|
261
|
+
internalMarkMagicallyAvailable(coValue: CoValueCore) {
|
|
262
|
+
const previousState = this.highLevelState;
|
|
263
|
+
this.core = coValue;
|
|
264
|
+
this.updateCounter(previousState);
|
|
265
|
+
this.notifyListeners();
|
|
266
|
+
}
|
|
369
267
|
|
|
370
|
-
|
|
371
|
-
|
|
268
|
+
markErrored(peerId: PeerID, error: TryAddTransactionsError) {
|
|
269
|
+
const previousState = this.highLevelState;
|
|
270
|
+
this.peers.set(peerId, { type: "errored", error });
|
|
271
|
+
this.updateCounter(previousState);
|
|
272
|
+
this.notifyListeners();
|
|
273
|
+
}
|
|
372
274
|
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
275
|
+
private markPending(peerId: PeerID) {
|
|
276
|
+
const previousState = this.highLevelState;
|
|
277
|
+
this.peers.set(peerId, { type: "pending" });
|
|
278
|
+
this.updateCounter(previousState);
|
|
279
|
+
this.notifyListeners();
|
|
377
280
|
}
|
|
378
281
|
}
|
|
379
282
|
|
|
@@ -400,29 +303,6 @@ async function runWithRetry<T>(fn: () => Promise<T>, maxRetries: number) {
|
|
|
400
303
|
}
|
|
401
304
|
}
|
|
402
305
|
|
|
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
306
|
function sleep(ms: number) {
|
|
414
307
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
415
308
|
}
|
|
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,
|