Skip to main content

pkg/gateway/server/ratelimiter.go

Source

Overview

What: In-memory per-client rate limiter used by the server middleware.

Why: Provides basic protection against bursts and accidental abuse without requiring an external datastore.

How: Uses golang.org/x/time/rate per client key and periodically evicts inactive clients to bound memory usage.

Contents

Imports

import block 1

pkg/gateway/server/ratelimiter.go#L3
import (
"sync"
"time"

"golang.org/x/time/rate"
)

Types

type block 1

pkg/gateway/server/ratelimiter.go#L10
type clientLimiter struct {
limiter *rate.Limiter
lastSeen time.Time
}

clientLimiter

What: Per-client bucket holding a rate.Limiter and last-seen timestamp.

Why: Tracks both rate state and usage recency for cleanup.

How: Stored in rateLimiter.clients keyed by client identifier.

type block 2

pkg/gateway/server/ratelimiter.go#L15
type rateLimiter struct {
mu sync.Mutex
window time.Duration
max int
limit rate.Limit
clients map[string]*clientLimiter
nextCleanup time.Time
}

rateLimiter

What: Tracks rate limiting configuration and per-client limiters.

Why: Keeps rate limiting state isolated from request handlers and encapsulates concurrency control.

How: Uses a mutex to protect shared state, computes a per-second limit from window/max, and stores limiters in a map by client key.

Functions and Methods

newRateLimiter

What: Constructs a rateLimiter from a window duration and max requests per window.

Why: Encapsulates rate math and creates a safe "disabled" limiter when config is invalid.

How: Converts window+max into a rate.Limit, initializes the per-client map, and returns a limiter that allows all requests when window/max are non-positive.

pkg/gateway/server/ratelimiter.go#L24
func newRateLimiter(window time.Duration, max int) *rateLimiter {
if window <= 0 || max <= 0 {
return &rateLimiter{
window: 0,
max: 0,
}
}

seconds := window.Seconds()
if seconds <= 0 {
seconds = 1
}

limit := rate.Limit(float64(max) / seconds)
if limit <= 0 {
limit = rate.Every(window / time.Duration(max))
}

return &rateLimiter{
window: window,
max: max,
limit: limit,
clients: make(map[string]*clientLimiter),
}
}

Walkthrough

The list below documents the statements inside the function body, including nested blocks and inline closures.

  • L25: if window <= 0 || max <= 0 { return &rateLimiter{ window: 0, max: 0, } }
    • What: Branches conditionally.
    • Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
    • How: Evaluates the condition and executes the matching branch.
    • Nested steps:
      • L26: return &rateLimiter{ window: 0, max: 0, }
        • What: Returns from the current function.
        • Why: Ends the current execution path and hands control back to the caller.
        • How: Executes a return statement (possibly returning values).
  • L32: seconds := window.Seconds()
    • What: Defines seconds.
    • Why: Keeps intermediate state available for later steps in the function.
    • How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
  • L33: if seconds <= 0 { seconds = 1 }
    • What: Branches conditionally.
    • Why: Handles different execution paths based on runtime state.
    • How: Evaluates the condition and executes the matching branch.
    • Nested steps:
      • L34: seconds = 1
        • What: Assigns seconds.
        • Why: Keeps intermediate state available for later steps in the function.
        • How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
  • L37: limit := rate.Limit(float64(max) / seconds)
    • What: Defines limit.
    • Why: Keeps intermediate state available for later steps in the function.
    • How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
  • L38: if limit <= 0 { limit = rate.Every(window / time.Duration(max)) }
    • What: Branches conditionally.
    • Why: Handles different execution paths based on runtime state.
    • How: Evaluates the condition and executes the matching branch.
    • Nested steps:
      • L39: limit = rate.Every(window / time.Duration(max))
        • What: Assigns limit.
        • Why: Keeps intermediate state available for later steps in the function.
        • How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
  • L42: return &rateLimiter{ window: window, max: max, limit: limit, clients: make(map[string]*clientLimiter), }
    • What: Returns from the current function.
    • Why: Ends the current execution path and hands control back to the caller.
    • How: Executes a return statement (possibly returning values).

(*rateLimiter).allow

What: Returns whether a request should be allowed for a given client key at a given time.

Why: This is the hot-path check used by the rate limit middleware.

How: Creates a per-key limiter on first sight, updates lastSeen, calls Allow(), and occasionally triggers cleanup of stale client entries.

pkg/gateway/server/ratelimiter.go#L50
func (r *rateLimiter) allow(key string, now time.Time) bool {
if r == nil || r.window <= 0 || r.max <= 0 {
return true
}

r.mu.Lock()
defer r.mu.Unlock()

if r.clients == nil {
r.clients = make(map[string]*clientLimiter)
}

client, ok := r.clients[key]
if !ok {
client = &clientLimiter{
limiter: rate.NewLimiter(r.limit, r.max),
lastSeen: now,
}
r.clients[key] = client
} else {
client.lastSeen = now
}

allowed := client.limiter.Allow()

if r.nextCleanup.IsZero() || now.After(r.nextCleanup) {
r.cleanupLocked(now)
r.nextCleanup = now.Add(r.window)
}

return allowed
}

Walkthrough

