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
java
public final class RetryExecutor { private final int maxAttempts; private final Duration initialDelay;
Exponential backoff retry in Java
retry
exponential-backoff
jitter
Intermediate
8 steps
go
package handlers import ( "fmt"
Handling multipart profile uploads in Gin
form binding
file upload
validation
Intermediate
7 steps
java
import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.locks.ReentrantReadWriteLock;
A thread-safe LRU cache in Java
lru-cache
concurrency
linkedhashmap
Intermediate
7 steps
go
package middleware import ( "time"
Building a request logger middleware in Gin
middleware
structured-logging
closures
Intermediate
6 steps
go
package fetch import ( "context"
Concurrent API fetches with errgroup in Go
concurrency
goroutines
error-handling
Intermediate
8 steps
java
import java.util.concurrent.atomic.AtomicInteger; public final class RequestCounter {
A thread-safe request counter with AtomicInteger
concurrency
atomics
lock-free
Intermediate
6 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.