Initial commit: Go SRE interview exercises
Includes solved 02-worker-pool and 09-safe-counter (mutex, atomic, sharded). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GbnJj9ZqEx9nUvzkjdwfMq
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
const (
|
||||
goroutines = 16
|
||||
perG = 1000
|
||||
)
|
||||
|
||||
func TestMutexCounter(t *testing.T) {
|
||||
c := NewMutexCounter()
|
||||
var wg sync.WaitGroup
|
||||
for g := 0; g < goroutines; g++ {
|
||||
wg.Add(1)
|
||||
go func(g int) {
|
||||
defer wg.Done()
|
||||
for i := 0; i < perG; i++ {
|
||||
c.Inc(fmt.Sprintf("k%d", i%4))
|
||||
}
|
||||
}(g)
|
||||
}
|
||||
wg.Wait()
|
||||
var total int64
|
||||
for i := 0; i < 4; i++ {
|
||||
total += c.Get(fmt.Sprintf("k%d", i))
|
||||
}
|
||||
if want := int64(goroutines * perG); total != want {
|
||||
t.Fatalf("lost updates: want %d got %d", want, total)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSnapshotIsACopy(t *testing.T) {
|
||||
c := NewMutexCounter()
|
||||
c.Inc("a")
|
||||
snap := c.Snapshot()
|
||||
if snap == nil || snap["a"] != 1 {
|
||||
t.Fatalf("bad snapshot: %v", snap)
|
||||
}
|
||||
snap["a"] = 999 // mutating the snapshot must not affect the counter
|
||||
if c.Get("a") != 1 {
|
||||
t.Fatal("Snapshot returned the internal map, not a copy")
|
||||
}
|
||||
// And writing to the snapshot while counting must not race (-race checks).
|
||||
var wg sync.WaitGroup
|
||||
wg.Add(1)
|
||||
go func() { defer wg.Done(); for i := 0; i < 100; i++ { c.Inc("a") } }()
|
||||
for i := 0; i < 100; i++ {
|
||||
snap["b"] = int64(i)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
func TestAtomicCounter(t *testing.T) {
|
||||
c := NewAtomicCounter()
|
||||
var wg sync.WaitGroup
|
||||
for g := 0; g < goroutines; g++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for i := 0; i < perG; i++ {
|
||||
c.Inc()
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
if want := int64(goroutines * perG); c.Get() != want {
|
||||
t.Fatalf("lost updates: want %d got %d", want, c.Get())
|
||||
}
|
||||
}
|
||||
|
||||
func TestShardedCounter(t *testing.T) {
|
||||
c := NewShardedCounter()
|
||||
var wg sync.WaitGroup
|
||||
for g := 0; g < goroutines; g++ {
|
||||
wg.Add(1)
|
||||
go func(g int) {
|
||||
defer wg.Done()
|
||||
for i := 0; i < perG; i++ {
|
||||
c.Inc(fmt.Sprintf("key-%d", i%50))
|
||||
}
|
||||
}(g)
|
||||
}
|
||||
wg.Wait()
|
||||
var total int64
|
||||
for k, v := range c.Snapshot() {
|
||||
_ = k
|
||||
total += v
|
||||
}
|
||||
if want := int64(goroutines * perG); total != want {
|
||||
t.Fatalf("lost updates across shards: want %d got %d", want, total)
|
||||
}
|
||||
if c.Get("key-0") == 0 {
|
||||
t.Fatal("Get by key broken")
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMutexCounter(b *testing.B) {
|
||||
c := NewMutexCounter()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
c.Inc("hot")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkShardedCounter(b *testing.B) {
|
||||
c := NewShardedCounter()
|
||||
keys := [8]string{"a", "b", "c", "d", "e", "f", "g", "h"}
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
i := 0
|
||||
for pb.Next() {
|
||||
c.Inc(keys[i&7])
|
||||
i++
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkAtomicCounter(b *testing.B) {
|
||||
c := NewAtomicCounter()
|
||||
b.RunParallel(func(pb *testing.PB) {
|
||||
for pb.Next() {
|
||||
c.Inc()
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user