@livedigital/client 3.14.0 → 3.14.1
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/dist/engine/handlers/ChannelStateSyncEventHandler/ChannelStateConsistencyCheckResult.d.ts +13 -0
- package/dist/engine/handlers/ChannelStateSyncEventHandler/ChannelStateConsistencyChecker.d.ts +24 -0
- package/dist/engine/handlers/ChannelStateSyncEventHandler/index.d.ts +29 -0
- package/dist/index.es.js +2 -2
- package/dist/index.js +2 -2
- package/dist/types/channelStateSyncronizer.d.ts +9 -13
- package/package.json +1 -1
- package/src/engine/handlers/ChannelStateSyncEventHandler/ChannelStateConsistencyCheckResult.ts +27 -0
- package/src/engine/handlers/ChannelStateSyncEventHandler/ChannelStateConsistencyChecker.ts +100 -0
- package/src/engine/handlers/ChannelStateSyncEventHandler/index.ts +218 -0
- package/src/types/channelStateSyncronizer.ts +11 -15
- package/dist/engine/handlers/ChannelStateSyncEventHandler.d.ts +0 -29
- package/src/engine/handlers/ChannelStateSyncEventHandler.ts +0 -270
|
@@ -1,270 +0,0 @@
|
|
|
1
|
-
import { decompressSync, strFromU8 } from 'fflate';
|
|
2
|
-
import { serializeError } from 'serialize-error';
|
|
3
|
-
import { LogMessageHandler } from '../../types/common';
|
|
4
|
-
import Engine from '../index';
|
|
5
|
-
import Logger from '../Logger';
|
|
6
|
-
import {
|
|
7
|
-
ChannelState,
|
|
8
|
-
ChannelStateSyncEventPayload,
|
|
9
|
-
LastProbablyInconsistencyCheckResults,
|
|
10
|
-
ProbablyInconsistencyCheckResult,
|
|
11
|
-
} from '../../types/channelStateSyncronizer';
|
|
12
|
-
|
|
13
|
-
const MAX_CHECK_RETRIES_BEFORE_CONFIRM = 2;
|
|
14
|
-
|
|
15
|
-
class ChannelStateSyncEventHandler {
|
|
16
|
-
private readonly engine: Engine;
|
|
17
|
-
|
|
18
|
-
private readonly logger: Logger;
|
|
19
|
-
|
|
20
|
-
#confirmedMissingPeers: string[] = [];
|
|
21
|
-
|
|
22
|
-
#confirmedMissingProducers: string[] = [];
|
|
23
|
-
|
|
24
|
-
#confirmedProducersWithInconsistentState: string[] = [];
|
|
25
|
-
|
|
26
|
-
#lastProbablyInconsistencyCheckResults: LastProbablyInconsistencyCheckResults = [];
|
|
27
|
-
|
|
28
|
-
constructor(params: { engine: Engine, onLogMessage?: LogMessageHandler }) {
|
|
29
|
-
const { engine, onLogMessage } = params;
|
|
30
|
-
this.engine = engine;
|
|
31
|
-
this.logger = new Logger({
|
|
32
|
-
logLevel: engine.logLevel,
|
|
33
|
-
namespace: 'ChannelStateSyncEventHandler',
|
|
34
|
-
onLogMessage,
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
async handle(payload: Buffer): Promise<void> {
|
|
39
|
-
try {
|
|
40
|
-
const { event, data } = await this.parseChannelStateSyncEventPayload(payload);
|
|
41
|
-
if (event === 'channel-state') {
|
|
42
|
-
this.handleChannelStateEvent(data);
|
|
43
|
-
}
|
|
44
|
-
} catch (error: unknown) {
|
|
45
|
-
this.logger.error('handleChannelStateSyncEvent()', {
|
|
46
|
-
error: serializeError(error),
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
private handleChannelStateEvent(remoteState: ChannelState) {
|
|
52
|
-
const localState = this.getLocalState();
|
|
53
|
-
this.processStateInconsistency(localState, remoteState);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
private processStateInconsistency(localState: ChannelState, remoteState: ChannelState) {
|
|
57
|
-
this.notifyIfHasConfirmedInconsistency(localState, remoteState);
|
|
58
|
-
this.prepareProbableInconsistencyStats(localState, remoteState);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
private notifyIfHasConfirmedInconsistency(localState: ChannelState, remoteState: ChannelState) {
|
|
62
|
-
remoteState.peers.forEach((remotePeer) => {
|
|
63
|
-
const peer = localState.peers.find((p) => p.id === remotePeer.id);
|
|
64
|
-
if (!peer) {
|
|
65
|
-
this.notifyIfConfirmedPeerInconsistency(remotePeer.id);
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
this.resetPeerInconsistency(remotePeer.id);
|
|
70
|
-
|
|
71
|
-
remotePeer.producers.forEach((remoteProducer) => {
|
|
72
|
-
const producer = peer.producers.find((p) => p.id === remoteProducer.id);
|
|
73
|
-
if (!producer) {
|
|
74
|
-
this.notifyIfConfirmedProducerInconsistency(remoteProducer.id);
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
this.resetProducerInconsistency(producer.id);
|
|
79
|
-
|
|
80
|
-
const isInconsistentState = producer.paused !== remoteProducer.paused;
|
|
81
|
-
if (isInconsistentState) {
|
|
82
|
-
this.notifyIfConfirmedProducerStateInconsistency(producer.id, {
|
|
83
|
-
localPaused: producer.paused,
|
|
84
|
-
remotePaused: remoteProducer.paused,
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
this.resetProducerStateInconsistency(producer.id);
|
|
91
|
-
});
|
|
92
|
-
});
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
private prepareProbableInconsistencyStats(localState: ChannelState, remoteState: ChannelState) {
|
|
96
|
-
const probablyMissingPeers: string[] = [];
|
|
97
|
-
const probablyMissingProducers: string[] = [];
|
|
98
|
-
const probablyProducersWithInconsistentState: string[] = [];
|
|
99
|
-
|
|
100
|
-
if (!this.getIsConnectionActive()) {
|
|
101
|
-
this.#lastProbablyInconsistencyCheckResults = [];
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// fill probably missings for the second check
|
|
106
|
-
remoteState.peers.forEach((remotePeer) => {
|
|
107
|
-
if (this.#confirmedMissingPeers.includes(remotePeer.id)) {
|
|
108
|
-
// already alerted about
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const localPeer = localState.peers.find((peer) => peer.id === remotePeer.id);
|
|
113
|
-
if (!localPeer) {
|
|
114
|
-
probablyMissingPeers.push(remotePeer.id);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (!localPeer) {
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
remotePeer.producers.forEach((remoteProducer) => {
|
|
122
|
-
if (this.#confirmedMissingProducers.includes(remoteProducer.id)) {
|
|
123
|
-
// already alerted about
|
|
124
|
-
return;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const localProducer = localPeer.producers.find((producer) => producer.id === remoteProducer.id);
|
|
128
|
-
if (!localProducer) {
|
|
129
|
-
probablyMissingProducers.push(remoteProducer.id);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (!localProducer) {
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
if (this.#confirmedProducersWithInconsistentState.includes(remoteProducer.id)) {
|
|
137
|
-
// already alerted about
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
const isInconsistentState = localProducer.paused !== remoteProducer.paused;
|
|
142
|
-
if (isInconsistentState) {
|
|
143
|
-
probablyProducersWithInconsistentState.push(remoteProducer.id);
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
if (this.#lastProbablyInconsistencyCheckResults.length === MAX_CHECK_RETRIES_BEFORE_CONFIRM) {
|
|
149
|
-
this.#lastProbablyInconsistencyCheckResults.shift();
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
this.#lastProbablyInconsistencyCheckResults.push({
|
|
153
|
-
probablyMissingPeers,
|
|
154
|
-
probablyMissingProducers,
|
|
155
|
-
probablyProducersWithInconsistentState,
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
private isReachedMaxCheckRetries(storageKey: keyof ProbablyInconsistencyCheckResult, id: string) {
|
|
160
|
-
let retries = 0;
|
|
161
|
-
this.#lastProbablyInconsistencyCheckResults.forEach((checkResults) => {
|
|
162
|
-
if (checkResults[storageKey].includes(id)) {
|
|
163
|
-
retries += 1;
|
|
164
|
-
}
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
return retries >= MAX_CHECK_RETRIES_BEFORE_CONFIRM;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
private isAlreadyAlertedAbout(storage: string[], id: string) {
|
|
171
|
-
return storage.includes(id);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
notifyIfConfirmedPeerInconsistency(peerId: string): void {
|
|
175
|
-
const isReachedMaxCheckRetries = this.isReachedMaxCheckRetries('probablyMissingPeers', peerId);
|
|
176
|
-
const isAlreadyAlertedAbout = this.isAlreadyAlertedAbout(this.#confirmedMissingPeers, peerId);
|
|
177
|
-
if (isReachedMaxCheckRetries && !isAlreadyAlertedAbout) {
|
|
178
|
-
this.#confirmedMissingPeers.push(peerId);
|
|
179
|
-
this.logger.warn('Peer is missing', {
|
|
180
|
-
case: 'channel-state-sync',
|
|
181
|
-
missingPeerId: peerId,
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
private resetPeerInconsistency(peerId: string) {
|
|
187
|
-
const confirmedMissingPeerIdx = this.#confirmedMissingPeers.indexOf(peerId);
|
|
188
|
-
if (confirmedMissingPeerIdx > -1) {
|
|
189
|
-
// issue resolved
|
|
190
|
-
delete this.#confirmedMissingPeers[confirmedMissingPeerIdx];
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
private notifyIfConfirmedProducerInconsistency(producerId: string) {
|
|
195
|
-
const isReachedMaxCheckRetries = this.isReachedMaxCheckRetries('probablyMissingProducers', producerId);
|
|
196
|
-
const isAlreadyAlertedAbout = this.isAlreadyAlertedAbout(this.#confirmedMissingProducers, producerId);
|
|
197
|
-
if (isReachedMaxCheckRetries && !isAlreadyAlertedAbout) {
|
|
198
|
-
this.#confirmedMissingProducers.push(producerId);
|
|
199
|
-
this.logger.warn('Producer is missing', {
|
|
200
|
-
case: 'channel-state-sync',
|
|
201
|
-
missingProducerId: producerId,
|
|
202
|
-
});
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
private resetProducerInconsistency(producerId: string) {
|
|
207
|
-
const confirmedMissingProducerIdx = this.#confirmedMissingProducers.indexOf(producerId);
|
|
208
|
-
if (confirmedMissingProducerIdx > -1) {
|
|
209
|
-
// issue resolved
|
|
210
|
-
delete this.#confirmedMissingProducers[confirmedMissingProducerIdx];
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
private notifyIfConfirmedProducerStateInconsistency(producerId: string, meta: Record<string, unknown>) {
|
|
215
|
-
const isReachedMaxCheckRetries = this.isReachedMaxCheckRetries(
|
|
216
|
-
'probablyProducersWithInconsistentState', producerId,
|
|
217
|
-
);
|
|
218
|
-
const isAlreadyAlertedAbout = this.isAlreadyAlertedAbout(this.#confirmedProducersWithInconsistentState, producerId);
|
|
219
|
-
if (isReachedMaxCheckRetries && !isAlreadyAlertedAbout) {
|
|
220
|
-
this.#confirmedProducersWithInconsistentState.push(producerId);
|
|
221
|
-
this.logger.warn('Producer is in inconsistent state', {
|
|
222
|
-
case: 'channel-state-sync',
|
|
223
|
-
producerId,
|
|
224
|
-
...meta,
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
private resetProducerStateInconsistency(producerId: string) {
|
|
230
|
-
const confirmedProducersWithInconsistentStateIdx = this
|
|
231
|
-
.#confirmedProducersWithInconsistentState.indexOf(producerId);
|
|
232
|
-
|
|
233
|
-
if (confirmedProducersWithInconsistentStateIdx > -1) {
|
|
234
|
-
// issue resolved
|
|
235
|
-
delete this.#confirmedProducersWithInconsistentState[confirmedProducersWithInconsistentStateIdx];
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
private async parseChannelStateSyncEventPayload(data: Blob | Buffer): Promise<ChannelStateSyncEventPayload> {
|
|
240
|
-
const arrayData = 'arrayBuffer' in data
|
|
241
|
-
? new Uint8Array(await data.arrayBuffer())
|
|
242
|
-
: new Uint8Array(data);
|
|
243
|
-
const decompressed = decompressSync(arrayData);
|
|
244
|
-
return JSON.parse(strFromU8(decompressed)) as ChannelStateSyncEventPayload;
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
private getLocalState(): ChannelState {
|
|
248
|
-
const { hostPeers } = this.engine;
|
|
249
|
-
return {
|
|
250
|
-
peers: hostPeers.map((peer) => ({
|
|
251
|
-
id: peer.id,
|
|
252
|
-
producers: peer.getProducers().map((producer) => ({
|
|
253
|
-
id: producer.id,
|
|
254
|
-
paused: producer.paused,
|
|
255
|
-
})),
|
|
256
|
-
})),
|
|
257
|
-
};
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
private getIsConnectionActive() {
|
|
261
|
-
try {
|
|
262
|
-
this.engine.checkConnectionActivity();
|
|
263
|
-
return true;
|
|
264
|
-
} catch (error: unknown) {
|
|
265
|
-
return false;
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
export default ChannelStateSyncEventHandler;
|