Internal Architecture

Updated:
Author: Erkin Khidirov

This documentation is intended for system architects who want to understand the engineering decisions that allow slyber_turn to achieve high throughput with minimal CPU and memory overhead.

1. Network Stack: SO_REUSEPORT and Socket Buffer Auto Tuning

In classic TURN implementations, a single UDP socket on port 3478 becomes a bottleneck due to OS kernel-level lock contention when reading packets from a single queue.

  • Scaling Across CPU Cores: On Linux, slyber_turn by default creates as many UDP sockets on port 3478 as there are available CPU cores (TURN_THREADS = runtime.NumCPU()), setting the SO_REUSEPORT and SO_REUSEADDR socket flags. The Linux kernel automatically distributes incoming packets across the queues of different sockets, allowing pion/turn goroutines to process traffic in parallel without contending for a single file descriptor.
  • Cascading Tuning of SO_RCVBUF and SO_SNDBUF:
  • For control sockets (3478), the server sequentially attempts to set the receive and send buffer sizes to 8 MB → 4 MB → 1 MB → 512 KB (selecting the maximum size permitted by the kernel).
  • For each allocated relay socket (49152–65535), a buffer of 1 MB → 512 KB → 256 KB is automatically applied.

2. Non-Blocking Traffic Shaping and Telemetry Batching

Each relay connection is wrapped in trackedPacketConn (for UDP) or trackedConn (for TCP). Because tens of thousands of packets per second pass through these methods, mutexes (sync.Mutex) are completely eliminated from the hot path (ReadFrom / WriteTo):

  • Lock-Free bandwidthLimiter: The per-session rate limiter is implemented using atomic operations (atomic.Int64 + CompareAndSwap) with a 10 ms token replenishment window. When the limit is exceeded on a UDP socket, packets are dropped without blocking the thread, while on a TCP socket, a soft wait (wait) is applied.
  • Batched Traffic Counter Flushing: To avoid triggering global telemetry synchronization for every 100-byte voice packet, each session accumulates bytes in a local atomic variable unflushedBytes and flushes them to the global DefaultTelemetry counter only once 64 KB has been accumulated (or when the session is closed or polled).

3. Zero-Allocation Authentication and Memory Leak Protection

  • Stack-Based Password Computation: In the computePassword method (and in the client-side turnclient.GenerateLocal), buffers for the SHA1 hash (var sumBuf [20]byte) and Base64 (var b64Buf [28]byte) are allocated as fixed-size arrays on the stack, minimizing garbage collector (GC) pressure during mass client reconnections.
  • Hard Table Limits (maxTrackedEntries = 50000): Session tracking (sessions) and error deduplication (failDedup) tables are capped at 50,000 entries by an atomic counter and cleaned up in a background goroutine at most once every 30 seconds without halting request processing.

4. Linking Authentication and Allocation

Because the pion/turn library separates the password verification interface (AuthHandler) from the relay port allocation interface (RelayAddressGenerator), slyber_turn links them via a high-speed 64-element ring buffer authQueue and an atomic pointer lastAuth in NodeTelemetry. This makes it possible to accurately bind user_id and client_ip to a specific allocated UDP/TCP relay port at the moment of AllocatePacketConn / AllocateConn.