go 36 lines · 6 steps

A percentage feature-rollout middleware in Gin

A Gin middleware gates a feature to a stable percentage of clients by hashing feature name plus IP into buckets.

Explained by highlit
1package middleware
2 
3import (
4 "hash/fnv"
5 "net/http"
6 
7 "github.com/gin-gonic/gin"
8)
9 
10func PercentageRollout(feature string, percent int) gin.HandlerFunc {
11 return func(c *gin.Context) {
12 if percent >= 100 {
13 c.Set("feature."+feature, true)
14 c.Next()
15 return
16 }
17 
18 if percent <= 0 || !inBucket(feature, c.ClientIP(), percent) {
19 c.AbortWithStatusJSON(http.StatusNotFound, gin.H{
20 "error": "feature not available",
21 })
22 return
23 }
24 
25 c.Set("feature."+feature, true)
26 c.Next()
27 }
28}
29 
30func inBucket(feature, ip string, percent int) bool {
31 h := fnv.New32a()
32 h.Write([]byte(feature))
33 h.Write([]byte{':'})
34 h.Write([]byte(ip))
35 return int(h.Sum32()%100) < percent
36}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Hashing a stable key like feature plus IP gives each client a consistent bucket, so rollout decisions don't flicker between requests.
  2. 2Handling the 100 and 0 percent edges explicitly avoids paying the hashing cost and makes the full-on and full-off cases exact.
  3. 3Storing a flag on the request context lets downstream handlers read the rollout decision without recomputing it.

Related explainers

Share this explainer

Here's the card — post it anywhere.

A percentage feature-rollout middleware in Gin — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code