Skip to content

Commit 3fecb18

Browse files
committed
feat: add cleanup of prev stats
1 parent f2c7ebd commit 3fecb18

10 files changed

+127
-10
lines changed

src/detectors/FramesDroppedIssueDetector.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,25 @@ import {
55
IssueType,
66
WebRTCStatsParsed,
77
} from '../types';
8+
import { scheduleTask } from '../utils/tasks';
9+
import { CLEANUP_PREV_STATS_TTL_MS } from '../utils/constants';
810

911
class FramesDroppedIssueDetector implements IssueDetector {
1012
#lastProcessedStats: { [connectionId: string]: WebRTCStatsParsed | undefined } = {};
1113

1214
#framesDroppedThreshold = 0.5;
1315

1416
detect(data: WebRTCStatsParsed): IssueDetectorResult {
17+
const { connection: { id: connectionId } } = data;
1518
const issues = this.processData(data);
16-
this.#lastProcessedStats[data.connection.id] = data;
19+
this.#lastProcessedStats[connectionId] = data;
20+
21+
scheduleTask({
22+
taskId: connectionId,
23+
delayMs: CLEANUP_PREV_STATS_TTL_MS,
24+
callback: () => (delete this.#lastProcessedStats[connectionId]),
25+
});
26+
1727
return issues;
1828
}
1929

src/detectors/FramesEncodedSentIssueDetector.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,25 @@ import {
55
IssueType,
66
WebRTCStatsParsed,
77
} from '../types';
8+
import { scheduleTask } from '../utils/tasks';
9+
import { CLEANUP_PREV_STATS_TTL_MS } from '../utils/constants';
810

911
class FramesEncodedSentIssueDetector implements IssueDetector {
1012
#lastProcessedStats: { [connectionId: string]: WebRTCStatsParsed | undefined } = {};
1113

1214
#missedFramesThreshold = 0.15;
1315

1416
detect(data: WebRTCStatsParsed): IssueDetectorResult {
17+
const { connection: { id: connectionId } } = data;
1518
const issues = this.processData(data);
16-
this.#lastProcessedStats[data.connection.id] = data;
19+
this.#lastProcessedStats[connectionId] = data;
20+
21+
scheduleTask({
22+
taskId: connectionId,
23+
delayMs: CLEANUP_PREV_STATS_TTL_MS,
24+
callback: () => (delete this.#lastProcessedStats[connectionId]),
25+
});
26+
1727
return issues;
1828
}
1929

src/detectors/InboundNetworkIssueDetector.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,23 @@ import {
55
IssueType,
66
WebRTCStatsParsed,
77
} from '../types';
8+
import { scheduleTask } from '../utils/tasks';
9+
import { CLEANUP_PREV_STATS_TTL_MS } from '../utils/constants';
810

911
class InboundNetworkIssueDetector implements IssueDetector {
1012
#lastProcessedStats: { [connectionId: string]: WebRTCStatsParsed | undefined } = {};
1113

1214
detect(data: WebRTCStatsParsed): IssueDetectorResult {
15+
const { connection: { id: connectionId } } = data;
1316
const issues = this.processData(data);
14-
this.#lastProcessedStats[data.connection.id] = data;
17+
this.#lastProcessedStats[connectionId] = data;
18+
19+
scheduleTask({
20+
taskId: connectionId,
21+
delayMs: CLEANUP_PREV_STATS_TTL_MS,
22+
callback: () => (delete this.#lastProcessedStats[connectionId]),
23+
});
24+
1525
return issues;
1626
}
1727

src/detectors/NetworkMediaSyncIssueDetector.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,23 @@ import {
55
IssueType,
66
WebRTCStatsParsed,
77
} from '../types';
8+
import { scheduleTask } from '../utils/tasks';
9+
import { CLEANUP_PREV_STATS_TTL_MS } from '../utils/constants';
810

911
class NetworkMediaSyncIssueDetector implements IssueDetector {
1012
#lastProcessedStats: { [connectionId: string]: WebRTCStatsParsed | undefined } = {};
1113

1214
detect(data: WebRTCStatsParsed): IssueDetectorResult {
15+
const { connection: { id: connectionId } } = data;
1316
const issues = this.processData(data);
14-
this.#lastProcessedStats[data.connection.id] = data;
17+
this.#lastProcessedStats[connectionId] = data;
18+
19+
scheduleTask({
20+
taskId: connectionId,
21+
delayMs: CLEANUP_PREV_STATS_TTL_MS,
22+
callback: () => (delete this.#lastProcessedStats[connectionId]),
23+
});
24+
1525
return issues;
1626
}
1727

src/detectors/OutboundNetworkIssueDetector.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,23 @@ import {
55
IssueType,
66
WebRTCStatsParsed,
77
} from '../types';
8+
import { scheduleTask } from '../utils/tasks';
9+
import { CLEANUP_PREV_STATS_TTL_MS } from '../utils/constants';
810

911
class OutboundNetworkIssueDetector implements IssueDetector {
1012
#lastProcessedStats: { [connectionId: string]: WebRTCStatsParsed | undefined } = {};
1113

1214
detect(data: WebRTCStatsParsed): IssueDetectorResult {
15+
const { connection: { id: connectionId } } = data;
1316
const issues = this.processData(data);
14-
this.#lastProcessedStats[data.connection.id] = data;
17+
this.#lastProcessedStats[connectionId] = data;
18+
19+
scheduleTask({
20+
taskId: connectionId,
21+
delayMs: CLEANUP_PREV_STATS_TTL_MS,
22+
callback: () => (delete this.#lastProcessedStats[connectionId]),
23+
});
24+
1525
return issues;
1626
}
1727

src/detectors/QualityLimitationsIssueDetector.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,23 @@ import {
55
IssueType,
66
WebRTCStatsParsed,
77
} from '../types';
8+
import { scheduleTask } from '../utils/tasks';
9+
import { CLEANUP_PREV_STATS_TTL_MS } from '../utils/constants';
810

911
class QualityLimitationsIssueDetector implements IssueDetector {
1012
#lastProcessedStats: { [connectionId: string]: WebRTCStatsParsed | undefined } = {};
1113

1214
detect(data: WebRTCStatsParsed): IssueDetectorResult {
15+
const { connection: { id: connectionId } } = data;
1316
const issues = this.processData(data);
14-
this.#lastProcessedStats[data.connection.id] = data;
17+
this.#lastProcessedStats[connectionId] = data;
18+
19+
scheduleTask({
20+
taskId: connectionId,
21+
delayMs: CLEANUP_PREV_STATS_TTL_MS,
22+
callback: () => (delete this.#lastProcessedStats[connectionId]),
23+
});
24+
1525
return issues;
1626
}
1727

src/detectors/VideoCodecMismatchDetector.ts

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import {
55
IssueType,
66
WebRTCStatsParsed,
77
} from '../types';
8+
import { scheduleTask } from '../utils/tasks';
9+
import { CLEANUP_PREV_STATS_TTL_MS } from '../utils/constants';
810

911
class VideoCodecMismatchDetector implements IssueDetector {
1012
readonly UNKNOWN_DECODER = 'unknown';
@@ -16,8 +18,19 @@ class VideoCodecMismatchDetector implements IssueDetector {
1618
} = {};
1719

1820
detect(data: WebRTCStatsParsed): IssueDetectorResult {
21+
const { connection: { id: connectionId } } = data;
1922
const issues = this.processData(data);
20-
this.#lastProcessedStats[data.connection.id] = data;
23+
this.#lastProcessedStats[connectionId] = data;
24+
25+
scheduleTask({
26+
taskId: connectionId,
27+
delayMs: CLEANUP_PREV_STATS_TTL_MS,
28+
callback: () => {
29+
delete this.#lastProcessedStats[connectionId];
30+
delete this.#lastDecoderWithIssue[connectionId];
31+
},
32+
});
33+
2134
return issues;
2235
}
2336

src/utils/constants.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// eslint-disable-next-line import/prefer-default-export
2+
export const CLEANUP_PREV_STATS_TTL_MS = 35_000;

src/utils/tasks.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
export const createTaskScheduler = () => {
2+
const scheduledTasks = new Map<string, NodeJS.Timer>();
3+
4+
return (payload: {
5+
taskId: string;
6+
callback: () => void;
7+
delayMs: number;
8+
maxJitterMs?: number;
9+
}) => {
10+
const {
11+
taskId, delayMs, maxJitterMs, callback,
12+
} = payload;
13+
const jitter = Math.ceil(Math.random() * (maxJitterMs || 0));
14+
15+
const timer = scheduledTasks.get(taskId);
16+
17+
if (timer) {
18+
clearTimeout(timer);
19+
}
20+
21+
const newTimer = setTimeout(() => {
22+
callback();
23+
scheduledTasks.delete(taskId);
24+
}, delayMs + jitter);
25+
26+
scheduledTasks.set(taskId, newTimer);
27+
};
28+
};
29+
30+
export const scheduleTask = createTaskScheduler();

test/parser/PeriodicWebRTCStatsReporter.spec.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,19 @@ describe('wid/lib/PeriodicWebRTCStatsReporter', () => {
9292
expect(parseMethodDouble.getCalls()).to.have.length(numberOfIntervals);
9393
});
9494

95+
it('should emit stats parsed event', async () => {
96+
const getStatsInterval = faker.datatype.number({ min: 1, max: 9999 });
97+
const compositeStatsParser = createCompositeStatsParserFake();
98+
const reporter = createPeriodicStatsReporter({ compositeStatsParser, getStatsInterval });
99+
sandbox.stub(compositeStatsParser, 'parse');
100+
const emitSpy = sandbox.spy(reporter, 'emit');
101+
102+
reporter.startReporting();
103+
await clock.tickAsync(getStatsInterval);
104+
105+
expect(emitSpy).to.be.calledWith(PeriodicWebRTCStatsReporter.STATS_REPORTS_PARSED, { timeTaken: 0 });
106+
});
107+
95108
it('should emit stats report ready event for each stats report item', async () => {
96109
const getStatsInterval = faker.datatype.number({ min: 1, max: 9999 });
97110
const compositeStatsParser = createCompositeStatsParserFake();
@@ -105,9 +118,8 @@ describe('wid/lib/PeriodicWebRTCStatsReporter', () => {
105118
reporter.startReporting();
106119
await clock.tickAsync(getStatsInterval);
107120

108-
expect(emitSpy).to.be.calledTwice;
109-
expect(emitSpy.getCall(0)).to.be.calledWithExactly(PeriodicWebRTCStatsReporter.STATS_REPORT_READY_EVENT, firstReportItem);
110-
expect(emitSpy.getCall(1)).to.be.calledWithExactly(PeriodicWebRTCStatsReporter.STATS_REPORT_READY_EVENT, secondReportItem);
121+
expect(emitSpy).to.be.calledWith(PeriodicWebRTCStatsReporter.STATS_REPORT_READY_EVENT, firstReportItem);
122+
expect(emitSpy).to.be.calledWith(PeriodicWebRTCStatsReporter.STATS_REPORT_READY_EVENT, secondReportItem);
111123
});
112124

113125
it('should be be in stopped state', async () => {

0 commit comments

Comments
 (0)