github.com/thanos-io/thanos@v0.32.5/internal/cortex/chunk/cache/memcached_test.go (about)

     1  // Copyright (c) The Cortex Authors.
     2  // Licensed under the Apache License 2.0.
     3  
     4  package cache_test
     5  
     6  import (
     7  	"context"
     8  	"errors"
     9  	"fmt"
    10  	"testing"
    11  
    12  	"github.com/bradfitz/gomemcache/memcache"
    13  	"github.com/go-kit/log"
    14  	"github.com/stretchr/testify/require"
    15  	"go.uber.org/atomic"
    16  
    17  	"github.com/thanos-io/thanos/internal/cortex/chunk/cache"
    18  )
    19  
    20  func TestMemcached(t *testing.T) {
    21  	t.Run("unbatched", func(t *testing.T) {
    22  		client := newMockMemcache()
    23  		memcache := cache.NewMemcached(cache.MemcachedConfig{}, client,
    24  			"test", nil, log.NewNopLogger())
    25  
    26  		testMemcache(t, memcache)
    27  	})
    28  
    29  	t.Run("batched", func(t *testing.T) {
    30  		client := newMockMemcache()
    31  		memcache := cache.NewMemcached(cache.MemcachedConfig{
    32  			BatchSize:   10,
    33  			Parallelism: 5,
    34  		}, client, "test", nil, log.NewNopLogger())
    35  
    36  		testMemcache(t, memcache)
    37  	})
    38  }
    39  
    40  func testMemcache(t *testing.T, memcache *cache.Memcached) {
    41  	numKeys := 1000
    42  
    43  	ctx := context.Background()
    44  	keysIncMissing := make([]string, 0, numKeys)
    45  	keys := make([]string, 0, numKeys)
    46  	bufs := make([][]byte, 0, numKeys)
    47  
    48  	// Insert 1000 keys skipping all multiples of 5.
    49  	for i := 0; i < numKeys; i++ {
    50  		keysIncMissing = append(keysIncMissing, fmt.Sprint(i))
    51  		if i%5 == 0 {
    52  			continue
    53  		}
    54  
    55  		keys = append(keys, fmt.Sprint(i))
    56  		bufs = append(bufs, []byte(fmt.Sprint(i)))
    57  	}
    58  	memcache.Store(ctx, keys, bufs)
    59  
    60  	found, bufs, missing := memcache.Fetch(ctx, keysIncMissing)
    61  	for i := 0; i < numKeys; i++ {
    62  		if i%5 == 0 {
    63  			require.Equal(t, fmt.Sprint(i), missing[0])
    64  			missing = missing[1:]
    65  			continue
    66  		}
    67  
    68  		require.Equal(t, fmt.Sprint(i), found[0])
    69  		require.Equal(t, fmt.Sprint(i), string(bufs[0]))
    70  		found = found[1:]
    71  		bufs = bufs[1:]
    72  	}
    73  }
    74  
    75  // mockMemcache whose calls fail 1/3rd of the time.
    76  type mockMemcacheFailing struct {
    77  	*mockMemcache
    78  	calls atomic.Uint64
    79  }
    80  
    81  func newMockMemcacheFailing() *mockMemcacheFailing {
    82  	return &mockMemcacheFailing{
    83  		mockMemcache: newMockMemcache(),
    84  	}
    85  }
    86  
    87  func (c *mockMemcacheFailing) GetMulti(keys []string) (map[string]*memcache.Item, error) {
    88  	calls := c.calls.Inc()
    89  	if calls%3 == 0 {
    90  		return nil, errors.New("fail")
    91  	}
    92  
    93  	return c.mockMemcache.GetMulti(keys)
    94  }
    95  
    96  func TestMemcacheFailure(t *testing.T) {
    97  	t.Run("unbatched", func(t *testing.T) {
    98  		client := newMockMemcacheFailing()
    99  		memcache := cache.NewMemcached(cache.MemcachedConfig{}, client,
   100  			"test", nil, log.NewNopLogger())
   101  
   102  		testMemcacheFailing(t, memcache)
   103  	})
   104  
   105  	t.Run("batched", func(t *testing.T) {
   106  		client := newMockMemcacheFailing()
   107  		memcache := cache.NewMemcached(cache.MemcachedConfig{
   108  			BatchSize:   10,
   109  			Parallelism: 5,
   110  		}, client, "test", nil, log.NewNopLogger())
   111  
   112  		testMemcacheFailing(t, memcache)
   113  	})
   114  }
   115  
   116  func testMemcacheFailing(t *testing.T, memcache *cache.Memcached) {
   117  	numKeys := 1000
   118  
   119  	ctx := context.Background()
   120  	keysIncMissing := make([]string, 0, numKeys)
   121  	keys := make([]string, 0, numKeys)
   122  	bufs := make([][]byte, 0, numKeys)
   123  	// Insert 1000 keys skipping all multiples of 5.
   124  	for i := 0; i < numKeys; i++ {
   125  		keysIncMissing = append(keysIncMissing, fmt.Sprint(i))
   126  		if i%5 == 0 {
   127  			continue
   128  		}
   129  		keys = append(keys, fmt.Sprint(i))
   130  		bufs = append(bufs, []byte(fmt.Sprint(i)))
   131  	}
   132  	memcache.Store(ctx, keys, bufs)
   133  
   134  	for i := 0; i < 10; i++ {
   135  		found, bufs, missing := memcache.Fetch(ctx, keysIncMissing)
   136  
   137  		require.Equal(t, len(found), len(bufs))
   138  		for i := range found {
   139  			require.Equal(t, found[i], string(bufs[i]))
   140  		}
   141  
   142  		keysReturned := make(map[string]struct{})
   143  		for _, key := range found {
   144  			_, ok := keysReturned[key]
   145  			require.False(t, ok, "duplicate key returned")
   146  
   147  			keysReturned[key] = struct{}{}
   148  		}
   149  		for _, key := range missing {
   150  			_, ok := keysReturned[key]
   151  			require.False(t, ok, "duplicate key returned")
   152  
   153  			keysReturned[key] = struct{}{}
   154  		}
   155  
   156  		for _, key := range keys {
   157  			_, ok := keysReturned[key]
   158  			require.True(t, ok, "key missing %s", key)
   159  		}
   160  	}
   161  }
   162  
   163  func TestMemcacheStop(t *testing.T) {
   164  	t.Run("unbatched", func(t *testing.T) {
   165  		client := newMockMemcacheFailing()
   166  		memcache := cache.NewMemcached(cache.MemcachedConfig{}, client,
   167  			"test", nil, log.NewNopLogger())
   168  
   169  		testMemcachedStopping(t, memcache)
   170  	})
   171  
   172  	t.Run("batched", func(t *testing.T) {
   173  		client := newMockMemcacheFailing()
   174  		memcache := cache.NewMemcached(cache.MemcachedConfig{
   175  			BatchSize:   10,
   176  			Parallelism: 5,
   177  		}, client, "test", nil, log.NewNopLogger())
   178  
   179  		testMemcachedStopping(t, memcache)
   180  	})
   181  }
   182  
   183  func testMemcachedStopping(t *testing.T, memcache *cache.Memcached) {
   184  	numKeys := 1000
   185  	ctx := context.Background()
   186  	keys := make([]string, 0, numKeys)
   187  	bufs := make([][]byte, 0, numKeys)
   188  	for i := 0; i < numKeys; i++ {
   189  		keys = append(keys, fmt.Sprint(i))
   190  		bufs = append(bufs, []byte(fmt.Sprint(i)))
   191  	}
   192  
   193  	memcache.Store(ctx, keys, bufs)
   194  
   195  	go memcache.Fetch(ctx, keys)
   196  	memcache.Stop()
   197  }