go.mercari.io/datastore@v1.8.2/dsmiddleware/dsmemcache/dsmemcache_test.go (about) 1 package dsmemcache 2 3 import ( 4 "context" 5 "fmt" 6 "os" 7 "strings" 8 "testing" 9 10 "github.com/MakeNowJust/heredoc/v2" 11 "github.com/bradfitz/gomemcache/memcache" 12 "go.mercari.io/datastore" 13 "go.mercari.io/datastore/dsmiddleware/dslog" 14 "go.mercari.io/datastore/dsmiddleware/storagecache" 15 "go.mercari.io/datastore/internal/testutils" 16 ) 17 18 func inCache(ctx context.Context, ch storagecache.Storage, key datastore.Key) (bool, error) { 19 resp, err := ch.GetMulti(ctx, []datastore.Key{key}) 20 if err != nil { 21 return false, err 22 } else if v := len(resp); v != 1 { 23 return false, nil 24 } else if v := resp[0]; v == nil { 25 return false, nil 26 } 27 28 return true, nil 29 } 30 31 func TestMemcache_Basic(t *testing.T) { 32 ctx, client, cleanUp := testutils.SetupCloudDatastore(t) 33 defer cleanUp() 34 35 var logs []string 36 logf := func(ctx context.Context, format string, args ...interface{}) { 37 t.Logf(format, args...) 38 logs = append(logs, fmt.Sprintf(format, args...)) 39 } 40 41 // setup. strategies are first in - first apply. 42 43 bLog := dslog.NewLogger("before: ", logf) 44 client.AppendMiddleware(bLog) 45 defer func() { 46 // stop logging before cleanUp func called. 47 client.RemoveMiddleware(bLog) 48 }() 49 50 memcacheClient := memcache.New(os.Getenv("MEMCACHE_ADDR")) 51 ch := New( 52 memcacheClient, 53 WithLogger(logf), 54 ) 55 client.AppendMiddleware(ch) 56 defer func() { 57 if err := memcacheClient.FlushAll(); err != nil { 58 t.Fatal(err) 59 } 60 // stop logging before cleanUp func called. 61 client.RemoveMiddleware(ch) 62 }() 63 64 aLog := dslog.NewLogger("after: ", logf) 65 client.AppendMiddleware(aLog) 66 defer func() { 67 // stop logging before cleanUp func called. 68 client.RemoveMiddleware(aLog) 69 }() 70 71 // exec. 72 73 type Data struct { 74 Name string 75 } 76 77 // Put. add to cache. 78 key := client.IDKey("Data", 111, nil) 79 objBefore := &Data{Name: "Data"} 80 if _, err := client.Put(ctx, key, objBefore); err != nil { 81 t.Fatal(err) 82 } 83 84 hit, err := inCache(ctx, ch, key) 85 if err != nil { 86 t.Fatal(err) 87 } else if v := hit; !v { 88 t.Fatalf("unexpected: %v", v) 89 } 90 91 // Get. from cache. 92 objAfter := &Data{} 93 err = client.Get(ctx, key, objAfter) 94 if err != nil { 95 t.Fatal(err) 96 } 97 98 // Delete. 99 err = client.Delete(ctx, key) 100 if err != nil { 101 t.Fatal(err) 102 } 103 104 hit, err = inCache(ctx, ch, key) 105 if err != nil { 106 t.Fatal(err) 107 } else if v := hit; v { 108 t.Fatalf("unexpected: %v", v) 109 } 110 111 expected := heredoc.Doc(` 112 before: PutMultiWithoutTx #1, len(keys)=1, keys=[/Data,111] 113 after: PutMultiWithoutTx #1, len(keys)=1, keys=[/Data,111] 114 after: PutMultiWithoutTx #1, keys=[/Data,111] 115 dsmiddleware/dsmemcache.SetMulti: incoming len=1 116 before: PutMultiWithoutTx #1, keys=[/Data,111] 117 dsmiddleware/dsmemcache.GetMulti: incoming len=1 118 dsmiddleware/dsmemcache.GetMulti: hit=1 miss=0 119 before: GetMultiWithoutTx #2, len(keys)=1, keys=[/Data,111] 120 dsmiddleware/dsmemcache.GetMulti: incoming len=1 121 dsmiddleware/dsmemcache.GetMulti: hit=1 miss=0 122 before: DeleteMultiWithoutTx #3, len(keys)=1, keys=[/Data,111] 123 after: DeleteMultiWithoutTx #2, len(keys)=1, keys=[/Data,111] 124 dsmiddleware/dsmemcache.DeleteMulti: incoming len=1 125 dsmiddleware/dsmemcache.GetMulti: incoming len=1 126 dsmiddleware/dsmemcache.GetMulti: hit=0 miss=1 127 `) 128 129 if v := strings.Join(logs, "\n") + "\n"; v != expected { 130 t.Errorf("unexpected: %v", v) 131 } 132 } 133 134 func TestMemcache_BasicWithoutExpire(t *testing.T) { 135 ctx, client, cleanUp := testutils.SetupCloudDatastore(t) 136 defer cleanUp() 137 138 var logs []string 139 logf := func(ctx context.Context, format string, args ...interface{}) { 140 t.Logf(format, args...) 141 logs = append(logs, fmt.Sprintf(format, args...)) 142 } 143 144 // setup. strategies are first in - first apply. 145 146 bLog := dslog.NewLogger("before: ", logf) 147 client.AppendMiddleware(bLog) 148 defer func() { 149 // stop logging before cleanUp func called. 150 client.RemoveMiddleware(bLog) 151 }() 152 153 memcacheClient := memcache.New(os.Getenv("MEMCACHE_ADDR")) 154 ch := New( 155 memcacheClient, 156 WithExpireDuration(0), 157 WithLogger(logf), 158 ) 159 client.AppendMiddleware(ch) 160 defer func() { 161 if err := memcacheClient.FlushAll(); err != nil { 162 t.Fatal(err) 163 } 164 // stop logging before cleanUp func called. 165 client.RemoveMiddleware(ch) 166 }() 167 168 aLog := dslog.NewLogger("after: ", logf) 169 client.AppendMiddleware(aLog) 170 defer func() { 171 // stop logging before cleanUp func called. 172 client.RemoveMiddleware(aLog) 173 }() 174 175 // exec. 176 177 type Data struct { 178 Name string 179 } 180 181 // Put. add to cache. 182 key := client.IDKey("Data", 111, nil) 183 objBefore := &Data{Name: "Data"} 184 if _, err := client.Put(ctx, key, objBefore); err != nil { 185 t.Fatal(err) 186 } 187 188 hit, err := inCache(ctx, ch, key) 189 if err != nil { 190 t.Fatal(err) 191 } else if v := hit; !v { 192 t.Fatalf("unexpected: %v", v) 193 } 194 195 // Get. from cache. 196 objAfter := &Data{} 197 err = client.Get(ctx, key, objAfter) 198 if err != nil { 199 t.Fatal(err) 200 } 201 202 // Delete. 203 err = client.Delete(ctx, key) 204 if err != nil { 205 t.Fatal(err) 206 } 207 208 hit, err = inCache(ctx, ch, key) 209 if err != nil { 210 t.Fatal(err) 211 } else if v := hit; v { 212 t.Fatalf("unexpected: %v", v) 213 } 214 215 expected := heredoc.Doc(` 216 before: PutMultiWithoutTx #1, len(keys)=1, keys=[/Data,111] 217 after: PutMultiWithoutTx #1, len(keys)=1, keys=[/Data,111] 218 after: PutMultiWithoutTx #1, keys=[/Data,111] 219 dsmiddleware/dsmemcache.SetMulti: incoming len=1 220 before: PutMultiWithoutTx #1, keys=[/Data,111] 221 dsmiddleware/dsmemcache.GetMulti: incoming len=1 222 dsmiddleware/dsmemcache.GetMulti: hit=1 miss=0 223 before: GetMultiWithoutTx #2, len(keys)=1, keys=[/Data,111] 224 dsmiddleware/dsmemcache.GetMulti: incoming len=1 225 dsmiddleware/dsmemcache.GetMulti: hit=1 miss=0 226 before: DeleteMultiWithoutTx #3, len(keys)=1, keys=[/Data,111] 227 after: DeleteMultiWithoutTx #2, len(keys)=1, keys=[/Data,111] 228 dsmiddleware/dsmemcache.DeleteMulti: incoming len=1 229 dsmiddleware/dsmemcache.GetMulti: incoming len=1 230 dsmiddleware/dsmemcache.GetMulti: hit=0 miss=1 231 `) 232 233 if v := strings.Join(logs, "\n") + "\n"; v != expected { 234 t.Errorf("unexpected: %v", v) 235 } 236 } 237 238 func TestMemcache_MultiError(t *testing.T) { 239 ctx, client, cleanUp := testutils.SetupCloudDatastore(t) 240 defer cleanUp() 241 242 var logs []string 243 logf := func(ctx context.Context, format string, args ...interface{}) { 244 t.Logf(format, args...) 245 logs = append(logs, fmt.Sprintf(format, args...)) 246 } 247 248 // setup. strategies are first in - first apply. 249 bLog := dslog.NewLogger("before: ", logf) 250 client.AppendMiddleware(bLog) 251 defer func() { 252 // stop logging before cleanUp func called. 253 client.RemoveMiddleware(bLog) 254 }() 255 256 memcacheClient := memcache.New(os.Getenv("MEMCACHE_ADDR")) 257 ch := New( 258 memcacheClient, 259 WithLogger(logf), 260 ) 261 client.AppendMiddleware(ch) 262 defer func() { 263 if err := memcacheClient.FlushAll(); err != nil { 264 t.Fatal(err) 265 } 266 // stop logging before cleanUp func called. 267 client.RemoveMiddleware(ch) 268 }() 269 270 aLog := dslog.NewLogger("after: ", logf) 271 client.AppendMiddleware(aLog) 272 defer func() { 273 // stop logging before cleanUp func called. 274 client.RemoveMiddleware(aLog) 275 }() 276 277 // exec. 278 279 type Data struct { 280 Name string 281 } 282 283 const size = 10 284 285 keys := make([]datastore.Key, 0, size) 286 list := make([]*Data, 0, size) 287 for i := 1; i <= size; i++ { 288 keys = append(keys, client.IDKey("Data", int64(i), nil)) 289 list = append(list, &Data{ 290 Name: fmt.Sprintf("#%d", i), 291 }) 292 } 293 294 _, err := client.PutMulti(ctx, keys, list) 295 if err != nil { 296 t.Fatal(err) 297 } 298 299 for _, key := range keys { 300 if key.ID()%2 == 0 { 301 // delete cache id=2, 4, 6, 8, 10 302 err := ch.DeleteMulti(ctx, []datastore.Key{key}) 303 if err != nil { 304 t.Fatal(err) 305 } 306 } 307 if key.ID()%3 == 0 { 308 client.RemoveMiddleware(ch) 309 err := client.Delete(ctx, key) 310 if err != nil { 311 t.Fatal(err) 312 } 313 314 client.RemoveMiddleware(aLog) 315 client.AppendMiddleware(ch) 316 client.AppendMiddleware(aLog) 317 } 318 } 319 320 list = make([]*Data, size) 321 err = client.GetMulti(ctx, keys, list) 322 merr, ok := err.(datastore.MultiError) 323 if !ok { 324 t.Fatal(err) 325 } 326 327 if v := len(merr); v != size { 328 t.Fatalf("unexpected: %v", v) 329 } 330 for idx, err := range merr { 331 key := keys[idx] 332 if key.ID()%2 == 0 && key.ID()%3 == 0 { 333 // not exists on cache & datastore both 334 if err != datastore.ErrNoSuchEntity { 335 t.Error(err) 336 } 337 } else { 338 if v := list[idx].Name; v != fmt.Sprintf("#%d", idx+1) { 339 t.Errorf("unexpected: %v", v) 340 } 341 } 342 } 343 344 expected := heredoc.Doc(` 345 before: PutMultiWithoutTx #1, len(keys)=10, keys=[/Data,1, /Data,2, /Data,3, /Data,4, /Data,5, /Data,6, /Data,7, /Data,8, /Data,9, /Data,10] 346 after: PutMultiWithoutTx #1, len(keys)=10, keys=[/Data,1, /Data,2, /Data,3, /Data,4, /Data,5, /Data,6, /Data,7, /Data,8, /Data,9, /Data,10] 347 after: PutMultiWithoutTx #1, keys=[/Data,1, /Data,2, /Data,3, /Data,4, /Data,5, /Data,6, /Data,7, /Data,8, /Data,9, /Data,10] 348 dsmiddleware/dsmemcache.SetMulti: incoming len=10 349 before: PutMultiWithoutTx #1, keys=[/Data,1, /Data,2, /Data,3, /Data,4, /Data,5, /Data,6, /Data,7, /Data,8, /Data,9, /Data,10] 350 dsmiddleware/dsmemcache.DeleteMulti: incoming len=1 351 before: DeleteMultiWithoutTx #2, len(keys)=1, keys=[/Data,3] 352 after: DeleteMultiWithoutTx #2, len(keys)=1, keys=[/Data,3] 353 dsmiddleware/dsmemcache.DeleteMulti: incoming len=1 354 dsmiddleware/dsmemcache.DeleteMulti: incoming len=1 355 before: DeleteMultiWithoutTx #3, len(keys)=1, keys=[/Data,6] 356 after: DeleteMultiWithoutTx #3, len(keys)=1, keys=[/Data,6] 357 dsmiddleware/dsmemcache.DeleteMulti: incoming len=1 358 before: DeleteMultiWithoutTx #4, len(keys)=1, keys=[/Data,9] 359 after: DeleteMultiWithoutTx #4, len(keys)=1, keys=[/Data,9] 360 dsmiddleware/dsmemcache.DeleteMulti: incoming len=1 361 before: GetMultiWithoutTx #5, len(keys)=10, keys=[/Data,1, /Data,2, /Data,3, /Data,4, /Data,5, /Data,6, /Data,7, /Data,8, /Data,9, /Data,10] 362 dsmiddleware/dsmemcache.GetMulti: incoming len=10 363 dsmiddleware/dsmemcache.GetMulti: hit=5 miss=5 364 after: GetMultiWithoutTx #5, len(keys)=5, keys=[/Data,2, /Data,4, /Data,6, /Data,8, /Data,10] 365 after: GetMultiWithoutTx #5, err=datastore: no such entity 366 dsmiddleware/dsmemcache.SetMulti: incoming len=4 367 before: GetMultiWithoutTx #5, err=datastore: no such entity 368 `) 369 370 if v := strings.Join(logs, "\n") + "\n"; v != expected { 371 t.Errorf("unexpected: %v", v) 372 } 373 }