The list below documents the statements inside the function body, including nested blocks and inline closures.

  • L51: if r == nil || r.window <= 0 || r.max <= 0 { return true }
    • What: Branches conditionally.
    • Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
    • How: Evaluates the condition and executes the matching branch.
    • Nested steps:
      • L52: return true
        • What: Returns from the current function.
        • Why: Ends the current execution path and hands control back to the caller.
        • How: Executes a return statement (possibly returning values).
  • L55: r.mu.Lock()
    • What: Calls r.mu.Lock.
    • Why: Performs side effects or delegates work to a helper.
    • How: Executes the expression statement.
  • L56: defer r.mu.Unlock()
    • What: Defers a call for cleanup.
    • Why: Ensures the deferred action runs even on early returns.
    • How: Schedules the call to run when the surrounding function returns.
  • L58: if r.clients == nil { r.clients = make(map[string]*clientLimiter) }
    • What: Branches conditionally.
    • Why: Handles different execution paths based on runtime state.
    • How: Evaluates the condition and executes the matching branch.
    • Nested steps:
      • L59: r.clients = make(map[string]*clientLimiter)
        • What: Assigns r.clients.
        • Why: Keeps intermediate state available for later steps in the function.
        • How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
  • L62: client, ok := r.clients[key]
    • What: Defines client, ok.
    • Why: Keeps intermediate state available for later steps in the function.
    • How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
  • L63: if !ok { client = &clientLimiter{ limiter: rate.NewLimiter(r.limit, r.max), lastSeen: now, } r.clients[key] = client } else { client.lastSe…
    • What: Branches conditionally.
    • Why: Handles different execution paths based on runtime state.
    • How: Evaluates the condition and executes the matching branch.
    • Nested steps:
      • L64: client = &clientLimiter{ limiter: rate.NewLimiter(r.limit, r.max), lastSeen: now, }
        • What: Assigns client.
        • Why: Keeps intermediate state available for later steps in the function.
        • How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
      • L68: r.clients[key] = client
        • What: Assigns r.clients[key].
        • Why: Keeps intermediate state available for later steps in the function.
        • How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
      • L70: client.lastSeen = now
        • What: Assigns client.lastSeen.
        • Why: Keeps intermediate state available for later steps in the function.
        • How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
  • L73: allowed := client.limiter.Allow()
    • What: Defines allowed.
    • Why: Keeps intermediate state available for later steps in the function.
    • How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
  • L75: if r.nextCleanup.IsZero() || now.After(r.nextCleanup) { r.cleanupLocked(now) r.nextCleanup = now.Add(r.window) }
    • What: Branches conditionally.
    • Why: Handles different execution paths based on runtime state.
    • How: Evaluates the condition and executes the matching branch.
    • Nested steps:
      • L76: r.cleanupLocked(now)
        • What: Calls r.cleanupLocked.
        • Why: Performs side effects or delegates work to a helper.
        • How: Executes the expression statement.
      • L77: r.nextCleanup = now.Add(r.window)
        • What: Assigns r.nextCleanup.
        • Why: Keeps intermediate state available for later steps in the function.
        • How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
  • L80: return allowed
    • What: Returns from the current function.
    • Why: Ends the current execution path and hands control back to the caller.
    • How: Executes a return statement (possibly returning values).

(*rateLimiter).cleanupLocked

What: Removes client limiter entries that have not been seen recently.

Why: Prevents unbounded growth of the clients map in long-running processes.

How: Deletes clients whose lastSeen is older than two windows, assuming they're no longer active.

pkg/gateway/server/ratelimiter.go#L83
func (r *rateLimiter) cleanupLocked(now time.Time) {
if r.clients == nil {
return
}

threshold := now.Add(-2 * r.window)
for key, client := range r.clients {
if client.lastSeen.Before(threshold) {
delete(r.clients, key)
}
}
}

Walkthrough

The list below documents the statements inside the function body, including nested blocks and inline closures.

  • L84: if r.clients == nil { return }
    • What: Branches conditionally.
    • Why: Short-circuits early when a precondition is not met or an error/edge case is detected.
    • How: Evaluates the condition and executes the matching branch.
    • Nested steps:
      • L85: return
        • What: Returns from the current function.
        • Why: Ends the current execution path and hands control back to the caller.
        • How: Executes a return statement (possibly returning values).
  • L88: threshold := now.Add(-2 * r.window)
    • What: Defines threshold.
    • Why: Keeps intermediate state available for later steps in the function.
    • How: Evaluates the right-hand side expressions and stores results in the left-hand variables.
  • L89: for key, client := range r.clients { if client.lastSeen.Before(threshold) { delete(r.clients, key) } }
    • What: Iterates over a collection.
    • Why: Processes multiple elements with the same logic.
    • How: Executes a for ... range loop.
    • Nested steps:
      • L90: if client.lastSeen.Before(threshold) { delete(r.clients, key) }
        • What: Branches conditionally.
        • Why: Handles different execution paths based on runtime state.
        • How: Evaluates the condition and executes the matching branch.
        • Nested steps:
          • L91: delete(r.clients, key)
            • What: Calls delete.
            • Why: Performs side effects or delegates work to a helper.
            • How: Executes the expression statement.

Start Here

Architecture

Guides

Reference

Neighboring source