Drop-in waiting room middleware for gin web applications. Built on sema.
When your application is at capacity, you have three choices: drop the request (429), queue it blindly (no ordering guarantee), or admit it through a proper waiting room with FIFO ordering, position awareness, and a live status page.
room does the third. It sits in front of your gin handlers as middleware,
issues every arriving request a ticket and admits them in ticket order as
slots open, though clients that become eligible simultaneously may be served
in any order among themselves. Clients that must wait see a clean waiting
room page that updates their position automatically — no refresh required.
go get github.com/andreimerlescu/room
Requires Go 1.21+.
r := gin.Default()
wr := &room.WaitingRoom{}
if err := wr.Init(500); err != nil {
log.Fatal(err)
}
defer wr.Stop()
// Registers GET /queue/status and attaches the middleware.
wr.RegisterRoutes(r)
r.Run(":8080")That's it. The 501st concurrent request sees the waiting room. The 500th slot to free up admits them automatically.
wr := &room.WaitingRoom{}
if err := wr.Init(500); err != nil {
log.Fatal(err)
}
defer wr.Stop()
// Custom waiting room page.
html, _ := os.ReadFile("my_waiting_room.html")
wr.SetHTML(html)
// Tighten the reaper for a high-traffic event.
wr.SetReaperInterval(15 * time.Second)
// Registers GET /queue/status and attaches the middleware.
wr.RegisterRoutes(r)
r.Run(":8080")func onConfigReload(cfg Config) {
wr.SetCap(int32(cfg.MaxConcurrent))
wr.SetReaperInterval(cfg.ReaperInterval)
}| Layer | Responsibility |
|---|---|
| Ticket counter | Assigns each request a monotonically increasing position on arrival |
| FIFO gate | Blocks requests whose ticket is outside the serving window |
| sema | Manages how many requests are actively being served |
| Token store | Maps session cookies to tickets for /queue/status polling |
| Reaper | Evicts ghost tickets from clients that disconnected mid-queue |
// Simple path.
room.NewWaitingRoom(cap int32) gin.HandlerFunc
// Full control path.
wr.Init(cap int32) error
wr.Stop()
wr.Middleware() gin.HandlerFunc
wr.RegisterRoutes(r *gin.Engine)
wr.StatusHandler() gin.HandlerFunc
wr.SetHTML(html []byte)
wr.SetCap(cap int32) error
wr.SetReaperInterval(d time.Duration) error
wr.Cap() int32
wr.Len() int
wr.QueueDepth() int64
wr.Utilization() float64
wr.UtilizationSmoothed() float64
wr.ReaperInterval() time.DurationApache 2.0 © Andrei Merlescu
Built on sema. FIFO ordering, live position tracking, and a reaper that keeps ghost tickets from stalling your queue.