Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Nightly Race / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
137 lines
3.5 KiB
Go
137 lines
3.5 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
||
// See LICENSE.txt for license information.
|
||
|
||
package cache
|
||
|
||
import (
|
||
"fmt"
|
||
"hash/maphash"
|
||
"testing"
|
||
|
||
"github.com/cespare/xxhash/v2"
|
||
"github.com/stretchr/testify/assert"
|
||
"github.com/stretchr/testify/require"
|
||
|
||
"github.com/mattermost/mattermost/server/public/model"
|
||
)
|
||
|
||
func makeLRUPredictableTestData(num int) [][2]string {
|
||
kv := make([][2]string, num)
|
||
for i := range kv {
|
||
kv[i] = [2]string{
|
||
fmt.Sprintf("%d-key-%d", i, i),
|
||
fmt.Sprintf("%d-val-%d", i, i),
|
||
}
|
||
}
|
||
return kv
|
||
}
|
||
|
||
func TestNewLRUStriped(t *testing.T) {
|
||
scache, err := NewLRUStriped(&CacheOptions{StripedBuckets: 3, Size: 20})
|
||
require.NoError(t, err)
|
||
|
||
cache := scache.(LRUStriped)
|
||
|
||
require.Len(t, cache.buckets, 3)
|
||
assert.Equal(t, 8, cache.buckets[0].size)
|
||
assert.Equal(t, 8, cache.buckets[1].size)
|
||
assert.Equal(t, 8, cache.buckets[2].size)
|
||
}
|
||
|
||
func TestLRUStripedKeyDistribution(t *testing.T) {
|
||
dataset := makeLRUPredictableTestData(100)
|
||
|
||
scache, err := NewLRUStriped(&CacheOptions{
|
||
StripedBuckets: 4,
|
||
Size: len(dataset),
|
||
DefaultExpiry: 0,
|
||
})
|
||
require.NoError(t, err)
|
||
cache := scache.(LRUStriped)
|
||
for _, kv := range dataset {
|
||
require.NoError(t, cache.SetWithDefaultExpiry(kv[0], kv[1]))
|
||
var out string
|
||
require.NoError(t, cache.Get(kv[0], &out))
|
||
require.Equal(t, kv[1], out)
|
||
}
|
||
|
||
require.Len(t, cache.buckets, 4)
|
||
acc := 0
|
||
for i := range 4 {
|
||
clen, err := cache.buckets[i].Len()
|
||
acc += clen
|
||
assert.NoError(t, err)
|
||
assert.GreaterOrEqual(t, clen, len(dataset)/2/4, "at least 50%/nbuckets of all keys in each bucket")
|
||
}
|
||
// because of the limited size of each bucket and the nature of our data,
|
||
// we may have around 10% of our keys evicted in this scenario. removing 1% because we cannot predict
|
||
// accurately what is happening with random data.
|
||
assert.GreaterOrEqual(t, acc, len(dataset)-(len(dataset)*1.0/100.0))
|
||
}
|
||
|
||
func TestLRUStriped_Size(t *testing.T) {
|
||
scache, err := NewLRUStriped(&CacheOptions{StripedBuckets: 2, Size: 128})
|
||
require.NoError(t, err)
|
||
cache := scache.(LRUStriped)
|
||
acc := 0
|
||
for _, bucket := range cache.buckets {
|
||
acc += bucket.size
|
||
}
|
||
assert.Equal(t, 128+13+1, acc) // +10% +modulo padding
|
||
}
|
||
|
||
func TestLRUStriped_HashKey(t *testing.T) {
|
||
scache, err := NewLRUStriped(&CacheOptions{StripedBuckets: 2, Size: 128})
|
||
require.NoError(t, err)
|
||
cache := scache.(LRUStriped)
|
||
first := cache.hashkeyMapHash("key")
|
||
cache.hashkeyMapHash("other_key_to_ensure_that_result_it’s_not_dependent_on_previous_input")
|
||
second := cache.hashkeyMapHash("key")
|
||
require.Equal(t, first, second)
|
||
}
|
||
|
||
func TestLRUStriped_Get(t *testing.T) {
|
||
cache, err := NewLRUStriped(&CacheOptions{
|
||
StripedBuckets: 4,
|
||
Size: 128,
|
||
DefaultExpiry: 0,
|
||
})
|
||
require.NoError(t, err)
|
||
var out string
|
||
require.Equal(t, ErrKeyNotFound, cache.Get("key", &out))
|
||
require.Zero(t, out)
|
||
|
||
require.NoError(t, cache.SetWithDefaultExpiry("key", "value"))
|
||
require.NoError(t, cache.Get("key", &out))
|
||
require.Equal(t, "value", out)
|
||
}
|
||
|
||
var hashSink uint64
|
||
|
||
func BenchmarkSum64(b *testing.B) {
|
||
cases := []string{
|
||
"1",
|
||
"22",
|
||
"333",
|
||
model.NewId(),
|
||
model.NewId() + model.NewId(),
|
||
}
|
||
|
||
for _, case_ := range cases {
|
||
b.Run(fmt.Sprintf("maphash_string_len_%d", len(case_)), func(b *testing.B) {
|
||
seed := maphash.MakeSeed()
|
||
for b.Loop() {
|
||
var h maphash.Hash
|
||
h.SetSeed(seed)
|
||
h.WriteString(case_) // documentation and code says it never fails
|
||
hashSink = h.Sum64()
|
||
}
|
||
})
|
||
b.Run(fmt.Sprintf("xxhash_string_len_%d", len(case_)), func(b *testing.B) {
|
||
for b.Loop() {
|
||
hashSink = xxhash.Sum64String(case_)
|
||
}
|
||
})
|
||
}
|
||
}
|