How to Implement Request Debouncing Middleware in Gorig: A Complete Guide

Gorig provides a built-in Debounce middleware in httpx/mid.debounce.go that uses a sharded request map with automatic cleanup to throttle duplicate HTTP requests within a configurable time window, returning HTTP 429 when limits are exceeded.

The jom-io/gorig framework includes a production-ready request debouncing middleware designed for high-concurrency Gin applications. When you implement request debouncing middleware in Gorig, you leverage a sharded map architecture that minimizes lock contention while automatically managing memory through background cleanup routines.

Understanding Gorig's Debounce Middleware Architecture

Sharded Request Map for High Concurrency

At the core of the implementation is the ShardedRequestMap structure defined in httpx/mid.debounce.go. This component splits the request tracking storage into 32 independent shards, each protected by its own RWMutex. When a request arrives, Gorig hashes the request key using FNV-1a to determine which shard owns the timestamp, dramatically reducing lock granularity under heavy load compared to a single global map.

Automatic Memory Cleanup

To prevent unbounded memory growth, the middleware spawns a background goroutine that executes every minute. As implemented in httpx/mid.debounce.go, this routine scans each shard and clears the map entirely if the shard's estimated memory consumption exceeds 2 MiB. This ensures that stale request keys from high-traffic endpoints do not accumulate indefinitely.

Request Key Generation

The middleware constructs a unique fingerprint for each request using the following logic from httpx/mid.debounce.go:

  • Path and Query: path + ? + RawQuery for GET requests
  • User Identification: If a JWT token is present, append :id: + userID; otherwise append :ip: + ClientIP

This dual strategy ensures authenticated users are throttled independently from anonymous traffic while preventing IP-based collision attacks.

How to Implement Request Debouncing Middleware in Gorig

Basic Implementation with Default Settings

Gorig automatically applies debouncing to all routes when you bootstrap the server. In httpx/serv.go, the default Gin engine is configured with a 200 millisecond debounce window:

gEngine.Use(Debounce(200 * time.Millisecond))

This single line at line 82 of httpx/serv.go protects your entire API from duplicate submissions without additional configuration.

Custom Debounce Durations for Specific Routes

For endpoints requiring different throttling behavior, apply the middleware to specific route groups. This overrides the global default for that subtree:

router := gEngine.Group("/api")
router.Use(Debounce(500 * time.Millisecond))
router.GET("/items", itemsHandler)

This configuration extends the debounce window to 500ms for all /api/* endpoints, accommodating slower client-side polling patterns while maintaining protection.

Disabling Debounce for Testing

When running integration tests that issue rapid sequential requests, disable the middleware globally to avoid HTTP 429 errors. Call DebounceDisable() before initializing test servers:

func TestFastCalls(t *testing.T) {
    httpx.DebounceDisable()
    // ... initialize server and run test code
}

This function sets the internal enable flag to false, causing the Debounce middleware to skip all checks while maintaining handler chain compatibility.

Whitelisting Specific Endpoints

For health checks, metrics scraping, or other high-frequency legitimate traffic, exclude specific paths from debouncing using DebouceAw() (note the spelling as implemented in the source):

func init() {
    httpx.DebouceAw("/health", "/metrics")
}

This registers the specified paths in a global whitelist map. When the middleware processes a request, it checks this map before generating the request key, allowing unlimited requests to these endpoints regardless of the debounce window.

Advanced: Manual Request Key Construction

For custom rate-limiting logic outside the standard middleware, Gorig exposes the key generation algorithm. Replicate the fingerprinting logic to interact with the sharded map directly:

func myHandler(c *gin.Context) {
    token := httpx.GetTokenByCtx(c, false)
    userID := httpx.GetUserIDByToken(token)

    key := c.Request.URL.Path
    if c.Request.Method == "GET" {
        key += "?" + c.Request.URL.RawQuery
    }
    if userID != "" {
        key += ":id:" + userID
    } else {
        key += ":ip:" + c.ClientIP()
    }
    
    // Access the sharded map for custom logic
    shard := httpx.NewShardedRequestMap()
    // ... custom implementation
}

This pattern allows you to extend the debounce concept to WebSocket connections, gRPC streams, or other non-HTTP contexts while maintaining consistency with Gorig's core architecture.

Summary

  • Gorig's debounce middleware is implemented in httpx/mid.debounce.go and automatically wired into the default Gin engine in httpx/serv.go with a 200ms window.
  • High-concurrency design uses a 32-sharded map with FNV-1a hashing and per-shard RWMutexes to minimize lock contention.
  • Memory safety is ensured by a background cleanup routine that clears shards exceeding 2 MiB every minute.
  • Flexible configuration allows custom durations per route group, global disabling via DebounceDisable(), and path whitelisting via DebouceAw().
  • Request fingerprinting combines path, query, and user ID (or IP for anonymous requests) to create unique throttle keys.

Frequently Asked Questions

What is the default debounce duration in Gorig?

The default debounce window is 200 milliseconds, configured automatically in httpx/serv.go at line 82 where the middleware is applied to the global Gin engine with Debounce(200 * time.Millisecond).

How does Gorig prevent memory leaks in the debounce middleware?

Gorig implements a background cleanup goroutine that executes every minute, scanning all 32 shards of the ShardedRequestMap. If a shard's estimated memory consumption exceeds 2 MiB, the routine clears that shard's map entirely, preventing unbounded growth from stale request timestamps.

Can I use different debounce intervals for different user groups?

While the middleware does not natively support per-user-group intervals, you can achieve this by creating separate route groups and applying different Debounce(duration) middleware instances to each group. For example, apply a 500ms window to /api/premium routes and a 200ms window to /api/free routes.

What HTTP status code is returned when a request is debounced?

When a duplicate request is detected within the debounce window, Gorig returns HTTP 429 Too Many Requests using the response.ErrorTooManyRequests function from apix/response/response.go.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →