go
44 lines · 6 steps
A lock-free request counter in Go
How atomic types let many goroutines tally requests and errors without a mutex.
Explained by
highlit
1package metrics
2
3import (
4 "sync/atomic"
5 "time"
6)
7
8type RequestCounter struct {
9 total atomic.Uint64
10 errors atomic.Uint64
11 lastHit atomic.Int64
12}
13
14func (c *RequestCounter) Record(failed bool) {
15 c.total.Add(1)
16 if failed {
17 c.errors.Add(1)
18 }
19 c.lastHit.Store(time.Now().UnixNano())
20}
21
22func (c *RequestCounter) Snapshot() (total, errors uint64, last time.Time) {
23 total = c.total.Load()
24 errors = c.errors.Load()
25 if ns := c.lastHit.Load(); ns != 0 {
26 last = time.Unix(0, ns)
27 }
28 return
29}
30
31func (c *RequestCounter) Reset() (total, errors uint64) {
32 total = c.total.Swap(0)
33 errors = c.errors.Swap(0)
34 c.lastHit.Store(0)
35 return
36}
37
38func (c *RequestCounter) ErrorRate() float64 {
39 total := c.total.Load()
40 if total == 0 {
41 return 0
42 }
43 return float64(c.errors.Load()) / float64(total)
44}
01 / 01
STEP 01
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Atomic types give you safe concurrent counters without the overhead or ceremony of a mutex.
- 2Swap lets you read and reset a counter in one indivisible step, avoiding lost updates.
- 3Encoding a timestamp as an int64 of nanoseconds makes even time atomically storable.
Related explainers
go
package streaming import ( "bufio"
Streaming NDJSON logs over HTTP in Go
http-streaming
channels
select
Advanced
10 steps
go
package api import ( "crypto/sha256"
ETag conditional requests in Gin
http-caching
etag
conditional-requests
Intermediate
6 steps
go
func (w *Watcher) resetDebounce(d time.Duration) { if !w.timer.Stop() { select { case <-w.timer.C:
Debouncing a stream of events in Go
debounce
timers
channels
Advanced
7 steps
go
package logging import ( "context"
Deduplicating log attributes in Go's slog
decorator-pattern
structured-logging
immutability
Intermediate
8 steps
rust
use std::collections::VecDeque; use std::sync::{Arc, Condvar, Mutex}; use std::time::{Duration, Instant};
Building a counting semaphore in Rust
concurrency
synchronization
condition-variable
Advanced
9 steps
go
package middleware import ( "net/http"
Per-plan export limits in Gin middleware
middleware
rate-limiting
authorization
Intermediate
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/a-lock-free-request-counter-in-go-explained-go-1581/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.