influxdb/tsdb/epoch_tracker_test.go

142 lines
3.1 KiB
Go

package tsdb
import (
"testing"
"time"
)
func TestEpochTracker(t *testing.T) {
t.Run("Delete waits", func(t *testing.T) {
tr := newEpochTracker()
// delete should proceed with no pending writes
waiter := tr.WaitDelete(newGuard(0, 0, nil, nil))
waiter.Wait()
waiter.Done()
for i := 0; i < 1000; i++ {
// start up some writes
_, w1 := tr.StartWrite()
_, w2 := tr.StartWrite()
_, w3 := tr.StartWrite()
// wait for a delete. this time based stuff isn't sufficient
// to check every problem, but it can catch some.
waiter := tr.WaitDelete(nil)
done := make(chan time.Time, 1)
go func() { waiter.Wait(); done <- time.Now() }()
// future writes should not block the waiter
_, w4 := tr.StartWrite()
// ending the writes allows the waiter to proceed
tr.EndWrite(w1)
tr.EndWrite(w2)
now := time.Now()
tr.EndWrite(w3)
if (<-done).Before(now) {
t.Fatal("Wait ended too soon")
}
tr.EndWrite(w4)
}
})
t.Run("Guards tracked", func(t *testing.T) {
checkGuards := func(got []*guard, exp ...*guard) {
t.Helper()
if len(exp) != len(got) {
t.Fatalf("invalid: %p != %p", exp, got)
}
next:
for _, g1 := range got {
for _, g2 := range exp {
if g1 == g2 {
continue next
}
}
t.Fatalf("invalid: %p != %p", exp, got)
}
}
tr := newEpochTracker()
g1, g2, g3 := newGuard(0, 0, nil, nil), newGuard(0, 0, nil, nil), newGuard(0, 0, nil, nil)
guards, _ := tr.StartWrite()
checkGuards(guards)
d1 := tr.WaitDelete(g1)
guards, _ = tr.StartWrite()
checkGuards(guards, g1)
d2 := tr.WaitDelete(g2)
guards, _ = tr.StartWrite()
checkGuards(guards, g1, g2)
d3 := tr.WaitDelete(g3)
guards, _ = tr.StartWrite()
checkGuards(guards, g1, g2, g3)
d2.Done()
guards, _ = tr.StartWrite()
checkGuards(guards, g1, g3)
d1.Done()
guards, _ = tr.StartWrite()
checkGuards(guards, g3)
d3.Done()
guards, _ = tr.StartWrite()
checkGuards(guards)
})
}
func BenchmarkEpochTracker(b *testing.B) {
b.Run("Writes with deletes", func(b *testing.B) {
b.Run("Serial", func(b *testing.B) {
run := func(b *testing.B, deletes int) {
tr := newEpochTracker()
tr.StartWrite()
for i := 0; i < deletes; i++ {
tr.WaitDelete(nil)
}
b.ReportAllocs()
b.ResetTimer()
for i := 0; i < b.N; i++ {
_, gen := tr.StartWrite()
tr.EndWrite(gen)
}
}
b.Run("0", func(b *testing.B) { run(b, 0) })
b.Run("1", func(b *testing.B) { run(b, 1) })
b.Run("10", func(b *testing.B) { run(b, 10) })
b.Run("100", func(b *testing.B) { run(b, 100) })
})
b.Run("Parallel", func(b *testing.B) {
run := func(b *testing.B, deletes int) {
tr := newEpochTracker()
tr.StartWrite()
for i := 0; i < deletes; i++ {
tr.WaitDelete(nil)
}
b.ReportAllocs()
b.ResetTimer()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_, gen := tr.StartWrite()
tr.EndWrite(gen)
}
})
}
b.Run("0", func(b *testing.B) { run(b, 0) })
b.Run("1", func(b *testing.B) { run(b, 1) })
b.Run("10", func(b *testing.B) { run(b, 10) })
b.Run("100", func(b *testing.B) { run(b, 100) })
})
})
}