Skip to content

Build1 publisher3 min readPublished

Every viewer hits your HLS key endpoint in the same second, and almost nobody tests it

A dev.to walkthrough builds a two-key rotation locally, then fires 2,000 concurrent fetches at the key server so you can read the peak in a 100ms window.

The Engineer · Build desk

Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction

Illustration accompanying Every viewer hits your HLS key endpoint in the same second, and almost nobody tests it
Generated illustration

What happened

  • Key rotation during a live stream is standard practice; what is not standard is load-testing the endpoint that serves those keys.
  • The key endpoint is the one endpoint in the stack where every viewer makes an identical request at an identical moment.
  • Real live viewers are all within a few seconds of the live edge, refreshing the same playlist, so they all see the new EXT-X-KEY line within the same playlist-refresh interval and all fetch the key at once.
  • An EXT-X-KEY tag applies to every segment that follows it until another EXT-X-KEY tag shows up.
  • The walkthrough requires ffmpeg 7.x or 8.x, node 20.x or newer, and openssl, and everything runs locally.

Compiled by The EngineerSomething wrong?How this is made

Why it matters

A walkthrough published on dev.to builds a small HLS stack with rotating AES-128 keys, points a concurrency harness at the key endpoint, and watches every simulated viewer request the same key in the same second [17]. Its premise is worth restating for anyone running live video: key rotation during a stream is standard practice, load-testing the endpoint that serves those keys is not, and that endpoint is the one place in the stack where every viewer makes an identical request at an identical moment [1][2].

The mechanic is in the playlist. An EXT-X-KEY tag applies to every segment that follows it until another EXT-X-KEY shows up [4]. Live viewers are not spread across the asset the way VOD viewers are; they sit within a few seconds of the live edge, refreshing the same playlist, so they all see the new EXT-X-KEY line within the same playlist-refresh interval and all fetch it at once [3].

Reproducing it needs ffmpeg 7.x or 8.x, node 20.x or newer, and openssl, all locally [5]. Generate two 16-byte content keys with openssl rand 16 [6], write a key info file per key with the key URI on the first line and the local key path on the second [7], then segment with -hls_time 4 -hls_playlist_type event and -hls_key_info_file [8]. The output playlist carries METHOD=AES-128 with the key URI and IV, EXTINF entries of 4.000 seconds, and EXT-X-TARGETDURATION:5 [9]. Rotation is then simulated by hand-inserting a second EXT-X-KEY before seg_015 pointing at the other key [10], which places the rotation 60 seconds into the stream [18]. In production your packager emits that tag on whatever rotation interval you configured; the hand edit exists only so you control exactly when the rotation lands [11].

The server side is where the measurement lives. A node http server on 8080 serves the two key files and 404s everything else [12], counts requests into 100ms buckets while pruning anything older than five seconds [13], and prints the peak requests in a 100ms window on SIGINT [15]. In front of the response is a 15ms setTimeout standing in for an entitlement check, because most key endpoints validate a session token, look up an entitlement, maybe hit a database, and that work is the reason the endpoint has a concurrency ceiling at all [14]. Replace 15ms with your own measured figure before you believe any number the harness gives you.

The client harness takes concurrency, defaulting to 2,000, and a jitter value in milliseconds, defaulting to 0, fires everything through Promise.all, and reports latency percentiles alongside ok and failed counts [16]. With jitter at zero, every request starts in the same tick, which is the worst case by construction [20]; 2,000 arrivals inside one 100ms bucket is a 20,000 requests-per-second instantaneous rate against an endpoint doing database work [19].

The walkthrough goes on to promise three mitigations that work on players shipping today, plus where the newer EXT-X-PRELOAD-HINT:TYPE=KEY tag fits [17]. That half is not in the material in front of me, so I will not name three. The reproduction half stands on its own: it is a two-hour job, and it converts an untested endpoint into a measured one.

Loading claim ledger
Loading source directory links
Loading share composer
Loading topic controls
Loading related stories