132 lines
3.9 KiB
Go
132 lines
3.9 KiB
Go
package mincore_test
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
"unsafe"
|
|
|
|
"github.com/influxdata/influxdb/v2/pkg/mincore"
|
|
"golang.org/x/time/rate"
|
|
)
|
|
|
|
func TestLimiter(t *testing.T) {
|
|
pageSize := os.Getpagesize()
|
|
|
|
// Ensure limiter waits long enough between faults
|
|
t.Run("WaitPointer", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
data := make([]byte, pageSize*2)
|
|
l := mincore.NewLimiter(rate.NewLimiter(1, 1), data) // 1 fault per sec
|
|
l.Mincore = func(data []byte) ([]byte, error) { return make([]byte, 2), nil }
|
|
|
|
start := time.Now()
|
|
if err := l.WaitPointer(context.Background(), unsafe.Pointer(&data[0])); err != nil {
|
|
t.Fatal(err)
|
|
} else if err := l.WaitPointer(context.Background(), unsafe.Pointer(&data[pageSize])); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if d := time.Since(start); d < time.Second {
|
|
t.Fatalf("not enough time elapsed: %s", d)
|
|
}
|
|
})
|
|
|
|
// Ensure limiter waits long enough between faults for a byte slice.
|
|
t.Run("WaitRange", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
data := make([]byte, 2*pageSize)
|
|
l := mincore.NewLimiter(rate.NewLimiter(1, 1), data) // 1 fault per sec
|
|
l.Mincore = func(data []byte) ([]byte, error) { return make([]byte, 2), nil }
|
|
|
|
start := time.Now()
|
|
if err := l.WaitRange(context.Background(), data); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
if d := time.Since(start); d < time.Second {
|
|
t.Fatalf("not enough time elapsed: %s", d)
|
|
}
|
|
})
|
|
|
|
// Ensure pages are marked as in-core after calling Wait() on them.
|
|
t.Run("MoveToInMemoryAfterUse", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
data := make([]byte, pageSize*10)
|
|
l := mincore.NewLimiter(rate.NewLimiter(1, 1), data)
|
|
l.Mincore = func(data []byte) ([]byte, error) {
|
|
return make([]byte, 10), nil
|
|
}
|
|
if err := l.Update(); err != nil {
|
|
t.Fatal(err)
|
|
} else if l.IsInCore(uintptr(unsafe.Pointer(&data[0]))) {
|
|
t.Fatal("expected page to not be in-memory")
|
|
}
|
|
|
|
if err := l.WaitPointer(context.Background(), unsafe.Pointer(&data[0])); err != nil {
|
|
t.Fatal(err)
|
|
} else if !l.IsInCore(uintptr(unsafe.Pointer(&data[0]))) {
|
|
t.Fatal("expected page to be in-memory")
|
|
}
|
|
})
|
|
|
|
// Ensure fresh in-core data is pulled after the update interval.
|
|
t.Run("UpdateAfterInterval", func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
data := make([]byte, pageSize*10)
|
|
l := mincore.NewLimiter(rate.NewLimiter(1, 1), data)
|
|
l.UpdateInterval = 100 * time.Millisecond
|
|
|
|
var n int
|
|
l.Mincore = func(data []byte) ([]byte, error) {
|
|
n++
|
|
return make([]byte, 10), nil
|
|
}
|
|
|
|
// Wait for two pages to pull them in-memory.
|
|
if err := l.WaitPointer(context.Background(), unsafe.Pointer(&data[0])); err != nil {
|
|
t.Fatal(err)
|
|
} else if err := l.WaitPointer(context.Background(), unsafe.Pointer(&data[pageSize])); err != nil {
|
|
t.Fatal(err)
|
|
} else if !l.IsInCore(uintptr(unsafe.Pointer(&data[0]))) {
|
|
t.Fatal("expected page to be in-memory")
|
|
} else if !l.IsInCore(uintptr(unsafe.Pointer(&data[pageSize]))) {
|
|
t.Fatal("expected page to be in-memory")
|
|
}
|
|
|
|
// Wait for interval to pass.
|
|
time.Sleep(l.UpdateInterval)
|
|
|
|
// Fetch one of the previous pages and ensure the other one has been flushed from the update.
|
|
if err := l.WaitPointer(context.Background(), unsafe.Pointer(&data[0])); err != nil {
|
|
t.Fatal(err)
|
|
} else if !l.IsInCore(uintptr(unsafe.Pointer(&data[0]))) {
|
|
t.Fatal("expected page to be in-memory")
|
|
} else if l.IsInCore(uintptr(unsafe.Pointer(&data[pageSize]))) {
|
|
t.Fatal("expected page to not be in-memory")
|
|
}
|
|
|
|
if got, want := n, 2; got != want {
|
|
t.Fatalf("refreshed %d times, expected %d times", got, want)
|
|
}
|
|
})
|
|
|
|
// Ensure referencing data outside the limiter's data shows as in-memory.
|
|
t.Run("OutOfBounds", func(t *testing.T) {
|
|
l := mincore.NewLimiter(rate.NewLimiter(1, 1), make([]byte, pageSize))
|
|
l.Mincore = func(data []byte) ([]byte, error) {
|
|
return make([]byte, 1), nil
|
|
}
|
|
|
|
data := make([]byte, pageSize)
|
|
if !l.IsInCore(uintptr(unsafe.Pointer(&data[0]))) {
|
|
t.Fatal("expected out-of-bounds page to be resident")
|
|
}
|
|
})
|
|
}
|