1package fastcache 2 3import ( 4 "fmt" 5 "runtime" 6 "sync" 7 "testing" 8 "time" 9) 10 11func TestCacheSmall(t *testing.T) { 12 c := New(1) 13 defer c.Reset() 14 15 if v := c.Get(nil, []byte("aaa")); len(v) != 0 { 16 t.Fatalf("unexpected non-empty value obtained from small cache: %q", v) 17 } 18 if v, exist := c.HasGet(nil, []byte("aaa")); exist || len(v) != 0 { 19 t.Fatalf("unexpected non-empty value obtained from small cache: %q", v) 20 } 21 22 c.Set([]byte("key"), []byte("value")) 23 if v := c.Get(nil, []byte("key")); string(v) != "value" { 24 t.Fatalf("unexpected value obtained; got %q; want %q", v, "value") 25 } 26 if v := c.Get(nil, nil); len(v) != 0 { 27 t.Fatalf("unexpected non-empty value obtained from small cache: %q", v) 28 } 29 if v, exist := c.HasGet(nil, nil); exist { 30 t.Fatalf("unexpected nil-keyed value obtained in small cache: %q", v) 31 } 32 if v := c.Get(nil, []byte("aaa")); len(v) != 0 { 33 t.Fatalf("unexpected non-empty value obtained from small cache: %q", v) 34 } 35 36 c.Set([]byte("aaa"), []byte("bbb")) 37 if v := c.Get(nil, []byte("aaa")); string(v) != "bbb" { 38 t.Fatalf("unexpected value obtained; got %q; want %q", v, "bbb") 39 } 40 if v, exist := c.HasGet(nil, []byte("aaa")); !exist || string(v) != "bbb" { 41 t.Fatalf("unexpected value obtained; got %q; want %q", v, "bbb") 42 } 43 44 c.Reset() 45 if v := c.Get(nil, []byte("aaa")); len(v) != 0 { 46 t.Fatalf("unexpected non-empty value obtained from empty cache: %q", v) 47 } 48 if v, exist := c.HasGet(nil, []byte("aaa")); exist || len(v) != 0 { 49 t.Fatalf("unexpected non-empty value obtained from small cache: %q", v) 50 } 51 52 // Test empty value 53 k := []byte("empty") 54 c.Set(k, nil) 55 if v := c.Get(nil, k); len(v) != 0 { 56 t.Fatalf("unexpected non-empty value obtained from empty entry: %q", v) 57 } 58 if v, exist := c.HasGet(nil, k); !exist { 59 t.Fatalf("cannot find empty entry for key %q", k) 60 } else if len(v) != 0 { 61 t.Fatalf("unexpected non-empty value obtained from empty entry: %q", v) 62 } 63 if !c.Has(k) { 64 t.Fatalf("cannot find empty entry for key %q", k) 65 } 66 if c.Has([]byte("foobar")) { 67 t.Fatalf("non-existing entry found in the cache") 68 } 69} 70 71func TestCacheWrap(t *testing.T) { 72 c := New(bucketsCount * chunkSize * 1.5) 73 defer c.Reset() 74 75 calls := uint64(5e6) 76 77 for i := uint64(0); i < calls; i++ { 78 k := []byte(fmt.Sprintf("key %d", i)) 79 v := []byte(fmt.Sprintf("value %d", i)) 80 c.Set(k, v) 81 vv := c.Get(nil, k) 82 if string(vv) != string(v) { 83 t.Fatalf("unexpected value for key %q; got %q; want %q", k, vv, v) 84 } 85 } 86 for i := uint64(0); i < calls/10; i++ { 87 x := i * 10 88 k := []byte(fmt.Sprintf("key %d", x)) 89 v := []byte(fmt.Sprintf("value %d", x)) 90 vv := c.Get(nil, k) 91 if len(vv) > 0 && string(v) != string(vv) { 92 t.Fatalf("unexpected value for key %q; got %q; want %q", k, vv, v) 93 } 94 } 95 96 var s Stats 97 c.UpdateStats(&s) 98 getCalls := calls + calls/10 99 if s.GetCalls != getCalls { 100 t.Fatalf("unexpected number of getCalls; got %d; want %d", s.GetCalls, getCalls) 101 } 102 if s.SetCalls != calls { 103 t.Fatalf("unexpected number of setCalls; got %d; want %d", s.SetCalls, calls) 104 } 105 if s.Misses == 0 || s.Misses >= calls/10 { 106 t.Fatalf("unexpected number of misses; got %d; it should be between 0 and %d", s.Misses, calls/10) 107 } 108 if s.Collisions != 0 { 109 t.Fatalf("unexpected number of collisions; got %d; want 0", s.Collisions) 110 } 111 if s.EntriesCount < calls/5 { 112 t.Fatalf("unexpected number of items; got %d; cannot be smaller than %d", s.EntriesCount, calls/5) 113 } 114 if s.BytesSize < 1024 { 115 t.Fatalf("unexpected number of bytesSize; got %d; cannot be smaller than %d", s.BytesSize, 1024) 116 } 117} 118 119func TestCacheDel(t *testing.T) { 120 c := New(1024) 121 defer c.Reset() 122 for i := 0; i < 100; i++ { 123 k := []byte(fmt.Sprintf("key %d", i)) 124 v := []byte(fmt.Sprintf("value %d", i)) 125 c.Set(k, v) 126 vv := c.Get(nil, k) 127 if string(vv) != string(v) { 128 t.Fatalf("unexpected value for key %q; got %q; want %q", k, vv, v) 129 } 130 c.Del(k) 131 vv = c.Get(nil, k) 132 if len(vv) > 0 { 133 t.Fatalf("unexpected non-empty value got for key %q: %q", k, vv) 134 } 135 } 136} 137 138func TestCacheBigKeyValue(t *testing.T) { 139 c := New(1024) 140 defer c.Reset() 141 142 // Both key and value exceed 64Kb 143 k := make([]byte, 90*1024) 144 v := make([]byte, 100*1024) 145 c.Set(k, v) 146 vv := c.Get(nil, k) 147 if len(vv) > 0 { 148 t.Fatalf("unexpected non-empty value got for key %q: %q", k, vv) 149 } 150 151 // len(key) + len(value) > 64Kb 152 k = make([]byte, 40*1024) 153 v = make([]byte, 40*1024) 154 c.Set(k, v) 155 vv = c.Get(nil, k) 156 if len(vv) > 0 { 157 t.Fatalf("unexpected non-empty value got for key %q: %q", k, vv) 158 } 159} 160 161func TestCacheSetGetSerial(t *testing.T) { 162 itemsCount := 10000 163 c := New(30 * itemsCount) 164 defer c.Reset() 165 if err := testCacheGetSet(c, itemsCount); err != nil { 166 t.Fatalf("unexpected error: %s", err) 167 } 168} 169 170func TestCacheGetSetConcurrent(t *testing.T) { 171 itemsCount := 10000 172 const gorotines = 10 173 c := New(30 * itemsCount * gorotines) 174 defer c.Reset() 175 176 ch := make(chan error, gorotines) 177 for i := 0; i < gorotines; i++ { 178 go func() { 179 ch <- testCacheGetSet(c, itemsCount) 180 }() 181 } 182 for i := 0; i < gorotines; i++ { 183 select { 184 case err := <-ch: 185 if err != nil { 186 t.Fatalf("unexpected error: %s", err) 187 } 188 case <-time.After(5 * time.Second): 189 t.Fatalf("timeout") 190 } 191 } 192} 193 194func testCacheGetSet(c *Cache, itemsCount int) error { 195 for i := 0; i < itemsCount; i++ { 196 k := []byte(fmt.Sprintf("key %d", i)) 197 v := []byte(fmt.Sprintf("value %d", i)) 198 c.Set(k, v) 199 vv := c.Get(nil, k) 200 if string(vv) != string(v) { 201 return fmt.Errorf("unexpected value for key %q after insertion; got %q; want %q", k, vv, v) 202 } 203 } 204 misses := 0 205 for i := 0; i < itemsCount; i++ { 206 k := []byte(fmt.Sprintf("key %d", i)) 207 vExpected := fmt.Sprintf("value %d", i) 208 v := c.Get(nil, k) 209 if string(v) != string(vExpected) { 210 if len(v) > 0 { 211 return fmt.Errorf("unexpected value for key %q after all insertions; got %q; want %q", k, v, vExpected) 212 } 213 misses++ 214 } 215 } 216 if misses >= itemsCount/100 { 217 return fmt.Errorf("too many cache misses; got %d; want less than %d", misses, itemsCount/100) 218 } 219 return nil 220} 221 222func TestCacheResetUpdateStatsSetConcurrent(t *testing.T) { 223 c := New(12334) 224 225 stopCh := make(chan struct{}) 226 227 // run workers for cache reset 228 var resettersWG sync.WaitGroup 229 for i := 0; i < 10; i++ { 230 resettersWG.Add(1) 231 go func() { 232 defer resettersWG.Done() 233 for { 234 select { 235 case <-stopCh: 236 return 237 default: 238 c.Reset() 239 runtime.Gosched() 240 } 241 } 242 }() 243 } 244 245 // run workers for update cache stats 246 var statsWG sync.WaitGroup 247 for i := 0; i < 10; i++ { 248 statsWG.Add(1) 249 go func() { 250 defer statsWG.Done() 251 var s Stats 252 for { 253 select { 254 case <-stopCh: 255 return 256 default: 257 c.UpdateStats(&s) 258 runtime.Gosched() 259 } 260 } 261 }() 262 } 263 264 // run workers for setting data to cache 265 var settersWG sync.WaitGroup 266 for i := 0; i < 10; i++ { 267 settersWG.Add(1) 268 go func() { 269 defer settersWG.Done() 270 for j := 0; j < 100; j++ { 271 key := []byte(fmt.Sprintf("key_%d", j)) 272 value := []byte(fmt.Sprintf("value_%d", j)) 273 c.Set(key, value) 274 runtime.Gosched() 275 } 276 }() 277 } 278 279 // wait for setters 280 settersWG.Wait() 281 close(stopCh) 282 statsWG.Wait() 283 resettersWG.Wait() 284} 285