Skip to content

Commit 89bdd02

Browse files
authored
feat: add stale connection stats auto removal (#4)
* feat: add stale connection stats auto removal * feat: fix dependency vulnerability * feat: add tests to check closed connection stats * feat: cleanup unnecessary test files * feat: add new tests * feat: add maintainers and keywords to package json
1 parent 38b881f commit 89bdd02

30 files changed

+386
-878
lines changed

.github/workflows/release.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ jobs:
2727
- name: Lint
2828
run: yarn lint
2929

30+
- name: Lint
31+
run: yarn lint:tests
32+
3033
- name: Test
3134
run: yarn test
3235

package.json

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,36 @@
77
"types": "dist/index.d.ts",
88
"repository": "git@github.com:VLprojects/webrtc-issue-detector.git",
99
"author": "Roman Kuzakov <roman.kuzakov@gmail.com>",
10+
"maintainers": [
11+
{
12+
"name": "Vladimir Panov",
13+
"email": "panov.va@mail.ru",
14+
"url": "https://github.com/panov-va"
15+
},
16+
{
17+
"name": "Evgeny Melnikov",
18+
"email": "melnikov_evg@mail.ru",
19+
"url": "https://github.com/evgmel"
20+
}
21+
],
1022
"license": "MIT",
1123
"private": false,
1224
"homepage": "https://github.com/VLprojects/webrtc-issue-detector#readme",
1325
"keywords": [
14-
"webrtc"
26+
"webrtc",
27+
"stats",
28+
"rtcstatsreport",
29+
"network",
30+
"issues",
31+
"mos calculator"
1532
],
1633
"files": [
1734
"dist/"
1835
],
1936
"scripts": {
2037
"build": "rm -rf dist && rollup -c",
2138
"lint": "eslint ./src",
39+
"lint:tests": "cd test && eslint ./",
2240
"test": "NODE_ENV=test mocha --config test/utils/runners/mocha/.mocharc.js"
2341
},
2442
"devDependencies": {
@@ -30,7 +48,7 @@
3048
"@types/chai": "^4.3.3",
3149
"@types/chai-as-promised": "^7.1.5",
3250
"@types/chai-subset": "^1.3.3",
33-
"@types/faker": "^6.6.9",
51+
"@types/faker": "^5.5.9",
3452
"@types/mocha": "^9.1.1",
3553
"@types/node": "12",
3654
"@types/sinon": "^10.0.13",
@@ -46,7 +64,7 @@
4664
"eslint-plugin-import": "^2.26.0",
4765
"eslint-plugin-jsx-a11y": "^6.6.1",
4866
"faker": "^5.5.3",
49-
"mocha": "^10.0.0",
67+
"mocha": "^10.2.0",
5068
"rollup": "^2.78.0",
5169
"rollup-plugin-bundle-size": "^1.0.3",
5270
"rollup-plugin-polyfill-node": "^0.10.2",

src/NetworkScoresCalculator.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ class NetworkScoresCalculator implements INetworkScoresCalculator {
1010
#lastProcessedStats: { [connectionId: string]: WebRTCStatsParsed } = {};
1111

1212
calculate(data: WebRTCStatsParsed): NetworkScores {
13-
const outbound = this.calcucateOutboundScore(data);
13+
const outbound = this.calculateOutboundScore(data);
1414
const inbound = this.calculateInboundScore(data);
1515
this.#lastProcessedStats[data.connection.id] = data;
1616
return { outbound, inbound };
1717
}
1818

19-
private calcucateOutboundScore(data: WebRTCStatsParsed): NetworkScore | undefined {
19+
private calculateOutboundScore(data: WebRTCStatsParsed): NetworkScore | undefined {
2020
const remoteInboundRTPStreamsStats = [
2121
...data.remote?.audio.inbound || [],
2222
...data.remote?.video.inbound || [],

src/parser/RTCStatsParser.ts

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,27 +16,42 @@ import {
1616
} from '../types';
1717
import { checkIsConnectionClosed, calcBitrate } from './utils';
1818

19+
interface PrevStatsItem {
20+
stats: WebRTCStatsParsed;
21+
ts: number;
22+
}
23+
1924
interface WebRTCStatsParserParams {
2025
logger: Logger;
26+
prevConnectionStatsTtlMs?: number;
2127
}
2228

2329
class RTCStatsParser implements StatsParser {
24-
private readonly prevStats = new Map<string, { stats: WebRTCStatsParsed, ts: number } | undefined>();
30+
private readonly prevStats = new Map<string, PrevStatsItem | undefined>();
2531

26-
private readonly allowedReportTypes: RTCStatsType[] = [
32+
private readonly prevStatsCleanupTimers = new Map<string, NodeJS.Timer>();
33+
34+
private readonly allowedReportTypes: Set<RTCStatsType> = new Set<RTCStatsType>([
2735
'candidate-pair',
2836
'inbound-rtp',
2937
'outbound-rtp',
3038
'remote-outbound-rtp',
3139
'remote-inbound-rtp',
3240
'track',
3341
'transport',
34-
];
42+
]);
3543

3644
private readonly logger: Logger;
3745

46+
private readonly prevConnectionStatsTtlMs: number;
47+
3848
constructor(params: WebRTCStatsParserParams) {
3949
this.logger = params.logger;
50+
this.prevConnectionStatsTtlMs = params.prevConnectionStatsTtlMs ?? 55_000;
51+
}
52+
53+
get previouslyParsedStatsConnectionsIds(): string[] {
54+
return [...this.prevStats.keys()];
4055
}
4156

4257
async parse(connection: ConnectionInfo): Promise<StatsReportItem | undefined> {
@@ -93,25 +108,28 @@ class RTCStatsParser implements StatsParser {
93108

94109
reports.forEach((rtcStats: RTCStatsReport) => {
95110
rtcStats.forEach((reportItem: Record<string, unknown>) => {
96-
if (!this.allowedReportTypes.includes(reportItem.type as RTCStatsType)) {
111+
if (!this.allowedReportTypes.has(reportItem.type as RTCStatsType)) {
97112
return;
98113
}
99114

100115
this.updateMappedStatsWithReportItemData(reportItem, mappedStats, rtcStats);
101116
});
102117
});
103118

104-
const prevStatsData = this.prevStats.get(connectionData.id);
119+
const { id: connectionId } = connectionData;
120+
const prevStatsData = this.prevStats.get(connectionId);
105121

106122
if (prevStatsData) {
107123
this.propagateStatsWithRateValues(mappedStats, prevStatsData.stats);
108124
}
109125

110-
this.prevStats.set(connectionData.id, {
126+
this.prevStats.set(connectionId, {
111127
stats: mappedStats,
112128
ts: Date.now(),
113129
});
114130

131+
this.resetStatsCleanupTimer(connectionId);
132+
115133
return mappedStats;
116134
}
117135

@@ -269,6 +287,21 @@ class RTCStatsParser implements StatsParser {
269287

270288
return connectionStats as ParsedConnectionStats;
271289
}
290+
291+
private resetStatsCleanupTimer(connectionId: string): void {
292+
const existingTimer = this.prevStatsCleanupTimers.get(connectionId);
293+
294+
if (existingTimer) {
295+
clearTimeout(existingTimer);
296+
}
297+
298+
const timer = setTimeout(() => {
299+
this.prevStats.delete(connectionId);
300+
this.prevStatsCleanupTimers.delete(connectionId);
301+
}, this.prevConnectionStatsTtlMs);
302+
303+
this.prevStatsCleanupTimers.set(connectionId, timer);
304+
}
272305
}
273306

274307
export default RTCStatsParser;

test/.eslintrc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,5 +11,8 @@
1111
"import/prefer-default-export": "off",
1212
"@typescript-eslint/no-unused-expressions": "off",
1313
"class-methods-use-this": "off"
14-
}
14+
},
15+
"ignorePatterns": [
16+
"**/*.js"
17+
]
1518
}

test/helpers/rtc.ts

Lines changed: 0 additions & 68 deletions
This file was deleted.

test/parser/CompositeRTCStatsParser.spec.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1+
import { beforeEach } from 'mocha';
12
import sinon from 'sinon';
23
import { expect } from 'chai';
3-
import { createPeerConnectionFake, createStatsReportItem } from '../helpers/rtc';
4-
import { StatsParser, StatsReportItem } from '../../src/types';
4+
import { createPeerConnectionFake, createStatsReportItem } from '../utils/rtc';
5+
import { StatsParser, StatsReportItem } from '../../src';
56
import CompositeRTCStatsParser from '../../src/parser/CompositeRTCStatsParser';
67

78
const createStatsParserFake = (): StatsParser => ({
@@ -19,6 +20,10 @@ const createCompositeStatsParser = (
1920
describe('wid/lib/parser/CompositeRTCStatsParser', () => {
2021
const sandbox = sinon.createSandbox();
2122

23+
beforeEach(() => {
24+
sandbox.restore();
25+
});
26+
2227
after(() => {
2328
sandbox.restore();
2429
});

test/parser/PeriodicWebRTCStatsReporter.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import faker from 'faker';
22
import sinon, { SinonFakeTimers } from 'sinon';
33
import { expect } from 'chai';
4-
import { StatsReportItem, CompositeStatsParser } from '../../src/types';
5-
import { createStatsReportItem } from '../helpers/rtc';
4+
import { StatsReportItem, CompositeStatsParser } from '../../src';
5+
import { createStatsReportItem } from '../utils/rtc';
66
import PeriodicWebRTCStatsReporter from '../../src/parser/PeriodicWebRTCStatsReporter';
77

88
const createCompositeStatsParserFake = (): CompositeStatsParser => ({

0 commit comments

Comments
 (0)