1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
"""Counts what aiortc's SCTP sender does not, applied on import (aiortc 1.15).
aiortc halves its congestion window on every loss and drops it to one packet
when the T3 timer expires, and keeps no record of either. Those two counts are
what say whether a slow transfer was held back by the path: a window that never
grows past a few hundred KB is losses, and the node could not tell.
A loss is counted when the sender enters fast recovery, so a burst of packets
dropped together is one loss, which is also one halving of the window.
"""
from aiortc.rtcsctptransport import RTCSctpTransport
_receive_sack_chunk = RTCSctpTransport._receive_sack_chunk
_t3_expired = RTCSctpTransport._t3_expired
async def _counting_receive_sack_chunk(self, chunk) -> None:
recovering = self._fast_recovery_exit is not None
await _receive_sack_chunk(self, chunk)
if not recovering and self._fast_recovery_exit is not None:
self._mb_losses = getattr(self, "_mb_losses", 0) + 1
def _counting_t3_expired(self) -> None:
self._mb_t3 = getattr(self, "_mb_t3", 0) + 1
_t3_expired(self)
RTCSctpTransport._receive_sack_chunk = _counting_receive_sack_chunk
RTCSctpTransport._t3_expired = _counting_t3_expired
def describe(pc) -> str:
"""The association of `pc` as a log suffix, or nothing when it has none."""
reported = getattr(pc, "sctp_line", None)
if reported is not None: # held by a connection process (workers.py)
return reported
sctp = getattr(pc, "sctp", None)
if sctp is None:
return ""
srtt = getattr(sctp, "_srtt", None)
rtt = f"{srtt * 1000:.0f} ms" if srtt else "?"
return (f" sctp cwnd={sctp._cwnd // 1024} KB rtt={rtt}"
f" loss={getattr(sctp, '_mb_losses', 0)}"
f" t3={getattr(sctp, '_mb_t3', 0)}")
|