Class WebRtcChannelPool
Caches one open WebRtcDataChannelSession per Hub database so successive SQL queries on the same database reuse the negotiated peer connection. Without the pool every query pays the full ~1–3s setup cost (STUN gather + signaling + ICE checks + DTLS + SCTP). With the pool the first query is unchanged, all subsequent queries cost ~50–100 ms (just one DataChannel message round-trip).
Lifecycle of a pooled session (three concurrent timers — none of them holds the channel open "forever"):
- Idle timeout (default 60 s): closes when no
ExecuteAsynchas been issued for that long. Matches typical residential NAT UDP binding TTL — past this point the warm channel wouldn't be useful even if we kept it. - Max lifetime (default 10 min): absolute cap regardless of use. Prevents stale DTLS state and rotates the channel periodically.
- Keepalive ping (default every 25 s): sends a tiny "ping" message on the DataChannel. The Agent ack'es with "pong"; the outbound UDP packet alone refreshes the NAT mapping so the channel keeps working past the router's idle binding TTL.
Also evicts when the channel is observed dead (DTLS failure, Agent closed it, peer state change). The next caller gets a freshly negotiated session transparently.
Implements
Inherited Members
Namespace: Reportman.Hub.Client.DataChannel
Assembly: Reportman.DataChannel.dll
Syntax
public sealed class WebRtcChannelPool : IAsyncDisposable
Constructors
View SourceWebRtcChannelPool()
Declaration
public WebRtcChannelPool()
WebRtcChannelPool(TimeSpan, TimeSpan, TimeSpan)
Declaration
public WebRtcChannelPool(TimeSpan idleTimeout, TimeSpan maxLifetime, TimeSpan keepaliveInterval)
Parameters
| Type | Name | Description |
|---|---|---|
| TimeSpan | idleTimeout | |
| TimeSpan | maxLifetime | |
| TimeSpan | keepaliveInterval |
Methods
View SourceDisposeAsync()
Declaration
public ValueTask DisposeAsync()
Returns
| Type | Description |
|---|---|
| ValueTask |
EvictAllAsync()
Closes every cached session but keeps the pool itself usable. Call on logout / identity change — pooled sessions hold JWTs tied to the previous identity and shouldn't be reused under a new one. Subsequent GetOrOpenAsync(long, Func<CancellationToken, Task<WebRtcDataChannelSession?>>, CancellationToken) calls will negotiate fresh sessions.
Declaration
public Task EvictAllAsync()
Returns
| Type | Description |
|---|---|
| Task |
GetOrOpenAsync(long, Func<CancellationToken, Task<WebRtcDataChannelSession?>>, CancellationToken)
Returns a live session for hubDatabaseId, either
from cache or by negotiating a new one via the supplied opener. The
opener is the same delegate the caller used to call
TryOpenAsync(HttpClient, Uri, string?, object, Action<QueryProgress>?, CancellationToken) — passing it
keeps this class free of HttpClient/Uri/bearer plumbing details.
Returns null when opening fails (caller falls back to HTTP).
Declaration
public Task<WebRtcDataChannelSession?> GetOrOpenAsync(long hubDatabaseId, Func<CancellationToken, Task<WebRtcDataChannelSession?>> opener, CancellationToken ct)
Parameters
| Type | Name | Description |
|---|---|---|
| long | hubDatabaseId | |
| Func<CancellationToken, Task<WebRtcDataChannelSession>> | opener | |
| CancellationToken | ct |
Returns
| Type | Description |
|---|---|
| Task<WebRtcDataChannelSession> |