Files
slide/decks/gen2D/public/ratelimit.svg
T

120 lines
8.0 KiB
XML
Raw Normal View History

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 400" font-family="system-ui, sans-serif" font-size="13">
<defs>
<filter id="shadow" x="-2%" y="-2%" width="104%" height="104%">
<feDropShadow dx="1" dy="2" stdDeviation="2" flood-opacity="0.3"/>
</filter>
<marker id="arrow-blue" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#60a5fa"/>
</marker>
<marker id="arrow-green" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#4ade80"/>
</marker>
<marker id="arrow-red" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#f87171"/>
</marker>
<marker id="arrow-yellow" viewBox="0 0 10 10" refX="10" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" fill="#fbbf24"/>
</marker>
</defs>
<!-- Background -->
<rect width="800" height="400" fill="#0f172a" rx="8"/>
<!-- Title -->
<text x="400" y="30" font-size="16" fill="#e2e8f0" text-anchor="middle" font-weight="bold">Rate Limiting Architecture</text>
<!-- Request (incoming) -->
<rect x="20" y="140" width="100" height="50" rx="6" fill="#1a1a2e" stroke="#60a5fa" stroke-width="1.5" filter="url(#shadow)"/>
<text x="70" y="160" font-size="13" fill="#e2e8f0" text-anchor="middle" font-weight="bold">Request</text>
<text x="70" y="178" font-size="10" fill="#94a3b8" text-anchor="middle">POST /generate</text>
<!-- Arrow: Request → Gin Middleware -->
<line x1="122" y1="165" x2="168" y2="165" stroke="#60a5fa" stroke-width="2" marker-end="url(#arrow-blue)"/>
<!-- Gin Middleware -->
<rect x="170" y="130" width="140" height="70" rx="6" fill="#1a1a2e" stroke="#818cf8" stroke-width="1.5" filter="url(#shadow)"/>
<text x="240" y="158" font-size="13" fill="#e2e8f0" text-anchor="middle" font-weight="bold">Gin Middleware</text>
<text x="240" y="175" font-size="10" fill="#94a3b8" text-anchor="middle">RateLimiter()</text>
<text x="240" y="190" font-size="10" fill="#64748b" text-anchor="middle">IP + Auth extraction</text>
<!-- Arrow: Middleware → User Limiter -->
<line x1="312" y1="152" x2="358" y2="112" stroke="#60a5fa" stroke-width="2" marker-end="url(#arrow-blue)"/>
<text x="340" y="128" font-size="10" fill="#94a3b8" text-anchor="middle">per-user</text>
<!-- Arrow: Middleware → Global Limiter -->
<line x1="312" y1="178" x2="358" y2="218" stroke="#60a5fa" stroke-width="2" marker-end="url(#arrow-blue)"/>
<text x="340" y="204" font-size="10" fill="#94a3b8" text-anchor="middle">global</text>
<!-- User-level Limiter -->
<rect x="360" y="72" width="170" height="80" rx="6" fill="#1a1a2e" stroke="#818cf8" stroke-width="1.5" filter="url(#shadow)"/>
<text x="445" y="95" font-size="13" fill="#e2e8f0" text-anchor="middle" font-weight="bold">User-Level Limiter</text>
<text x="445" y="112" font-size="10" fill="#94a3b8" text-anchor="middle">key: userID</text>
<text x="445" y="126" font-size="10" fill="#fbbf24" text-anchor="middle">rate: 3 req/min</text>
<text x="445" y="140" font-size="10" fill="#fbbf24" text-anchor="middle">burst: 15</text>
<!-- Global-level Limiter -->
<rect x="360" y="195" width="170" height="80" rx="6" fill="#1a1a2e" stroke="#818cf8" stroke-width="1.5" filter="url(#shadow)"/>
<text x="445" y="218" font-size="13" fill="#e2e8f0" text-anchor="middle" font-weight="bold">Global-Level Limiter</text>
<text x="445" y="235" font-size="10" fill="#94a3b8" text-anchor="middle">key: "generate"</text>
<text x="445" y="249" font-size="10" fill="#fbbf24" text-anchor="middle">rate: 20 req/min</text>
<text x="445" y="263" font-size="10" fill="#fbbf24" text-anchor="middle">burst: 50</text>
<!-- Arrow: User Limiter → Redis -->
<line x1="532" y1="112" x2="578" y2="140" stroke="#60a5fa" stroke-width="2" marker-end="url(#arrow-blue)"/>
<!-- Arrow: Global Limiter → Redis -->
<line x1="532" y1="235" x2="578" y2="210" stroke="#60a5fa" stroke-width="2" marker-end="url(#arrow-blue)"/>
<!-- Redis (Token Bucket Lua Script) -->
<rect x="580" y="120" width="180" height="105" rx="6" fill="#1a1a2e" stroke="#f87171" stroke-width="1.5" filter="url(#shadow)"/>
<text x="670" y="145" font-size="14" fill="#f87171" text-anchor="middle" font-weight="bold">Redis</text>
<text x="670" y="163" font-size="11" fill="#e2e8f0" text-anchor="middle">Token Bucket</text>
<text x="670" y="180" font-size="11" fill="#e2e8f0" text-anchor="middle">Lua Script</text>
<text x="670" y="200" font-size="10" fill="#94a3b8" text-anchor="middle">INCR + EXPIRE + EVAL</text>
<text x="670" y="215" font-size="10" fill="#64748b" text-anchor="middle">Atomic operation</text>
<!-- Pass path: both limiters → proceed -->
<path d="M 445,152 L 445,320 L 200,320 L 200,260" fill="none" stroke="#4ade80" stroke-width="2" marker-end="url(#arrow-green)" opacity="0"/>
<line x1="445" y1="152" x2="445" y2="310" stroke="#4ade80" stroke-width="0" opacity="0"/>
<!-- Decision: Allow? -->
<!-- Proceed to handler (both pass) -->
<rect x="260" y="310" width="120" height="40" rx="6" fill="#1a1a2e" stroke="#4ade80" stroke-width="1.5" filter="url(#shadow)"/>
<text x="320" y="335" font-size="13" fill="#4ade80" text-anchor="middle" font-weight="bold">→ Handler</text>
<!-- Success flow: from middleware area down -->
<path d="M 240,200 L 240,330 L 258,330" fill="none" stroke="#4ade80" stroke-width="2" marker-end="url(#arrow-green)"/>
<text x="230" y="270" font-size="10" fill="#4ade80" text-anchor="middle" transform="rotate(-90 230 270)">allow</text>
<!-- HTTP 429 response path -->
<rect x="520" y="310" width="130" height="40" rx="6" fill="#1a1a2e" stroke="#f87171" stroke-width="1.5" filter="url(#shadow)"/>
<text x="585" y="328" font-size="13" fill="#f87171" text-anchor="middle" font-weight="bold">HTTP 429</text>
<text x="585" y="343" font-size="10" fill="#94a3b8" text-anchor="middle">Too Many Requests</text>
<!-- Arrow: Redis → 429 (rate exceeded) -->
<path d="M 670,225 L 670,330 L 652,330" fill="none" stroke="#f87171" stroke-width="2" marker-end="url(#arrow-red)"/>
<text x="670" y="275" font-size="10" fill="#f87171" text-anchor="middle">exceeded</text>
<!-- Fail-open path (Redis unavailable) -->
<rect x="600" y="50" width="160" height="40" rx="6" fill="#1a1a2e" stroke="#fbbf24" stroke-width="1.5" stroke-dasharray="6,3" filter="url(#shadow)"/>
<text x="680" y="68" font-size="12" fill="#fbbf24" text-anchor="middle" font-weight="bold">Fail-Open</text>
<text x="680" y="82" font-size="10" fill="#94a3b8" text-anchor="middle">Redis unavailable → allow</text>
<!-- Arrow: Redis → Fail-Open -->
<path d="M 720,120 L 720,92" fill="none" stroke="#fbbf24" stroke-width="1.5" stroke-dasharray="4,3" marker-end="url(#arrow-yellow)"/>
<text x="738" y="110" font-size="9" fill="#fbbf24" text-anchor="start">error</text>
<!-- Arrow: Fail-Open → Handler -->
<path d="M 600,70 L 320,70 L 320,308" fill="none" stroke="#fbbf24" stroke-width="1.5" stroke-dasharray="4,3" marker-end="url(#arrow-yellow)"/>
<!-- Legend -->
<rect x="20" y="365" width="760" height="28" rx="6" fill="#1e293b" stroke="#334155" stroke-width="1"/>
<line x1="40" y1="379" x2="70" y2="379" stroke="#60a5fa" stroke-width="2" marker-end="url(#arrow-blue)"/>
<text x="80" y="383" font-size="10" fill="#94a3b8">Normal</text>
<line x1="140" y1="379" x2="170" y2="379" stroke="#4ade80" stroke-width="2" marker-end="url(#arrow-green)"/>
<text x="180" y="383" font-size="10" fill="#94a3b8">Allow</text>
<line x1="220" y1="379" x2="250" y2="379" stroke="#f87171" stroke-width="2" marker-end="url(#arrow-red)"/>
<text x="260" y="383" font-size="10" fill="#94a3b8">Rate Exceeded</text>
<line x1="370" y1="379" x2="400" y2="379" stroke="#fbbf24" stroke-width="1.5" stroke-dasharray="4,3" marker-end="url(#arrow-yellow)"/>
<text x="410" y="383" font-size="10" fill="#94a3b8">Fail-Open (Redis down)</text>
</svg>