<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: agustin18</title>
    <description>The latest articles on DEV Community by agustin18 (@agustin18).</description>
    <link>https://dev.to/agustin18</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4194347%2Fa4f665cc-5095-4ab3-acf4-3b5663aef315.jpg</url>
      <title>DEV Community: agustin18</title>
      <link>https://dev.to/agustin18</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/agustin18"/>
    <language>en</language>
    <item>
      <title>Why standard rate limiters fail for LLM &amp; SSE streams in FastAPI</title>
      <dc:creator>agustin18</dc:creator>
      <pubDate>Mon, 19 Oct 2026 14:49:48 +0000</pubDate>
      <link>https://dev.to/agustin18/why-standard-rate-limiters-fail-for-llm-sse-streams-in-fastapi-3jce</link>
      <guid>https://dev.to/agustin18/why-standard-rate-limiters-fail-for-llm-sse-streams-in-fastapi-3jce</guid>
      <description>&lt;p&gt;If you are building an AI chat assistant, real-time dashboard, or financial ticker in Python using FastAPI, chances are you are using Server-Sent Events (&lt;code&gt;StreamingResponse&lt;/code&gt;) or WebSockets.&lt;/p&gt;

&lt;p&gt;And if you care about uptime and cloud costs, you probably installed a rate-limiting middleware like &lt;code&gt;slowapi&lt;/code&gt; or configured a Redis token bucket.&lt;/p&gt;

&lt;p&gt;Here is the problem: &lt;strong&gt;standard rate limiters are designed for request frequency, not connection duration.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In this post, we'll break down why traditional rate limiters collapse under streaming workloads, and how to implement a distributed lease pattern using Redis to protect your workers.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Root Cause: Frequency vs. Duration
&lt;/h2&gt;

&lt;p&gt;Traditional HTTP endpoints are transactional:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Client sends request.&lt;/li&gt;
&lt;li&gt;Server queries DB (50ms).&lt;/li&gt;
&lt;li&gt;Server returns JSON response.&lt;/li&gt;
&lt;li&gt;Connection closes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In this model, limiting by frequency (&lt;strong&gt;10 requests per minute&lt;/strong&gt;) works reliably.&lt;/p&gt;

&lt;h3&gt;
  
  
  What Happens with Streaming (SSE / LLM Tokens)?
&lt;/h3&gt;

&lt;p&gt;An LLM stream (OpenAI, Claude, Ollama) stays open for &lt;strong&gt;30 to 60 seconds&lt;/strong&gt; while tokens are yielded chunk by chunk.&lt;/p&gt;

&lt;p&gt;If you have a rate limit of &lt;strong&gt;10 requests per minute&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A malicious or impatient user opens &lt;strong&gt;10 browser tabs simultaneously&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Each tab requests an SSE stream.&lt;/li&gt;
&lt;li&gt;The rate limiter inspects the counter: &lt;em&gt;"10 requests in 60s? Allowed!"&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The result:&lt;/strong&gt; That single user now holds &lt;strong&gt;10 open sockets concurrently for a full minute&lt;/strong&gt;.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Standard Rate Limiting (Permissive Failure):
User Request 1 ──► [OK] ──► Holding Socket (60s) ──────┐
User Request 2 ──► [OK] ──► Holding Socket (60s) ──────┤
User Request 3 ──► [OK] ──► Holding Socket (60s) ──────┼──► ASGI Worker Pool Exhausted!
User Request 4 ──► [OK] ──► Holding Socket (60s) ──────┤    (HTTP 502 / Socket Starvation)
User Request 5 ──► [OK] ──► Holding Socket (60s) ──────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If your Uvicorn worker pool has 100 concurrent connection limits, just 10 users doing this simultaneously will &lt;strong&gt;starve the worker&lt;/strong&gt;, causing HTTP 502/504 timeouts for all regular API endpoints.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Solution: Connection Leases, Not Request Counts
&lt;/h2&gt;

&lt;p&gt;To protect streaming endpoints, you need to answer a different question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"How many active streams is this user holding RIGHT NOW?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Instead of an incrementing counter with a TTL, you need a &lt;strong&gt;distributed lease mechanism&lt;/strong&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Before streaming:&lt;/strong&gt; Try to acquire an atomic slot for &lt;code&gt;user_id&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If &lt;code&gt;active_slots &amp;gt;= limit&lt;/code&gt;:&lt;/strong&gt; Reject immediately with &lt;strong&gt;HTTP 429 Too Many Requests&lt;/strong&gt; &lt;em&gt;before&lt;/em&gt; triggering the LLM generation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;While streaming:&lt;/strong&gt; Hold the lease and periodically refresh it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;On completion or client disconnect:&lt;/strong&gt; Release the slot immediately.&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Implementing Atomic Leases in Redis (Lua CAS)
&lt;/h2&gt;

&lt;p&gt;A naive implementation using multiple Redis commands (&lt;code&gt;GET&lt;/code&gt; then &lt;code&gt;INCR&lt;/code&gt;) introduces race conditions. Two parallel requests could both read &lt;code&gt;count=1&lt;/code&gt; and both succeed, violating your concurrency limit.&lt;/p&gt;

