@livedigital/client 3.23.1 → 3.24.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.
@@ -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
@@ -2,7 +2,7 @@
2
2
  "name": "@livedigital/client",
3
3
  "author": "vlprojects",
4
4
  "license": "MIT",
5
- "version": "3.23.1",
5
+ "version": "3.24.1",
6
6
  "private": false,
7
7
  "bugs": {
8
8
  "url": "https://github.com/vlprojects/livedigital-sdk/issues"
@@ -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
+ }
@@ -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;
@@ -3,7 +3,7 @@ import crypto from 'crypto';
3
3
 
4
4
  let skipGettingStorage = false;
5
5
  let fallbackClientUid: string | undefined;
6
- const CLIENT_UID_STORAGE_KEY = 'clientUniqueId';
6
+ const CLIENT_UID_STORAGE_KEY = 'sdk_clientUniqueId';
7
7
 
8
8
  const getLocalStorage = (): Storage | null => {
9
9
  try {
@@ -61,7 +61,7 @@ export const generateAndSetClientUniqueId = (): string => {
61
61
  return existingClientUniqueId;
62
62
  }
63
63
 
64
- const clientUniqueId = crypto.randomBytes(20).toString('hex');
64
+ const clientUniqueId = crypto.randomBytes(12).toString('hex');
65
65
  fallbackClientUid = clientUniqueId;
66
66
  setLocalStorageItem(CLIENT_UID_STORAGE_KEY, clientUniqueId);
67
67
 
@@ -53,79 +53,83 @@ class ChannelEventHandler {
53
53
  throw new Error('Socket connection not initialized');
54
54
  }
55
55
 
56
- connection.on(CHANNEL_EVENTS.channelEvent, (event: ChannelEvent) => {
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
- connection.on(CHANNEL_EVENTS.channelJoin, (peerData: PeerResponse) => {
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
- connection.on(CHANNEL_EVENTS.channelLeave, (peerId: string) => {
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
- connection.on(CHANNEL_EVENTS.activityConfirmationRequired, (event: ActivityConfirmationRequiredPayload) => {
72
- this.engine.clientEventEmitter.safeEmit(CLIENT_EVENTS.activityConfirmationRequired, event);
73
- this.logger.info('Notify the channel that activity confirmation is required', event);
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
- connection.on(CHANNEL_EVENTS.activityConfirmationExpired, (event: ChannelEvent) => {
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
- connection.on(CHANNEL_EVENTS.activityConfirmationAcquired, (event: ChannelEvent) => {
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
- connection.on(CHANNEL_EVENTS.changeProducePermissions, (payload: ChannelChangeProducePermissionsPayload) => {
87
- const { producePermissions, groups, changedProducePermissions } = payload;
88
- const peers = this.engine.peers.filter((peer) => peer.groups.some((group) => groups.includes(group)));
89
- peers.forEach((peer) => {
90
- const newProducePermissions = Array.from(new Set([...peer.producePermissions, ...changedProducePermissions]))
91
- .filter((label) => (changedProducePermissions.includes(label) ? producePermissions.includes(label) : true));
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
- peer.handleProducePermissionsChange(newProducePermissions);
100
+ this.logger.info('Peers changed produce permissions', { groups, producePermissions });
94
101
  });
95
102
 
96
- this.logger.info('Peers changed produce permissions', { groups, producePermissions });
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
- connection.on(CHANNEL_EVENTS.changePeerProducePermissions, (payload: ChangePeerProducePermissionPayload) => {
100
- const { peerId, producePermissions } = payload;
101
- const peer = this.engine.peersRepository.get(peerId);
108
+ if (!peer) {
109
+ return;
110
+ }
102
111
 
103
- if (!peer) {
104
- return;
105
- }
112
+ peer.handleProducePermissionsChange(producePermissions);
113
+ });
106
114
 
107
- peer.handleProducePermissionsChange(producePermissions);
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
- connection.on(CHANNEL_EVENTS.deleteProducePermissions, (payload: ChannelChangeProducePermissionsPayload) => {
111
- const { producePermissions, groups } = payload;
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
- peer.handleProducePermissionsChange(newProducePermissions);
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
- const { connection } = this.engine.network.socket;
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
- eventNames.forEach((x) => connection.removeAllListeners(x));
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) {
@@ -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
- #confirmedProducersWithInconsistentState: string[] = [];
35
+ #confirmedConsumersWithInconsistentState: string[] = [];
34
36
 
35
- #confirmedPeersWithInconsistentAppData: string[] = [];
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: 'missingPeers' | 'missingProducers' | 'producersWithInconsistentState' | 'peersWithInconsistentAppData',
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 >= MAX_CHECK_RETRIES_BEFORE_CONFIRM;
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';