fiber/middleware/idempotency/locker_test.go
René d5842860eb fix Benchmark_MemoryLock
panic: runtime error: index out of range [1] with length 1

goroutine 67 [running]:
github.com/gofiber/fiber/v3/middleware/idempotency_test.Benchmark_MemoryLock(0xc00012c2c8)
	/home/runner/work/fiber/fiber/middleware/idempotency/locker_test.go:74 +0x23d
testing.(*B).runN(0xc00012c2c8, 0x1)
	/opt/hostedtoolcache/go/1.24.4/x64/src/testing/benchmark.go:219 +0x190
testing.(*B).run1.func1()
	/opt/hostedtoolcache/go/1.24.4/x64/src/testing/benchmark.go:245 +0x48
created by testing.(*B).run1 in goroutine 1
	/opt/hostedtoolcache/go/1.24.4/x64/src/testing/benchmark.go:238 +0x90
exit status 2
2025-07-18 08:55:06 +02:00

130 lines
2.3 KiB
Go

package idempotency_test
import (
"strconv"
"sync/atomic"
"testing"
"time"
"github.com/gofiber/fiber/v3/middleware/idempotency"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// go test -run Test_MemoryLock
func Test_MemoryLock(t *testing.T) {
t.Parallel()
l := idempotency.NewMemoryLock()
{
err := l.Lock("a")
require.NoError(t, err)
}
{
done := make(chan struct{})
go func() {
defer close(done)
err := l.Lock("a")
assert.NoError(t, err)
}()
select {
case <-done:
t.Fatal("lock acquired again")
case <-time.After(time.Second):
}
}
{
err := l.Lock("b")
require.NoError(t, err)
}
{
err := l.Unlock("b")
require.NoError(t, err)
}
{
err := l.Lock("b")
require.NoError(t, err)
}
{
err := l.Unlock("c")
require.NoError(t, err)
}
{
err := l.Lock("d")
require.NoError(t, err)
}
}
func Benchmark_MemoryLock(b *testing.B) {
keys := make([]string, 50_000_000)
for i := range keys {
keys[i] = strconv.Itoa(i)
}
lock := idempotency.NewMemoryLock()
for i := 0; b.Loop(); i++ {
key := keys[i]
if err := lock.Lock(key); err != nil {
b.Fatal(err)
}
if err := lock.Unlock(key); err != nil {
b.Fatal(err)
}
}
}
func Benchmark_MemoryLock_Parallel(b *testing.B) {
// In order to prevent using repeated keys I pre-allocate keys
keys := make([]string, 1_000_000)
for i := range keys {
keys[i] = strconv.Itoa(i)
}
b.Run("UniqueKeys", func(b *testing.B) {
lock := idempotency.NewMemoryLock()
var keyI atomic.Int32
b.ReportAllocs()
b.ResetTimer()
b.RunParallel(func(p *testing.PB) {
for p.Next() {
i := int(keyI.Add(1)) % len(keys)
key := keys[i]
if err := lock.Lock(key); err != nil {
b.Fatal(err)
}
if err := lock.Unlock(key); err != nil {
b.Fatal(err)
}
}
})
})
b.Run("RepeatedKeys", func(b *testing.B) {
lock := idempotency.NewMemoryLock()
var keyI atomic.Int32
b.ReportAllocs()
b.ResetTimer()
b.RunParallel(func(p *testing.PB) {
for p.Next() {
// Division by 3 ensures that index will be repreated exactly 3 times
i := int(keyI.Add(1)) / 3 % len(keys)
key := keys[i]
if err := lock.Lock(key); err != nil {
b.Fatal(err)
}
if err := lock.Unlock(key); err != nil {
b.Fatal(err)
}
}
})
})
}