&lt;p&gt;To make lease acquisition atomic, we use Redis Sorted Sets (&lt;code&gt;ZSET&lt;/code&gt;) where:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Key:&lt;/strong&gt; &lt;code&gt;stream_lease:{user_id}&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Score:&lt;/strong&gt; Current Unix epoch timestamp&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Member:&lt;/strong&gt; Unique &lt;code&gt;lease_id&lt;/code&gt; (UUID)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is the atomic Lua script pattern ($O(\log N)$):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;KEYS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;tonumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ARGV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;max_leases&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;tonumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ARGV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;ttl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;tonumber&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ARGV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;lease_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ARGV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

&lt;span class="c1"&gt;-- 1. Remove expired/stale leases&lt;/span&gt;
&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ZREMRANGEBYSCORE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'-inf'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;ttl&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;-- 2. Check current active lease count&lt;/span&gt;
&lt;span class="kd"&gt;local&lt;/span&gt; &lt;span class="n"&gt;current_count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ZCARD'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;current_count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="n"&gt;max_leases&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="c1"&gt;-- Rejected (Concurrency limit reached)&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;-- 3. Acquire lease&lt;/span&gt;
&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ZADD'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;lease_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'EXPIRE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ttl&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="c1"&gt;-- Allowed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When the client disconnects or the generator completes, a cleanup script removes the &lt;code&gt;lease_id&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight lua"&gt;&lt;code&gt;&lt;span class="n"&gt;redis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ZREM'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;KEYS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;ARGV&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Handling the Edge Cases in Production
&lt;/h2&gt;

&lt;p&gt;In real-world microservices, three critical failure modes occur:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Zombie Tasks on Abrupt Disconnections
&lt;/h3&gt;

&lt;p&gt;If a user closes their laptop mid-stream, Starlette detects the socket disconnect via &lt;code&gt;request.is_disconnected()&lt;/code&gt;. But if your generator doesn't actively poll this flag or catch the &lt;code&gt;CancelledError&lt;/code&gt;, the background task keeps running, consuming downstream AI tokens and keeping the Redis lease alive until expiration.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Redis Sentinel Failover (&lt;code&gt;ReadOnlyError&lt;/code&gt;)
&lt;/h3&gt;

&lt;p&gt;When Redis switches from primary to replica, pending writes raise &lt;code&gt;ReadOnlyError&lt;/code&gt;. If your lease manager doesn't catch this, your entire API crashes. You need an automated &lt;strong&gt;fail-open&lt;/strong&gt; or &lt;strong&gt;fail-closed&lt;/strong&gt; fallback policy.&lt;/p&gt;




&lt;h2&gt;
  
  
  Enter &lt;code&gt;fastapi-stream-lease&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Instead of writing custom Lua scripts, disconnect wrappers, and metrics hooks in every microservice, I packaged this into a small, dependency-light library:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/agustin18/fastapi-stream-lease" rel="noopener noreferrer"&gt;fastapi-stream-lease on GitHub&lt;/a&gt;&lt;/strong&gt; (MIT, 100% test coverage)&lt;/p&gt;

&lt;h3&gt;
  
  
  1-Line Integration Example
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;FastAPI&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;fastapi_stream_lease&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;StreamLeaseManager&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;LeaseConfig&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;FastAPI&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;manager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;StreamLeaseManager&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;redis_url&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;redis://localhost:6379&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Register 1-line standard 429 exception handler
&lt;/span&gt;&lt;span class="n"&gt;manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register_exception_handlers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nd"&gt;@app.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/stream&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;chat_stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;token_generator&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="nf"&gt;range&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Token &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt; &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;

    &lt;span class="c1"&gt;# Acquires atomic Redis lease, wraps StreamingResponse,
&lt;/span&gt;    &lt;span class="c1"&gt;# detects disconnects, and releases slot on finish or error.
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="n"&gt;manager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;content&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nf"&gt;token_generator&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="n"&gt;media_type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;text/event-stream&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nc"&gt;LeaseConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_limit&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ttl_seconds&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  What You Get Out of the Box:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1-Line Integration:&lt;/strong&gt; Wrap any async generator in &lt;code&gt;manager.stream()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Atomic Concurrency Bounds:&lt;/strong&gt; Backed by Redis Lua CAS ($O(\log N)$).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graceful Fallback:&lt;/strong&gt; In-memory circuit breaker handles Redis failovers safely.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Production Observability:&lt;/strong&gt; Native Prometheus and OpenTelemetry gauges (&lt;code&gt;fastapi_stream_lease_active_streams&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Resilient Reconnection:&lt;/strong&gt; Disconnect hooks clean up slots immediately.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;When designing systems for LLM streaming and SSE, remember:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rate limiters&lt;/strong&gt; control how fast users knock on your door.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Lease managers&lt;/strong&gt; control how many rooms they can occupy at once.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you are running streaming endpoints in production, you need both.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Repo: &lt;a href="https://github.com/agustin18/fastapi-stream-lease" rel="noopener noreferrer"&gt;github.com/agustin18/fastapi-stream-lease&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;PyPI: &lt;a href="https://pypi.org/project/fastapi-stream-lease/" rel="noopener noreferrer"&gt;pypi.org/project/fastapi-stream-lease&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;(Note: English is not my native language; drafted with some AI assistance for translation and phrasing, but the architecture and code come directly from our production experience).&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;How are you currently handling streaming concurrency in your stack? Feedback and architecture critiques are welcome in the comments!&lt;/p&gt;

</description>
      <category>python</category>
      <category>fastapi</category>
      <category>webdev</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
