@livedigital/client 3.23.0 → 3.24.0
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/ChannelStateSynchronizer.d.ts +4 -1
- package/dist/engine/EventsQueue.d.ts +26 -0
- package/dist/engine/Peer.d.ts +5 -0
- package/dist/engine/handlers/ChannelStateSyncEventHandler/ChannelStateConsistencyCheckResult.d.ts +4 -0
- package/dist/engine/handlers/ChannelStateSyncEventHandler/ChannelStateConsistencyChecker.d.ts +8 -0
- package/dist/engine/handlers/ChannelStateSyncEventHandler/index.d.ts +2 -0
- package/dist/engine/handlers/ChannelStateSyncEventHandler/types.d.ts +4 -1
- package/dist/engine/index.d.ts +2 -0
- package/dist/engine/media/tracks/DefaultBaseTrack.d.ts +8 -0
- package/dist/engine/media/tracks/DefaultVideoTrack.d.ts +10 -1
- package/dist/engine/media/tracks/PeerTrack.d.ts +1 -0
- package/dist/index.es.js +3 -3
- package/dist/index.js +4 -4
- package/dist/types/channelStateSyncronizer.d.ts +6 -0
- package/package.json +1 -1
- package/src/engine/ChannelStateSynchronizer.ts +42 -1
- package/src/engine/EventsQueue.ts +114 -0
- package/src/engine/Peer.ts +11 -3
- package/src/engine/handlers/ChannelEventHandler.ts +47 -41
- package/src/engine/handlers/ChannelStateSyncEventHandler/ChannelStateConsistencyCheckResult.ts +10 -0
- package/src/engine/handlers/ChannelStateSyncEventHandler/ChannelStateConsistencyChecker.ts +35 -0
- package/src/engine/handlers/ChannelStateSyncEventHandler/index.ts +46 -5
- package/src/engine/handlers/ChannelStateSyncEventHandler/types.ts +9 -0
- package/src/engine/handlers/MediaSoupEventHandler.ts +48 -44
- package/src/engine/index.ts +13 -6
- package/src/engine/media/index.ts +1 -1
- package/src/engine/media/streamEffects/audio/noiseSuppression/TrackProcessor.ts +1 -1
- package/src/engine/media/tracks/DefaultAudioTrack.ts +2 -2
- package/src/engine/media/tracks/DefaultBaseTrack.ts +21 -10
- package/src/engine/media/tracks/DefaultVideoTrack.ts +16 -4
- package/src/engine/media/tracks/PeerTrack.ts +29 -17
- package/src/engine/network/Socket.ts +0 -4
- package/src/engine/network/index.ts +6 -6
- package/src/types/channelStateSyncronizer.ts +7 -0
|
@@ -2,10 +2,16 @@ export interface ChannelStateProducer {
|
|
|
2
2
|
id: string;
|
|
3
3
|
paused: boolean;
|
|
4
4
|
}
|
|
5
|
+
export interface ChannelStateConsumer {
|
|
6
|
+
id: string;
|
|
7
|
+
paused: boolean;
|
|
8
|
+
producerId: string;
|
|
9
|
+
}
|
|
5
10
|
export interface ChannelStatePeer {
|
|
6
11
|
id: string;
|
|
7
12
|
appDataHash: string;
|
|
8
13
|
producers: ChannelStateProducer[];
|
|
14
|
+
consumers?: ChannelStateConsumer[];
|
|
9
15
|
}
|
|
10
16
|
export interface ChannelState {
|
|
11
17
|
peers: ChannelStatePeer[];
|
package/package.json
CHANGED
|
@@ -9,8 +9,10 @@ import {
|
|
|
9
9
|
import Peer from './Peer';
|
|
10
10
|
import { debounce, deepEqualObject } from '../helpers/common';
|
|
11
11
|
import Engine from './index';
|
|
12
|
+
import { InconsistenceType } from './handlers/ChannelStateSyncEventHandler/types';
|
|
12
13
|
|
|
13
14
|
const RESTORE_STATE_DEBOUNCE_TIME_MS = 3000;
|
|
15
|
+
const SYNC_CONSUMERS_DEBOUNCE_TIME_MS = 1000;
|
|
14
16
|
|
|
15
17
|
export interface ChannelStateSynchronizerParams {
|
|
16
18
|
engine: Engine;
|
|
@@ -24,7 +26,9 @@ class ChannelStateSynchronizer {
|
|
|
24
26
|
|
|
25
27
|
readonly #logger: Logger;
|
|
26
28
|
|
|
27
|
-
readonly debouncedRestoreState: <U>(this: U, ...args: Parameters<() => Promise<void>>) => void
|
|
29
|
+
readonly debouncedRestoreState: <U>(this: U, ...args: Parameters<() => Promise<void>>) => void | Promise<void>;
|
|
30
|
+
|
|
31
|
+
readonly debouncedSyncPeerState: <U>(this: U, type: InconsistenceType) => void | Promise<void>;
|
|
28
32
|
|
|
29
33
|
constructor({
|
|
30
34
|
engine, onLogMessage, logLevel, sendAnalytics,
|
|
@@ -38,12 +42,49 @@ class ChannelStateSynchronizer {
|
|
|
38
42
|
});
|
|
39
43
|
this.watchNetworkState();
|
|
40
44
|
this.debouncedRestoreState = debounce(this.restoreState.bind(this), RESTORE_STATE_DEBOUNCE_TIME_MS);
|
|
45
|
+
this.debouncedSyncPeerState = debounce(this.syncPeerState.bind(this), SYNC_CONSUMERS_DEBOUNCE_TIME_MS);
|
|
41
46
|
}
|
|
42
47
|
|
|
43
48
|
private watchNetworkState(): void {
|
|
44
49
|
this.#engine.clientEventEmitter.on(CLIENT_EVENTS.connectionRestored, async () => {
|
|
45
50
|
await this.debouncedRestoreState();
|
|
46
51
|
});
|
|
52
|
+
|
|
53
|
+
this.#engine.clientEventEmitter.on(CLIENT_EVENTS.channelStateInconsistent, async ({ type }) => {
|
|
54
|
+
if ([InconsistenceType.IncorrectConsumerState, InconsistenceType.MissingConsumers].includes(type)) {
|
|
55
|
+
await this.debouncedSyncPeerState(type);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private async syncPeerState(type: InconsistenceType): Promise<void> {
|
|
61
|
+
try {
|
|
62
|
+
if (!this.#engine.isSocketConnectionActive) {
|
|
63
|
+
this.#logger.info('Connection inactive, skipping re-syncing local peers state', {
|
|
64
|
+
case: 'restoreLocalState',
|
|
65
|
+
type,
|
|
66
|
+
});
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
this.#engine.eventsQueue.pause();
|
|
71
|
+
|
|
72
|
+
this.#logger.info('Re-syncing local peers state', {
|
|
73
|
+
case: 'restoreLocalState',
|
|
74
|
+
type,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
const { peers: clusterPeers } = await this.#engine.network.getChannelPeers('host');
|
|
78
|
+
this.restoreLocalPeersState(clusterPeers);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
this.#logger.info('Failed to re-sync local peers state', {
|
|
81
|
+
case: 'restoreLocalState',
|
|
82
|
+
type,
|
|
83
|
+
error: serializeError(error),
|
|
84
|
+
});
|
|
85
|
+
} finally {
|
|
86
|
+
this.#engine.eventsQueue.resume();
|
|
87
|
+
}
|
|
47
88
|
}
|
|
48
89
|
|
|
49
90
|
private async restoreState(): Promise<void> {
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Single point of events handling
|
|
3
|
+
* All events are redirected to that queue
|
|
4
|
+
* And later flushed to subscribed events handlers
|
|
5
|
+
*
|
|
6
|
+
* Has ability to postpone events processing
|
|
7
|
+
*
|
|
8
|
+
* When subscribing to event from all places: sockets, data channels
|
|
9
|
+
* All events should be sent to that queue
|
|
10
|
+
* And handlers should subscribe to events of that queue
|
|
11
|
+
*/
|
|
12
|
+
import { DataConsumer } from 'mediasoup-client/lib/DataConsumer';
|
|
13
|
+
import { Socket } from 'socket.io-client';
|
|
14
|
+
|
|
15
|
+
interface EventArgs {
|
|
16
|
+
event: string
|
|
17
|
+
args: any[]
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default class EventsQueue {
|
|
21
|
+
readonly #queue: EventArgs[] = [];
|
|
22
|
+
|
|
23
|
+
readonly #listeners: Map<string, ((...args: any[]) => void | Promise<void>)[]> = new Map();
|
|
24
|
+
|
|
25
|
+
readonly #connections: Set<Socket> = new Set();
|
|
26
|
+
|
|
27
|
+
#paused: boolean = false;
|
|
28
|
+
|
|
29
|
+
subscribeToSocket(emmiter: Socket) {
|
|
30
|
+
if (this.#connections.has(emmiter)) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
emmiter.onAny((event: string, ...args: any[]) => {
|
|
35
|
+
this.push(event, args);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
this.#connections.add(emmiter);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
subscribeToDataChannel(dataChannel: DataConsumer) {
|
|
42
|
+
dataChannel.on('message', (...args: any[]) => {
|
|
43
|
+
this.push('message', args);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
on(event: string, handler: (...args: any[]) => void | Promise<void>) {
|
|
48
|
+
if (this.#listeners.has(event)) {
|
|
49
|
+
this.#listeners.get(event)?.push(handler);
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
this.#listeners.set(event, [handler]);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
removeListener(event: string) {
|
|
57
|
+
this.#listeners.delete(event);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
removeAllListeners(events: string[], emitter?: Socket) {
|
|
61
|
+
if (emitter && this.#connections.has(emitter)) {
|
|
62
|
+
emitter.offAny();
|
|
63
|
+
this.#connections.delete(emitter);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
events.forEach((event) => {
|
|
67
|
+
this.#listeners.delete(event);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private push(event: string, args: any[]) {
|
|
72
|
+
this.#queue.push({
|
|
73
|
+
event,
|
|
74
|
+
args,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
this.process();
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/*
|
|
81
|
+
* Process all events accumulated in the this.#queue
|
|
82
|
+
* Should happen immediately if queue is not paused
|
|
83
|
+
*
|
|
84
|
+
* Read existing listeners/handlers and emit events to them
|
|
85
|
+
*/
|
|
86
|
+
private process() {
|
|
87
|
+
if (this.#paused) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
while (this.#queue.length > 0) {
|
|
92
|
+
const eventArgs = this.#queue.shift()!;
|
|
93
|
+
|
|
94
|
+
this.emit(eventArgs.event, eventArgs.args);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
private emit(event: string, args: any[]) {
|
|
99
|
+
const handlers = this.#listeners.get(event) || [];
|
|
100
|
+
|
|
101
|
+
handlers.forEach((handler) => {
|
|
102
|
+
handler(...args);
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
pause() {
|
|
107
|
+
this.#paused = true;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
resume() {
|
|
111
|
+
this.#paused = false;
|
|
112
|
+
this.process();
|
|
113
|
+
}
|
|
114
|
+
}
|
package/src/engine/Peer.ts
CHANGED
|
@@ -242,7 +242,7 @@ class Peer {
|
|
|
242
242
|
this.tracks.set(track.label, track);
|
|
243
243
|
this.logger.debug(`Subscribed for ${producer.kind}`, { peer: logResponse(this) });
|
|
244
244
|
} catch (error) {
|
|
245
|
-
this.logger.error('subscribe()', { producerId, error });
|
|
245
|
+
this.logger.error('subscribe()', { producerId, error, peer: logResponse(this) });
|
|
246
246
|
throw new Error('Error subscribe media');
|
|
247
247
|
}
|
|
248
248
|
}
|
|
@@ -324,6 +324,14 @@ class Peer {
|
|
|
324
324
|
return Array.from(this.producers.values());
|
|
325
325
|
}
|
|
326
326
|
|
|
327
|
+
getConsumersState(): { id: string, producerId: string, paused: boolean }[] {
|
|
328
|
+
return this.getAllConsumers().map((consumer) => ({
|
|
329
|
+
id: consumer.id,
|
|
330
|
+
paused: consumer.paused,
|
|
331
|
+
producerId: consumer.producerId,
|
|
332
|
+
}));
|
|
333
|
+
}
|
|
334
|
+
|
|
327
335
|
private handleNewProducer(producerData: ProducerData): void {
|
|
328
336
|
if (this.producers.get(producerData.id)) {
|
|
329
337
|
return;
|
|
@@ -376,7 +384,7 @@ class Peer {
|
|
|
376
384
|
|
|
377
385
|
await track.close();
|
|
378
386
|
this.tracks.delete(track.label);
|
|
379
|
-
this.logger.debug('Peer video track was ended:', { peer: logResponse(this), track });
|
|
387
|
+
this.logger.debug('Peer video track was ended:', { peer: logResponse(this), track: track.logCtx });
|
|
380
388
|
});
|
|
381
389
|
|
|
382
390
|
this.observer.on(MEDIASOUP_EVENTS.pauseConsumer, async (consumerId) => {
|
|
@@ -495,7 +503,7 @@ class Peer {
|
|
|
495
503
|
consumer.setRequestedSpatialLayer(undefined);
|
|
496
504
|
}
|
|
497
505
|
|
|
498
|
-
this.logger.debug('consumerChangePreferredLayers()', {
|
|
506
|
+
this.logger.debug('consumerChangePreferredLayers()', { ...payload });
|
|
499
507
|
}
|
|
500
508
|
});
|
|
501
509
|
|
|
@@ -53,79 +53,83 @@ class ChannelEventHandler {
|
|
|
53
53
|
throw new Error('Socket connection not initialized');
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
this.engine.eventsQueue.subscribeToSocket(connection);
|
|
57
|
+
|
|
58
|
+
this.engine.eventsQueue.on(CHANNEL_EVENTS.channelEvent, (event: ChannelEvent) => {
|
|
57
59
|
this.engine.clientEventEmitter.safeEmit(CLIENT_EVENTS.channelEvent, event);
|
|
58
60
|
this.logger.debug('New channel event', { event });
|
|
59
61
|
});
|
|
60
62
|
|
|
61
|
-
|
|
63
|
+
this.engine.eventsQueue.on(CHANNEL_EVENTS.channelJoin, (peerData: PeerResponse) => {
|
|
62
64
|
const peer = this.engine.setPeer(peerData);
|
|
63
65
|
this.logger.debug('Peer joined to the channel', { peer: logResponse(peer) });
|
|
64
66
|
});
|
|
65
67
|
|
|
66
|
-
|
|
68
|
+
this.engine.eventsQueue.on(CHANNEL_EVENTS.channelLeave, (peerId: string) => {
|
|
67
69
|
this.engine.removePeer(peerId);
|
|
68
70
|
this.logger.debug('Peer left the channel', { peerId });
|
|
69
71
|
});
|
|
70
72
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
this.engine.eventsQueue
|
|
74
|
+
.on(CHANNEL_EVENTS.activityConfirmationRequired, (event: ActivityConfirmationRequiredPayload) => {
|
|
75
|
+
this.engine.clientEventEmitter.safeEmit(CLIENT_EVENTS.activityConfirmationRequired, event);
|
|
76
|
+
this.logger.info('Notify the channel that activity confirmation is required', event);
|
|
77
|
+
});
|
|
75
78
|
|
|
76
|
-
|
|
79
|
+
this.engine.eventsQueue.on(CHANNEL_EVENTS.activityConfirmationExpired, (event: ChannelEvent) => {
|
|
77
80
|
this.engine.clientEventEmitter.safeEmit(CLIENT_EVENTS.activityConfirmationExpired, event);
|
|
78
81
|
this.logger.info('Notify the channel that it has disconnected due to the activity confirmation expiring', event);
|
|
79
82
|
});
|
|
80
83
|
|
|
81
|
-
|
|
84
|
+
this.engine.eventsQueue.on(CHANNEL_EVENTS.activityConfirmationAcquired, (event: ChannelEvent) => {
|
|
82
85
|
this.engine.clientEventEmitter.safeEmit(CLIENT_EVENTS.activityConfirmationAcquired, event);
|
|
83
86
|
this.logger.info('Notify the channel that the activity has been acquired.', event);
|
|
84
87
|
});
|
|
85
88
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
.
|
|
89
|
+
this.engine.eventsQueue
|
|
90
|
+
.on(CHANNEL_EVENTS.changeProducePermissions, (payload: ChannelChangeProducePermissionsPayload) => {
|
|
91
|
+
const { producePermissions, groups, changedProducePermissions } = payload;
|
|
92
|
+
const peers = this.engine.peers.filter((peer) => peer.groups.some((group) => groups.includes(group)));
|
|
93
|
+
peers.forEach((peer) => {
|
|
94
|
+
const newProducePermissions = Array.from(new Set([...peer.producePermissions, ...changedProducePermissions]))
|
|
95
|
+
.filter((label) => (changedProducePermissions.includes(label) ? producePermissions.includes(label) : true));
|
|
96
|
+
|
|
97
|
+
peer.handleProducePermissionsChange(newProducePermissions);
|
|
98
|
+
});
|
|
92
99
|
|
|
93
|
-
|
|
100
|
+
this.logger.info('Peers changed produce permissions', { groups, producePermissions });
|
|
94
101
|
});
|
|
95
102
|
|
|
96
|
-
|
|
97
|
-
|
|
103
|
+
this.engine.eventsQueue
|
|
104
|
+
.on(CHANNEL_EVENTS.changePeerProducePermissions, (payload: ChangePeerProducePermissionPayload) => {
|
|
105
|
+
const { peerId, producePermissions } = payload;
|
|
106
|
+
const peer = this.engine.peersRepository.get(peerId);
|
|
98
107
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
108
|
+
if (!peer) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
102
111
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
}
|
|
112
|
+
peer.handleProducePermissionsChange(producePermissions);
|
|
113
|
+
});
|
|
106
114
|
|
|
107
|
-
|
|
108
|
-
|
|
115
|
+
this.engine.eventsQueue
|
|
116
|
+
.on(CHANNEL_EVENTS.deleteProducePermissions, (payload: ChannelChangeProducePermissionsPayload) => {
|
|
117
|
+
const { producePermissions, groups } = payload;
|
|
118
|
+
const peers = this.engine.peers.filter((peer) => peer.groups.some((group) => groups.includes(group)));
|
|
119
|
+
peers.forEach((peer) => {
|
|
120
|
+
const newProducePermissions = peer
|
|
121
|
+
.producePermissions
|
|
122
|
+
.filter((permission) => !producePermissions.includes(permission));
|
|
109
123
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const peers = this.engine.peers.filter((peer) => peer.groups.some((group) => groups.includes(group)));
|
|
113
|
-
peers.forEach((peer) => {
|
|
114
|
-
const newProducePermissions = peer
|
|
115
|
-
.producePermissions
|
|
116
|
-
.filter((permission) => !producePermissions.includes(permission));
|
|
124
|
+
peer.handleProducePermissionsChange(newProducePermissions);
|
|
125
|
+
});
|
|
117
126
|
|
|
118
|
-
|
|
127
|
+
this.logger.info('Peers deleted produce permissions', { groups, producePermissions });
|
|
119
128
|
});
|
|
120
|
-
|
|
121
|
-
this.logger.info('Peers deleted produce permissions', { groups, producePermissions });
|
|
122
|
-
});
|
|
123
129
|
}
|
|
124
130
|
|
|
125
131
|
private removeEventListeners() {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if (!connection) {
|
|
132
|
+
if (!this.engine.eventsQueue) {
|
|
129
133
|
return;
|
|
130
134
|
}
|
|
131
135
|
|
|
@@ -140,7 +144,9 @@ class ChannelEventHandler {
|
|
|
140
144
|
CHANNEL_EVENTS.changeProducePermissions,
|
|
141
145
|
];
|
|
142
146
|
|
|
143
|
-
|
|
147
|
+
const { connection } = this.engine.network.socket;
|
|
148
|
+
|
|
149
|
+
this.engine.eventsQueue.removeAllListeners(eventNames, connection);
|
|
144
150
|
}
|
|
145
151
|
|
|
146
152
|
private handlePeerAppDataUpdate({ peerId, appData }: UpdatePeerAppDataPayload) {
|
package/src/engine/handlers/ChannelStateSyncEventHandler/ChannelStateConsistencyCheckResult.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
interface ChannelStateConsistencyCheckResultsParams {
|
|
2
2
|
missingPeers: string[];
|
|
3
|
+
missingConsumers: string[];
|
|
3
4
|
missingProducers: string[];
|
|
5
|
+
consumersWithInconsistentState: string[];
|
|
4
6
|
producersWithInconsistentState: string[];
|
|
5
7
|
peersWithInconsistentAppData: string[];
|
|
6
8
|
}
|
|
@@ -8,22 +10,30 @@ interface ChannelStateConsistencyCheckResultsParams {
|
|
|
8
10
|
class ChannelStateConsistencyCheckResult {
|
|
9
11
|
readonly missingPeers: string[];
|
|
10
12
|
|
|
13
|
+
readonly missingConsumers: string[];
|
|
14
|
+
|
|
11
15
|
readonly missingProducers: string[];
|
|
12
16
|
|
|
17
|
+
readonly consumersWithInconsistentState: string[];
|
|
18
|
+
|
|
13
19
|
readonly producersWithInconsistentState: string[];
|
|
14
20
|
|
|
15
21
|
readonly peersWithInconsistentAppData: string[];
|
|
16
22
|
|
|
17
23
|
constructor(params: ChannelStateConsistencyCheckResultsParams) {
|
|
18
24
|
this.missingPeers = params.missingPeers;
|
|
25
|
+
this.missingConsumers = params.missingConsumers;
|
|
19
26
|
this.missingProducers = params.missingProducers;
|
|
27
|
+
this.consumersWithInconsistentState = params.consumersWithInconsistentState;
|
|
20
28
|
this.producersWithInconsistentState = params.producersWithInconsistentState;
|
|
21
29
|
this.peersWithInconsistentAppData = params.peersWithInconsistentAppData;
|
|
22
30
|
}
|
|
23
31
|
|
|
24
32
|
hasInconsistencyCases() {
|
|
25
33
|
return this.missingPeers.length > 0
|
|
34
|
+
|| this.missingConsumers.length > 0
|
|
26
35
|
|| this.missingProducers.length > 0
|
|
36
|
+
|| this.consumersWithInconsistentState.length > 0
|
|
27
37
|
|| this.producersWithInconsistentState.length > 0
|
|
28
38
|
|| this.peersWithInconsistentAppData.length > 0;
|
|
29
39
|
}
|
|
@@ -20,8 +20,12 @@ class ChannelStateConsistencyChecker {
|
|
|
20
20
|
|
|
21
21
|
private missingPeers: string[] = [];
|
|
22
22
|
|
|
23
|
+
private missingConsumers: string[] = [];
|
|
24
|
+
|
|
23
25
|
private missingProducers: string[] = [];
|
|
24
26
|
|
|
27
|
+
private consumersWithInconsistentState: string[] = [];
|
|
28
|
+
|
|
25
29
|
private producersWithInconsistentState: string[] = [];
|
|
26
30
|
|
|
27
31
|
private peersWithInconsistentAppData: string[] = [];
|
|
@@ -55,9 +59,21 @@ class ChannelStateConsistencyChecker {
|
|
|
55
59
|
return;
|
|
56
60
|
}
|
|
57
61
|
|
|
62
|
+
const localConsumer = this.findLocalConsumerByProducerId(localPeer, remoteProducer.id);
|
|
63
|
+
if (localConsumer && localConsumer.paused !== remoteProducer.paused) {
|
|
64
|
+
this.consumersWithInconsistentState.push(localConsumer.id);
|
|
65
|
+
}
|
|
66
|
+
|
|
58
67
|
const localProducer = this.findLocalProducerById(localPeer, remoteProducer.id);
|
|
59
68
|
if (!localProducer) {
|
|
60
69
|
this.missingProducers.push(remoteProducer.id);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (!localConsumer && !localProducer) {
|
|
73
|
+
this.missingConsumers.push(remoteProducer.id);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (!localProducer) {
|
|
61
77
|
return;
|
|
62
78
|
}
|
|
63
79
|
|
|
@@ -70,7 +86,9 @@ class ChannelStateConsistencyChecker {
|
|
|
70
86
|
|
|
71
87
|
const checkResults = new ChannelStateConsistencyCheckResult({
|
|
72
88
|
missingPeers: this.missingPeers,
|
|
89
|
+
missingConsumers: this.missingConsumers,
|
|
73
90
|
missingProducers: this.missingProducers,
|
|
91
|
+
consumersWithInconsistentState: this.consumersWithInconsistentState,
|
|
74
92
|
producersWithInconsistentState: this.producersWithInconsistentState,
|
|
75
93
|
peersWithInconsistentAppData: this.peersWithInconsistentAppData,
|
|
76
94
|
});
|
|
@@ -82,10 +100,21 @@ class ChannelStateConsistencyChecker {
|
|
|
82
100
|
return this.missingPeers;
|
|
83
101
|
}
|
|
84
102
|
|
|
103
|
+
/**
|
|
104
|
+
* Contains list of remote producer ids for which local consumer is missing
|
|
105
|
+
*/
|
|
106
|
+
getMissingConsumers(): string[] {
|
|
107
|
+
return this.missingConsumers;
|
|
108
|
+
}
|
|
109
|
+
|
|
85
110
|
getMissingProducers(): string[] {
|
|
86
111
|
return this.missingProducers;
|
|
87
112
|
}
|
|
88
113
|
|
|
114
|
+
getConsumersWithInconsistentState(): string[] {
|
|
115
|
+
return this.consumersWithInconsistentState;
|
|
116
|
+
}
|
|
117
|
+
|
|
89
118
|
getProducersWithInconsistentState(): string[] {
|
|
90
119
|
return this.producersWithInconsistentState;
|
|
91
120
|
}
|
|
@@ -98,13 +127,19 @@ class ChannelStateConsistencyChecker {
|
|
|
98
127
|
return this.localState.peers.find((item) => item.id === id);
|
|
99
128
|
}
|
|
100
129
|
|
|
130
|
+
private findLocalConsumerByProducerId(peer: ChannelStatePeer, producerId: string) {
|
|
131
|
+
return peer.consumers?.find((item) => item.producerId === producerId);
|
|
132
|
+
}
|
|
133
|
+
|
|
101
134
|
private findLocalProducerById(peer: ChannelStatePeer, id: string): ChannelStateProducer | undefined {
|
|
102
135
|
return peer.producers.find((item) => item.id === id);
|
|
103
136
|
}
|
|
104
137
|
|
|
105
138
|
private resetPreviousCheckResults(): void {
|
|
106
139
|
this.missingPeers = [];
|
|
140
|
+
this.missingConsumers = [];
|
|
107
141
|
this.missingProducers = [];
|
|
142
|
+
this.consumersWithInconsistentState = [];
|
|
108
143
|
this.producersWithInconsistentState = [];
|
|
109
144
|
this.peersWithInconsistentAppData = [];
|
|
110
145
|
}
|
|
@@ -13,7 +13,7 @@ import ChannelStateConsistencyCheckResult from './ChannelStateConsistencyCheckRe
|
|
|
13
13
|
import EnhancedEventEmitter from '../../../EnhancedEventEmitter';
|
|
14
14
|
import { ChannelStateSyncEventHandlerParams, ClientObserverEvents } from '../../../types/engine';
|
|
15
15
|
import { CLIENT_EVENTS } from '../../../constants/events';
|
|
16
|
-
import { InconsistenceType } from './types';
|
|
16
|
+
import { InconsistenceType, StorageKey } from './types';
|
|
17
17
|
import {
|
|
18
18
|
BAD_NETWORK_SCORE,
|
|
19
19
|
EXCELLENT_NETWORK_SCORE,
|
|
@@ -28,11 +28,13 @@ class ChannelStateSyncEventHandler {
|
|
|
28
28
|
|
|
29
29
|
#confirmedMissingPeers: string[] = [];
|
|
30
30
|
|
|
31
|
+
#confirmedMissingConsumers: string[] = [];
|
|
32
|
+
|
|
31
33
|
#confirmedMissingProducers: string[] = [];
|
|
32
34
|
|
|
33
|
-
#
|
|
35
|
+
#confirmedConsumersWithInconsistentState: string[] = [];
|
|
34
36
|
|
|
35
|
-
#
|
|
37
|
+
#confirmedProducersWithInconsistentState: string[] = [];
|
|
36
38
|
|
|
37
39
|
#checkResults: ChannelStateConsistencyCheckResult[] = [];
|
|
38
40
|
|
|
@@ -118,6 +120,21 @@ class ChannelStateSyncEventHandler {
|
|
|
118
120
|
type: InconsistenceType.IncorrectProducerState,
|
|
119
121
|
});
|
|
120
122
|
});
|
|
123
|
+
|
|
124
|
+
if (confirmedInconsistencyResult.missingConsumers.length > 0) {
|
|
125
|
+
this.#confirmedMissingConsumers.push(...confirmedInconsistencyResult.missingConsumers);
|
|
126
|
+
this.clientEventEmitter.safeEmit(CLIENT_EVENTS.channelStateInconsistent, {
|
|
127
|
+
type: InconsistenceType.MissingConsumers,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
if (confirmedInconsistencyResult.consumersWithInconsistentState.length > 0) {
|
|
132
|
+
this.#confirmedConsumersWithInconsistentState
|
|
133
|
+
.push(...confirmedInconsistencyResult.consumersWithInconsistentState);
|
|
134
|
+
this.clientEventEmitter.safeEmit(CLIENT_EVENTS.channelStateInconsistent, {
|
|
135
|
+
type: InconsistenceType.IncorrectConsumerState,
|
|
136
|
+
});
|
|
137
|
+
}
|
|
121
138
|
}
|
|
122
139
|
|
|
123
140
|
private getConfirmedInconsistencyResult(): ChannelStateConsistencyCheckResult | undefined {
|
|
@@ -128,7 +145,11 @@ class ChannelStateSyncEventHandler {
|
|
|
128
145
|
|
|
129
146
|
const confirmedCheckResult = new ChannelStateConsistencyCheckResult({
|
|
130
147
|
missingPeers: this.getConfirmedMissingPeers(lastCheckResult.missingPeers),
|
|
148
|
+
missingConsumers: this.getConfirmedMissingConsumers(lastCheckResult.missingConsumers),
|
|
131
149
|
missingProducers: this.getConfirmedMissingProducers(lastCheckResult.missingProducers),
|
|
150
|
+
consumersWithInconsistentState: this.getConfirmedConsumersWithInconsistencState(
|
|
151
|
+
lastCheckResult.consumersWithInconsistentState,
|
|
152
|
+
),
|
|
132
153
|
producersWithInconsistentState: this.getConfirmedProducersWithInconsistencState(
|
|
133
154
|
lastCheckResult.producersWithInconsistentState,
|
|
134
155
|
),
|
|
@@ -176,8 +197,9 @@ class ChannelStateSyncEventHandler {
|
|
|
176
197
|
}
|
|
177
198
|
|
|
178
199
|
private isReachedMaxCheckRetries(
|
|
179
|
-
storageKey:
|
|
200
|
+
storageKey: StorageKey,
|
|
180
201
|
id: string,
|
|
202
|
+
maxRetries = MAX_CHECK_RETRIES_BEFORE_CONFIRM,
|
|
181
203
|
) {
|
|
182
204
|
let retries = 0;
|
|
183
205
|
this.#checkResults.forEach((checkResults) => {
|
|
@@ -186,17 +208,33 @@ class ChannelStateSyncEventHandler {
|
|
|
186
208
|
}
|
|
187
209
|
});
|
|
188
210
|
|
|
189
|
-
return retries >=
|
|
211
|
+
return retries >= maxRetries;
|
|
190
212
|
}
|
|
191
213
|
|
|
192
214
|
private getConfirmedMissingPeers(peerIds: string[]): string[] {
|
|
193
215
|
return peerIds.filter((peerId) => this.isReachedMaxCheckRetries('missingPeers', peerId));
|
|
194
216
|
}
|
|
195
217
|
|
|
218
|
+
private getConfirmedMissingConsumers(consumersIds: string[]): string[] {
|
|
219
|
+
return consumersIds.filter((consumerId) => this.isReachedMaxCheckRetries(
|
|
220
|
+
'missingConsumers',
|
|
221
|
+
consumerId,
|
|
222
|
+
1,
|
|
223
|
+
));
|
|
224
|
+
}
|
|
225
|
+
|
|
196
226
|
private getConfirmedMissingProducers(producerIds: string[]): string[] {
|
|
197
227
|
return producerIds.filter((producerId) => this.isReachedMaxCheckRetries('missingProducers', producerId));
|
|
198
228
|
}
|
|
199
229
|
|
|
230
|
+
private getConfirmedConsumersWithInconsistencState(consumersIds: string[]): string[] {
|
|
231
|
+
return consumersIds.filter((consumerId) => this.isReachedMaxCheckRetries(
|
|
232
|
+
'consumersWithInconsistentState',
|
|
233
|
+
consumerId,
|
|
234
|
+
1,
|
|
235
|
+
));
|
|
236
|
+
}
|
|
237
|
+
|
|
200
238
|
private getConfirmedProducersWithInconsistencState(producerIds: string[]): string[] {
|
|
201
239
|
return producerIds.filter((producerId) => this.isReachedMaxCheckRetries(
|
|
202
240
|
'producersWithInconsistentState',
|
|
@@ -219,7 +257,9 @@ class ChannelStateSyncEventHandler {
|
|
|
219
257
|
private getAllConfirmedInconsistentEntityIds(): string[] {
|
|
220
258
|
return [
|
|
221
259
|
...this.#confirmedMissingPeers,
|
|
260
|
+
...this.#confirmedMissingConsumers,
|
|
222
261
|
...this.#confirmedMissingProducers,
|
|
262
|
+
...this.#confirmedConsumersWithInconsistentState,
|
|
223
263
|
...this.#confirmedProducersWithInconsistentState,
|
|
224
264
|
];
|
|
225
265
|
}
|
|
@@ -234,6 +274,7 @@ class ChannelStateSyncEventHandler {
|
|
|
234
274
|
id: producer.id,
|
|
235
275
|
paused: producer.paused,
|
|
236
276
|
})),
|
|
277
|
+
consumers: peer.getConsumersState(),
|
|
237
278
|
})),
|
|
238
279
|
};
|
|
239
280
|
}
|
|
@@ -2,6 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
export enum InconsistenceType {
|
|
4
4
|
MissingPeer = 'missing-peer',
|
|
5
|
+
MissingConsumers = 'missing-consumers',
|
|
5
6
|
MissingProducer = 'missing-producer',
|
|
6
7
|
IncorrectProducerState = 'incorrect-producer-state',
|
|
8
|
+
IncorrectConsumerState = 'incorrect-consumer-state',
|
|
7
9
|
}
|
|
10
|
+
|
|
11
|
+
export type StorageKey = 'missingPeers'
|
|
12
|
+
| 'missingConsumers'
|
|
13
|
+
| 'missingProducers'
|
|
14
|
+
| 'consumersWithInconsistentState'
|
|
15
|
+
| 'producersWithInconsistentState'
|
|
16
|
+
| 'peersWithInconsistentAppData';
|