github.com/sequix/cortex@v1.1.6/pkg/chunk/storage/caching_index_client_test.go (about)

     1  package storage
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/sequix/cortex/pkg/chunk"
     9  	"github.com/sequix/cortex/pkg/chunk/cache"
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  	"github.com/weaveworks/common/user"
    13  )
    14  
    15  var ctx = user.InjectOrgID(context.Background(), "1")
    16  
    17  type mockStore struct {
    18  	chunk.IndexClient
    19  	queries int
    20  	results ReadBatch
    21  }
    22  
    23  func (m *mockStore) QueryPages(ctx context.Context, queries []chunk.IndexQuery, callback func(chunk.IndexQuery, chunk.ReadBatch) (shouldContinue bool)) error {
    24  	for _, query := range queries {
    25  		m.queries++
    26  		callback(query, m.results)
    27  	}
    28  	return nil
    29  }
    30  
    31  func TestCachingStorageClientBasic(t *testing.T) {
    32  	store := &mockStore{
    33  		results: ReadBatch{
    34  			Entries: []Entry{{
    35  				Column: []byte("foo"),
    36  				Value:  []byte("bar"),
    37  			}},
    38  		},
    39  	}
    40  	limits, err := defaultLimits()
    41  	require.NoError(t, err)
    42  	cache := cache.NewFifoCache("test", cache.FifoCacheConfig{Size: 10, Validity: 10 * time.Second})
    43  	client := newCachingIndexClient(store, cache, 1*time.Second, limits)
    44  	queries := []chunk.IndexQuery{{
    45  		TableName: "table",
    46  		HashValue: "baz",
    47  	}}
    48  	err = client.QueryPages(ctx, queries, func(_ chunk.IndexQuery, _ chunk.ReadBatch) bool {
    49  		return true
    50  	})
    51  	require.NoError(t, err)
    52  	assert.EqualValues(t, 1, store.queries)
    53  
    54  	// If we do the query to the cache again, the underlying store shouldn't see it.
    55  	err = client.QueryPages(ctx, queries, func(_ chunk.IndexQuery, _ chunk.ReadBatch) bool {
    56  		return true
    57  	})
    58  	require.NoError(t, err)
    59  	assert.EqualValues(t, 1, store.queries)
    60  }
    61  
    62  func TestTempCachingStorageClient(t *testing.T) {
    63  	store := &mockStore{
    64  		results: ReadBatch{
    65  			Entries: []Entry{{
    66  				Column: []byte("foo"),
    67  				Value:  []byte("bar"),
    68  			}},
    69  		},
    70  	}
    71  	limits, err := defaultLimits()
    72  	require.NoError(t, err)
    73  	cache := cache.NewFifoCache("test", cache.FifoCacheConfig{Size: 10, Validity: 10 * time.Second})
    74  	client := newCachingIndexClient(store, cache, 100*time.Millisecond, limits)
    75  	queries := []chunk.IndexQuery{
    76  		{TableName: "table", HashValue: "foo"},
    77  		{TableName: "table", HashValue: "bar"},
    78  		{TableName: "table", HashValue: "baz"},
    79  	}
    80  	results := 0
    81  	err = client.QueryPages(ctx, queries, func(query chunk.IndexQuery, batch chunk.ReadBatch) bool {
    82  		iter := batch.Iterator()
    83  		for iter.Next() {
    84  			results++
    85  		}
    86  		return true
    87  	})
    88  	require.NoError(t, err)
    89  	assert.EqualValues(t, len(queries), store.queries)
    90  	assert.EqualValues(t, len(queries), results)
    91  
    92  	// If we do the query to the cache again, the underlying store shouldn't see it.
    93  	results = 0
    94  	err = client.QueryPages(ctx, queries, func(query chunk.IndexQuery, batch chunk.ReadBatch) bool {
    95  		iter := batch.Iterator()
    96  		for iter.Next() {
    97  			results++
    98  		}
    99  		return true
   100  	})
   101  	require.NoError(t, err)
   102  	assert.EqualValues(t, len(queries), store.queries)
   103  	assert.EqualValues(t, len(queries), results)
   104  
   105  	// If we do the query after validity, it should see the queries.
   106  	time.Sleep(100 * time.Millisecond)
   107  	results = 0
   108  	err = client.QueryPages(ctx, queries, func(query chunk.IndexQuery, batch chunk.ReadBatch) bool {
   109  		iter := batch.Iterator()
   110  		for iter.Next() {
   111  			results++
   112  		}
   113  		return true
   114  	})
   115  	require.NoError(t, err)
   116  	assert.EqualValues(t, 2*len(queries), store.queries)
   117  	assert.EqualValues(t, len(queries), results)
   118  }
   119  
   120  func TestPermCachingStorageClient(t *testing.T) {
   121  	store := &mockStore{
   122  		results: ReadBatch{
   123  			Entries: []Entry{{
   124  				Column: []byte("foo"),
   125  				Value:  []byte("bar"),
   126  			}},
   127  		},
   128  	}
   129  	limits, err := defaultLimits()
   130  	require.NoError(t, err)
   131  	cache := cache.NewFifoCache("test", cache.FifoCacheConfig{Size: 10, Validity: 10 * time.Second})
   132  	client := newCachingIndexClient(store, cache, 100*time.Millisecond, limits)
   133  	queries := []chunk.IndexQuery{
   134  		{TableName: "table", HashValue: "foo", Immutable: true},
   135  		{TableName: "table", HashValue: "bar", Immutable: true},
   136  		{TableName: "table", HashValue: "baz", Immutable: true},
   137  	}
   138  	results := 0
   139  	err = client.QueryPages(ctx, queries, func(query chunk.IndexQuery, batch chunk.ReadBatch) bool {
   140  		iter := batch.Iterator()
   141  		for iter.Next() {
   142  			results++
   143  		}
   144  		return true
   145  	})
   146  	require.NoError(t, err)
   147  	assert.EqualValues(t, len(queries), store.queries)
   148  	assert.EqualValues(t, len(queries), results)
   149  
   150  	// If we do the query to the cache again, the underlying store shouldn't see it.
   151  	results = 0
   152  	err = client.QueryPages(ctx, queries, func(query chunk.IndexQuery, batch chunk.ReadBatch) bool {
   153  		iter := batch.Iterator()
   154  		for iter.Next() {
   155  			results++
   156  		}
   157  		return true
   158  	})
   159  	require.NoError(t, err)
   160  	assert.EqualValues(t, len(queries), store.queries)
   161  	assert.EqualValues(t, len(queries), results)
   162  
   163  	// If we do the query after validity, it still shouldn't see the queries.
   164  	time.Sleep(200 * time.Millisecond)
   165  	results = 0
   166  	err = client.QueryPages(ctx, queries, func(query chunk.IndexQuery, batch chunk.ReadBatch) bool {
   167  		iter := batch.Iterator()
   168  		for iter.Next() {
   169  			results++
   170  		}
   171  		return true
   172  	})
   173  	require.NoError(t, err)
   174  	assert.EqualValues(t, len(queries), store.queries)
   175  	assert.EqualValues(t, len(queries), results)
   176  }
   177  
   178  func TestCachingStorageClientEmptyResponse(t *testing.T) {
   179  	store := &mockStore{}
   180  	limits, err := defaultLimits()
   181  	require.NoError(t, err)
   182  	cache := cache.NewFifoCache("test", cache.FifoCacheConfig{Size: 10, Validity: 10 * time.Second})
   183  	client := newCachingIndexClient(store, cache, 1*time.Second, limits)
   184  	queries := []chunk.IndexQuery{{TableName: "table", HashValue: "foo"}}
   185  	err = client.QueryPages(ctx, queries, func(query chunk.IndexQuery, batch chunk.ReadBatch) bool {
   186  		assert.False(t, batch.Iterator().Next())
   187  		return true
   188  	})
   189  	require.NoError(t, err)
   190  	assert.EqualValues(t, 1, store.queries)
   191  
   192  	// If we do the query to the cache again, the underlying store shouldn't see it.
   193  	err = client.QueryPages(ctx, queries, func(query chunk.IndexQuery, batch chunk.ReadBatch) bool {
   194  		assert.False(t, batch.Iterator().Next())
   195  		return true
   196  	})
   197  	require.NoError(t, err)
   198  	assert.EqualValues(t, 1, store.queries)
   199  }
   200  
   201  func TestCachingStorageClientCollision(t *testing.T) {
   202  	// These two queries should result in one query to the cache & index, but
   203  	// two results, as we cache entire rows.
   204  	store := &mockStore{
   205  		results: ReadBatch{
   206  			Entries: []Entry{
   207  				{
   208  					Column: []byte("bar"),
   209  					Value:  []byte("bar"),
   210  				},
   211  				{
   212  					Column: []byte("baz"),
   213  					Value:  []byte("baz"),
   214  				},
   215  			},
   216  		},
   217  	}
   218  	limits, err := defaultLimits()
   219  	require.NoError(t, err)
   220  	cache := cache.NewFifoCache("test", cache.FifoCacheConfig{Size: 10, Validity: 10 * time.Second})
   221  	client := newCachingIndexClient(store, cache, 1*time.Second, limits)
   222  	queries := []chunk.IndexQuery{
   223  		{TableName: "table", HashValue: "foo", RangeValuePrefix: []byte("bar")},
   224  		{TableName: "table", HashValue: "foo", RangeValuePrefix: []byte("baz")},
   225  	}
   226  
   227  	var results ReadBatch
   228  	err = client.QueryPages(ctx, queries, func(query chunk.IndexQuery, batch chunk.ReadBatch) bool {
   229  		iter := batch.Iterator()
   230  		for iter.Next() {
   231  			results.Entries = append(results.Entries, Entry{
   232  				Column: iter.RangeValue(),
   233  				Value:  iter.Value(),
   234  			})
   235  		}
   236  		return true
   237  	})
   238  	require.NoError(t, err)
   239  	assert.EqualValues(t, 1, store.queries)
   240  	assert.EqualValues(t, store.results, results)
   241  
   242  	// If we do the query to the cache again, the underlying store shouldn't see it.
   243  	results = ReadBatch{}
   244  	err = client.QueryPages(ctx, queries, func(query chunk.IndexQuery, batch chunk.ReadBatch) bool {
   245  		iter := batch.Iterator()
   246  		for iter.Next() {
   247  			results.Entries = append(results.Entries, Entry{
   248  				Column: iter.RangeValue(),
   249  				Value:  iter.Value(),
   250  			})
   251  		}
   252  		return true
   253  	})
   254  	require.NoError(t, err)
   255  	assert.EqualValues(t, 1, store.queries)
   256  	assert.EqualValues(t, store.results, results)
   257  }