@@ -6,8 +6,10 @@ import (
6
6
"testing"
7
7
"time"
8
8
9
+ "github.com/go-kit/log"
9
10
"github.com/prometheus/client_golang/prometheus"
10
11
"github.com/stretchr/testify/require"
12
+ "github.com/thanos-io/thanos/pkg/cache"
11
13
)
12
14
13
15
func Test_MultiLevelChunkCacheStore (t * testing.T ) {
@@ -72,6 +74,43 @@ func Test_MultiLevelChunkCacheStore(t *testing.T) {
72
74
}
73
75
}
74
76
77
+ func Test_MultiLevelChunkCacheFetchRace (t * testing.T ) {
78
+ cfg := MultiLevelChunkCacheConfig {
79
+ MaxAsyncConcurrency : 10 ,
80
+ MaxAsyncBufferSize : 100000 ,
81
+ MaxBackfillItems : 10000 ,
82
+ BackFillTTL : time .Hour * 24 ,
83
+ }
84
+ reg := prometheus .NewRegistry ()
85
+
86
+ m1 := newMockChunkCache ("m1" , map [string ][]byte {
87
+ "key1" : []byte ("value1" ),
88
+ "key2" : []byte ("value2" ),
89
+ })
90
+
91
+ inMemory , err := cache .NewInMemoryCacheWithConfig ("test" , log .NewNopLogger (), reg , cache.InMemoryCacheConfig {MaxSize : 10 * 1024 , MaxItemSize : 1024 })
92
+ require .NoError (t , err )
93
+
94
+ inMemory .Store (map [string ][]byte {
95
+ "key2" : []byte ("value2" ),
96
+ "key3" : []byte ("value3" ),
97
+ }, time .Minute )
98
+
99
+ c := newMultiLevelChunkCache ("chunk-cache" , cfg , reg , inMemory , m1 )
100
+
101
+ hits := c .Fetch (context .Background (), []string {"key1" , "key2" , "key3" , "key4" })
102
+
103
+ require .Equal (t , 3 , len (hits ))
104
+
105
+ // We should be able to change the returned values without any race problem
106
+ delete (hits , "key1" )
107
+
108
+ mlc := c .(* multiLevelChunkCache )
109
+ //Wait until async operation finishes.
110
+ mlc .backfillProcessor .Stop ()
111
+
112
+ }
113
+
75
114
func Test_MultiLevelChunkCacheFetch (t * testing.T ) {
76
115
cfg := MultiLevelChunkCacheConfig {
77
116
MaxAsyncConcurrency : 10 ,
@@ -81,12 +120,14 @@ func Test_MultiLevelChunkCacheFetch(t *testing.T) {
81
120
}
82
121
83
122
testCases := map [string ]struct {
84
- m1ExistingData map [string ][]byte
85
- m2ExistingData map [string ][]byte
86
- expectedM1Data map [string ][]byte
87
- expectedM2Data map [string ][]byte
88
- expectedFetchedData map [string ][]byte
89
- fetchKeys []string
123
+ m1ExistingData map [string ][]byte
124
+ m2ExistingData map [string ][]byte
125
+ expectedM1Data map [string ][]byte
126
+ expectedM2Data map [string ][]byte
127
+ expectedFetchedData map [string ][]byte
128
+ expectedM1FetchedKeys []string
129
+ expectedM2FetchedKeys []string
130
+ fetchKeys []string
90
131
}{
91
132
"fetched data should be union of m1, m2 and 'key2' and `key3' should be backfilled to m1" : {
92
133
m1ExistingData : map [string ][]byte {
@@ -96,6 +137,8 @@ func Test_MultiLevelChunkCacheFetch(t *testing.T) {
96
137
"key2" : []byte ("value2" ),
97
138
"key3" : []byte ("value3" ),
98
139
},
140
+ expectedM1FetchedKeys : []string {"key1" , "key2" , "key3" },
141
+ expectedM2FetchedKeys : []string {"key2" , "key3" },
99
142
expectedM1Data : map [string ][]byte {
100
143
"key1" : []byte ("value1" ),
101
144
"key2" : []byte ("value2" ),
@@ -119,6 +162,8 @@ func Test_MultiLevelChunkCacheFetch(t *testing.T) {
119
162
m2ExistingData : map [string ][]byte {
120
163
"key2" : []byte ("value2" ),
121
164
},
165
+ expectedM1FetchedKeys : []string {"key1" , "key2" , "key3" },
166
+ expectedM2FetchedKeys : []string {"key2" , "key3" },
122
167
expectedM1Data : map [string ][]byte {
123
168
"key1" : []byte ("value1" ),
124
169
"key2" : []byte ("value2" ),
@@ -157,6 +202,8 @@ type mockChunkCache struct {
157
202
mu sync.Mutex
158
203
name string
159
204
data map [string ][]byte
205
+
206
+ fetchedKeys []string
160
207
}
161
208
162
209
func newMockChunkCache (name string , data map [string ][]byte ) * mockChunkCache {
@@ -180,6 +227,7 @@ func (m *mockChunkCache) Fetch(_ context.Context, keys []string) map[string][]by
180
227
h := map [string ][]byte {}
181
228
182
229
for _ , k := range keys {
230
+ m .fetchedKeys = append (m .fetchedKeys , k )
183
231
if _ , ok := m .data [k ]; ok {
184
232
h [k ] = m .data [k ]
185
233
}
0 commit comments