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

Walkthrough

Space play step click any line
Three takeaways
  1. 1Atomic types give you safe concurrent counters without the overhead or ceremony of a mutex.
  2. 2Swap lets you read and reset a counter in one indivisible step, avoiding lost updates.
  3. 3Encoding a timestamp as an int64 of nanoseconds makes even time atomically storable.

Related explainers

Share this explainer

Here's the card — post it anywhere.

A lock-free request counter in Go — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code