You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
199 lines
5.4 KiB
199 lines
5.4 KiB
package cache
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"math/rand"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func BenchmarkCache_ConcurrentUpsertAndGet(b *testing.B) {
|
|
ctx := context.Background()
|
|
expiredInterval := 10 * time.Minute // Произвольно выбранный интервал истечения срока действия
|
|
runGCInterval := 1 * time.Minute // Произвольно выбранный интервал запуска сборщика мусора
|
|
|
|
// Инициализация кэша
|
|
Init(ctx, expiredInterval, runGCInterval)
|
|
c := Cache()
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
// Количество операций для каждого типа (запись и чтение)
|
|
operationsCount := 100000
|
|
|
|
b.ResetTimer()
|
|
// Запуск горутин для записи
|
|
for i := 0; i < operationsCount; i++ {
|
|
wg.Add(1)
|
|
go func(i int) {
|
|
defer wg.Done()
|
|
key := fmt.Sprintf("key%d", i)
|
|
_, err := c.Upsert(key, func() (interface{}, error) {
|
|
return fmt.Sprintf("value%d", i), nil
|
|
}, 5*time.Minute) // Используем 5 минут как интервал обновления
|
|
if err != nil {
|
|
b.Errorf("Failed to upsert item: %s", err)
|
|
}
|
|
}(i)
|
|
}
|
|
|
|
// Запуск горутин для чтения
|
|
for i := 0; i < operationsCount; i++ {
|
|
wg.Add(1)
|
|
go func(i int) {
|
|
defer wg.Done()
|
|
key := fmt.Sprintf("key%d", i)
|
|
_, err := c.Get(key)
|
|
if err != nil && !errors.Is(err, ErrorKeyNotFound) {
|
|
b.Errorf("Failed to get item: %s", err)
|
|
}
|
|
}(i)
|
|
}
|
|
|
|
wg.Wait() // Ожидание завершения всех горутин
|
|
}
|
|
|
|
// BenchmarkCache_ConcurrentUpsert бенчмарк для многопоточной операции Upsert.
|
|
func BenchmarkCache_ConcurrentUpsert(b *testing.B) {
|
|
ctx := context.Background()
|
|
Init(ctx, 10*time.Minute, 1*time.Minute)
|
|
c := Cache()
|
|
|
|
b.ResetTimer()
|
|
var wg sync.WaitGroup
|
|
for i := 0; i < b.N; i++ {
|
|
wg.Add(1)
|
|
go func(i int) {
|
|
defer wg.Done()
|
|
_, err := c.Upsert(fmt.Sprintf("key%d", i), func() (interface{}, error) {
|
|
return fmt.Sprintf("value%d", i), nil
|
|
}, 5*time.Minute)
|
|
if err != nil {
|
|
b.Errorf("Failed to upsert item: %s", err)
|
|
}
|
|
}(i)
|
|
}
|
|
wg.Wait()
|
|
}
|
|
|
|
func BenchmarkCache_ConcurrentGet2(b *testing.B) {
|
|
ctx := context.Background()
|
|
Init(ctx, 10*time.Minute, 1*time.Minute)
|
|
c := Cache()
|
|
|
|
// Предварительно заполняем кэш данными для чтения
|
|
for i := 0; i < 100000; i++ {
|
|
key := fmt.Sprintf("key%d", i)
|
|
c.Upsert(key, func() (interface{}, error) {
|
|
return fmt.Sprintf("value%d", i), nil
|
|
}, 5*time.Minute)
|
|
}
|
|
|
|
b.ResetTimer()
|
|
b.RunParallel(func(pb *testing.PB) {
|
|
for pb.Next() {
|
|
_, err := c.Get(fmt.Sprintf("key%d", rand.Intn(10000)))
|
|
if err != nil {
|
|
b.Errorf("Failed to get item: %s", err)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func BenchmarkCache_ProductionLike(b *testing.B) {
|
|
ctx := context.Background()
|
|
expiredInterval := 1 * time.Minute // Более короткий интервал истечения для тестирования GC
|
|
runGCInterval := 10 * time.Second // Частый запуск GC для имитации работы
|
|
|
|
Init(ctx, expiredInterval, runGCInterval)
|
|
c := Cache()
|
|
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
var wg sync.WaitGroup
|
|
|
|
// Чтение данных
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
key := fmt.Sprintf("key%d", rand.Intn(1000))
|
|
_, _ = c.Get(key)
|
|
}()
|
|
|
|
// Запись данных
|
|
wg.Add(1)
|
|
go func(i int) {
|
|
defer wg.Done()
|
|
key := fmt.Sprintf("key%d", i%1000) // Ограничение количества уникальных ключей для имитации повторных записей
|
|
_, _ = c.Upsert(key, func() (interface{}, error) {
|
|
return fmt.Sprintf("value%d", i), nil
|
|
}, time.Duration(rand.Intn(5)+1)*time.Minute) // Рандомный интервал обновления
|
|
}(i)
|
|
|
|
// Обновление данных
|
|
wg.Add(1)
|
|
go func(i int) {
|
|
defer wg.Done()
|
|
key := fmt.Sprintf("key%d", rand.Intn(1000))
|
|
_, _ = c.Upsert(key, func() (interface{}, error) {
|
|
return fmt.Sprintf("newValue%d", i), nil
|
|
}, time.Duration(rand.Intn(5)+1)*time.Minute)
|
|
}(i)
|
|
|
|
// Удаление данных
|
|
if i%10 == 0 { // Не на каждой итерации, чтобы не очищать все сразу
|
|
wg.Add(1)
|
|
go func(i int) {
|
|
defer wg.Done()
|
|
key := fmt.Sprintf("key%d", rand.Intn(1000))
|
|
c.Delete(key)
|
|
}(i)
|
|
}
|
|
|
|
wg.Wait()
|
|
}
|
|
}
|
|
|
|
func TestUpdate(t *testing.T) {
|
|
ctx := context.Background()
|
|
expiredInterval := time.Second * 5 // Более короткий интервал истечения для тестирования GC
|
|
runGCInterval := 5 * time.Second // Частый запуск GC для имитации работы
|
|
|
|
key := "key"
|
|
val1 := "val1"
|
|
val2 := "val2"
|
|
|
|
get1 := func() (interface{}, error) { return val1, nil }
|
|
get2 := func() (interface{}, error) { return val2, nil }
|
|
|
|
Init(ctx, expiredInterval, runGCInterval)
|
|
c := Cache()
|
|
|
|
c.Upsert(key, get1, expiredInterval)
|
|
|
|
time.Sleep(expiredInterval * 3)
|
|
|
|
val, err := c.Get(key)
|
|
if err != ErrorKeyNotFound {
|
|
t.Error("Key is not deleted", val, err)
|
|
}
|
|
|
|
c.Upsert(key, get1, expiredInterval)
|
|
c.Upsert(key, get2, expiredInterval)
|
|
|
|
val, err = c.Get(key)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if val.(string) != val2 {
|
|
t.Errorf("new value is %s, expected %s", val, val2)
|
|
}
|
|
|
|
if val.(string) != val2 {
|
|
t.Error("value is not updated", val, "!=", val2)
|
|
}
|
|
}
|
|
|