go.mercari.io/datastore@v1.8.2/dsmiddleware/localcache/localcache_test.go (about) 1 package localcache 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "regexp" 8 "strings" 9 "testing" 10 11 "github.com/MakeNowJust/heredoc/v2" 12 "go.mercari.io/datastore" 13 "go.mercari.io/datastore/dsmiddleware/dslog" 14 "go.mercari.io/datastore/internal/testutils" 15 "google.golang.org/api/iterator" 16 ) 17 18 func TestLocalCache_Basic(t *testing.T) { 19 ctx, client, cleanUp := testutils.SetupCloudDatastore(t) 20 defer cleanUp() 21 22 var logs []string 23 logf := func(ctx context.Context, format string, args ...interface{}) { 24 t.Logf(format, args...) 25 logs = append(logs, fmt.Sprintf(format, args...)) 26 } 27 28 // setup. strategies are first in - first apply. 29 30 bLog := dslog.NewLogger("before: ", logf) 31 client.AppendMiddleware(bLog) 32 defer func() { 33 // stop logging before cleanUp func called. 34 client.RemoveMiddleware(bLog) 35 }() 36 37 ch := New() 38 client.AppendMiddleware(ch) 39 defer func() { 40 // stop logging before cleanUp func called. 41 client.RemoveMiddleware(ch) 42 }() 43 44 aLog := dslog.NewLogger("after: ", logf) 45 client.AppendMiddleware(aLog) 46 defer func() { 47 // stop logging before cleanUp func called. 48 client.RemoveMiddleware(aLog) 49 }() 50 51 // exec. 52 53 type Data struct { 54 Name string 55 } 56 57 // Put. add to cache. 58 key := client.IDKey("Data", 111, nil) 59 objBefore := &Data{Name: "Data"} 60 _, err := client.Put(ctx, key, objBefore) 61 if err != nil { 62 t.Fatal(err) 63 } 64 65 if v := ch.HasCache(key); !v { 66 t.Fatalf("unexpected: %v", v) 67 } 68 69 // Get. from cache. 70 objAfter := &Data{} 71 err = client.Get(ctx, key, objAfter) 72 if err != nil { 73 t.Fatal(err) 74 } 75 76 // Delete. 77 err = client.Delete(ctx, key) 78 if err != nil { 79 t.Fatal(err) 80 } 81 82 if v := ch.HasCache(key); v { 83 t.Fatalf("unexpected: %v", v) 84 } 85 86 expected := heredoc.Doc(` 87 before: PutMultiWithoutTx #1, len(keys)=1, keys=[/Data,111] 88 after: PutMultiWithoutTx #1, len(keys)=1, keys=[/Data,111] 89 after: PutMultiWithoutTx #1, keys=[/Data,111] 90 before: PutMultiWithoutTx #1, keys=[/Data,111] 91 before: GetMultiWithoutTx #2, len(keys)=1, keys=[/Data,111] 92 before: DeleteMultiWithoutTx #3, len(keys)=1, keys=[/Data,111] 93 after: DeleteMultiWithoutTx #2, len(keys)=1, keys=[/Data,111] 94 `) 95 96 if v := strings.Join(logs, "\n") + "\n"; v != expected { 97 t.Errorf("unexpected: %v", v) 98 } 99 } 100 101 func TestLocalCache_WithIncludeKinds(t *testing.T) { 102 ctx, client, cleanUp := testutils.SetupCloudDatastore(t) 103 defer cleanUp() 104 105 var logs []string 106 logf := func(ctx context.Context, format string, args ...interface{}) { 107 t.Logf(format, args...) 108 logs = append(logs, fmt.Sprintf(format, args...)) 109 } 110 111 // setup. strategies are first in - first apply. 112 113 bLog := dslog.NewLogger("before: ", logf) 114 client.AppendMiddleware(bLog) 115 defer func() { 116 // stop logging before cleanUp func called. 117 client.RemoveMiddleware(bLog) 118 }() 119 120 ch := New( 121 WithLogger(logf), 122 WithIncludeKinds("DataA"), 123 ) 124 client.AppendMiddleware(ch) 125 defer func() { 126 // stop logging before cleanUp func called. 127 client.RemoveMiddleware(ch) 128 }() 129 130 aLog := dslog.NewLogger("after: ", logf) 131 client.AppendMiddleware(aLog) 132 defer func() { 133 // stop logging before cleanUp func called. 134 client.RemoveMiddleware(aLog) 135 }() 136 137 // exec. 138 139 type Data struct { 140 Name string 141 } 142 143 { // Put. cache target. 144 key := client.IDKey("DataA", 111, nil) 145 objBefore := &Data{Name: "A"} 146 _, err := client.Put(ctx, key, objBefore) 147 if err != nil { 148 t.Fatal(err) 149 } 150 151 obj := &Data{} 152 err = client.Get(ctx, key, obj) 153 if err != nil { 154 t.Fatal(err) 155 } 156 if v := obj.Name; v != "A" { 157 t.Errorf("unexpected: %v", v) 158 } 159 160 err = client.Delete(ctx, key) 161 if err != nil { 162 t.Fatal(err) 163 } 164 } 165 { // Put. cache ignored. 166 key := client.IDKey("DataB", 111, nil) 167 objBefore := &Data{Name: "B"} 168 _, err := client.Put(ctx, key, objBefore) 169 if err != nil { 170 t.Fatal(err) 171 } 172 173 obj := &Data{} 174 err = client.Get(ctx, key, obj) 175 if err != nil { 176 t.Fatal(err) 177 } 178 if v := obj.Name; v != "B" { 179 t.Errorf("unexpected: %v", v) 180 } 181 182 err = client.Delete(ctx, key) 183 if err != nil { 184 t.Fatal(err) 185 } 186 } 187 { // Put. cache target & ignored. 188 keyInc := client.IDKey("DataA", 111, nil) 189 keyExc := client.IDKey("DataB", 111, nil) 190 191 list := []*Data{{Name: "A"}, {Name: "B"}} 192 _, err := client.PutMulti(ctx, []datastore.Key{keyInc, keyExc}, list) 193 if err != nil { 194 t.Fatal(err) 195 } 196 197 list = make([]*Data, 2) 198 err = client.GetMulti(ctx, []datastore.Key{keyInc, keyExc}, list) 199 if err != nil { 200 t.Fatal(err) 201 } 202 if v := len(list); v != 2 { 203 t.Fatalf("unexpected: %v", v) 204 } 205 if v := list[0].Name; v != "A" { 206 t.Errorf("unexpected: %v", v) 207 } 208 if v := list[1].Name; v != "B" { 209 t.Errorf("unexpected: %v", v) 210 } 211 212 err = client.DeleteMulti(ctx, []datastore.Key{keyInc, keyExc}) 213 if err != nil { 214 t.Fatal(err) 215 } 216 } 217 { // Put. partially hit 218 keyIncA := client.IDKey("DataA", 111, nil) 219 keyIncB := client.IDKey("DataA", 222, nil) 220 keyExcA := client.IDKey("DataB", 111, nil) 221 keyExcB := client.IDKey("DataB", 222, nil) 222 223 list := []*Data{{Name: "A1"}, {Name: "A2"}, {Name: "B1"}, {Name: "B2"}} 224 _, err := client.PutMulti(ctx, []datastore.Key{keyIncA, keyIncB, keyExcA, keyExcB}, list) 225 if err != nil { 226 t.Fatal(err) 227 } 228 229 ch.DeleteCache(ctx, keyIncB) 230 ch.DeleteCache(ctx, keyExcB) 231 232 list = make([]*Data, 4) 233 err = client.GetMulti(ctx, []datastore.Key{keyIncA, keyIncB, keyExcA, keyExcB}, list) 234 if err != nil { 235 t.Fatal(err) 236 } 237 if v := len(list); v != 4 { 238 t.Fatalf("unexpected: %v", v) 239 } 240 if v := list[0].Name; v != "A1" { 241 t.Errorf("unexpected: %v", v) 242 } 243 if v := list[1].Name; v != "A2" { 244 t.Errorf("unexpected: %v", v) 245 } 246 if v := list[2].Name; v != "B1" { 247 t.Errorf("unexpected: %v", v) 248 } 249 if v := list[3].Name; v != "B2" { 250 t.Errorf("unexpected: %v", v) 251 } 252 253 err = client.DeleteMulti(ctx, []datastore.Key{keyIncA, keyIncB, keyExcA, keyExcB}) 254 if err != nil { 255 t.Fatal(err) 256 } 257 } 258 259 expected := heredoc.Doc(` 260 before: PutMultiWithoutTx #1, len(keys)=1, keys=[/DataA,111] 261 after: PutMultiWithoutTx #1, len(keys)=1, keys=[/DataA,111] 262 after: PutMultiWithoutTx #1, keys=[/DataA,111] 263 dsmiddleware/localcache.SetMulti: len=1 264 dsmiddleware/localcache.SetMulti: idx=0 key=/DataA,111 len(ps)=1 265 before: PutMultiWithoutTx #1, keys=[/DataA,111] 266 before: GetMultiWithoutTx #2, len(keys)=1, keys=[/DataA,111] 267 dsmiddleware/localcache.GetMulti: len=1 268 dsmiddleware/localcache.GetMulti: idx=0 key=/DataA,111 269 dsmiddleware/localcache.GetMulti: idx=0, hit key=/DataA,111 len(ps)=1 270 before: DeleteMultiWithoutTx #3, len(keys)=1, keys=[/DataA,111] 271 after: DeleteMultiWithoutTx #2, len(keys)=1, keys=[/DataA,111] 272 dsmiddleware/localcache.DeleteMulti: len=1 273 dsmiddleware/localcache.DeleteMulti: idx=0 key=/DataA,111 274 before: PutMultiWithoutTx #4, len(keys)=1, keys=[/DataB,111] 275 after: PutMultiWithoutTx #3, len(keys)=1, keys=[/DataB,111] 276 after: PutMultiWithoutTx #3, keys=[/DataB,111] 277 before: PutMultiWithoutTx #4, keys=[/DataB,111] 278 before: GetMultiWithoutTx #5, len(keys)=1, keys=[/DataB,111] 279 after: GetMultiWithoutTx #4, len(keys)=1, keys=[/DataB,111] 280 before: DeleteMultiWithoutTx #6, len(keys)=1, keys=[/DataB,111] 281 after: DeleteMultiWithoutTx #5, len(keys)=1, keys=[/DataB,111] 282 before: PutMultiWithoutTx #7, len(keys)=2, keys=[/DataA,111, /DataB,111] 283 after: PutMultiWithoutTx #6, len(keys)=2, keys=[/DataA,111, /DataB,111] 284 after: PutMultiWithoutTx #6, keys=[/DataA,111, /DataB,111] 285 dsmiddleware/localcache.SetMulti: len=1 286 dsmiddleware/localcache.SetMulti: idx=0 key=/DataA,111 len(ps)=1 287 before: PutMultiWithoutTx #7, keys=[/DataA,111, /DataB,111] 288 before: GetMultiWithoutTx #8, len(keys)=2, keys=[/DataA,111, /DataB,111] 289 dsmiddleware/localcache.GetMulti: len=1 290 dsmiddleware/localcache.GetMulti: idx=0 key=/DataA,111 291 dsmiddleware/localcache.GetMulti: idx=0, hit key=/DataA,111 len(ps)=1 292 after: GetMultiWithoutTx #7, len(keys)=1, keys=[/DataB,111] 293 before: DeleteMultiWithoutTx #9, len(keys)=2, keys=[/DataA,111, /DataB,111] 294 after: DeleteMultiWithoutTx #8, len(keys)=2, keys=[/DataA,111, /DataB,111] 295 dsmiddleware/localcache.DeleteMulti: len=1 296 dsmiddleware/localcache.DeleteMulti: idx=0 key=/DataA,111 297 before: PutMultiWithoutTx #10, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222] 298 after: PutMultiWithoutTx #9, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222] 299 after: PutMultiWithoutTx #9, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222] 300 dsmiddleware/localcache.SetMulti: len=2 301 dsmiddleware/localcache.SetMulti: idx=0 key=/DataA,111 len(ps)=1 302 dsmiddleware/localcache.SetMulti: idx=1 key=/DataA,222 len(ps)=1 303 before: PutMultiWithoutTx #10, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222] 304 dsmiddleware/localcache.DeleteCache: key=/DataA,222 305 dsmiddleware/localcache.DeleteCache: key=/DataB,222 306 before: GetMultiWithoutTx #11, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222] 307 dsmiddleware/localcache.GetMulti: len=2 308 dsmiddleware/localcache.GetMulti: idx=0 key=/DataA,111 309 dsmiddleware/localcache.GetMulti: idx=1 key=/DataA,222 310 dsmiddleware/localcache.GetMulti: idx=0, hit key=/DataA,111 len(ps)=1 311 dsmiddleware/localcache.GetMulti: idx=1, missed key=/DataA,222 312 after: GetMultiWithoutTx #10, len(keys)=3, keys=[/DataA,222, /DataB,111, /DataB,222] 313 dsmiddleware/localcache.SetMulti: len=1 314 dsmiddleware/localcache.SetMulti: idx=0 key=/DataA,222 len(ps)=1 315 before: DeleteMultiWithoutTx #12, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222] 316 after: DeleteMultiWithoutTx #11, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222] 317 dsmiddleware/localcache.DeleteMulti: len=2 318 dsmiddleware/localcache.DeleteMulti: idx=0 key=/DataA,111 319 dsmiddleware/localcache.DeleteMulti: idx=1 key=/DataA,222 320 `) 321 322 if v := strings.Join(logs, "\n") + "\n"; v != expected { 323 t.Errorf("unexpected: %v", v) 324 } 325 } 326 327 func TestLocalCache_WithExcludeKinds(t *testing.T) { 328 ctx, client, cleanUp := testutils.SetupCloudDatastore(t) 329 defer cleanUp() 330 331 var logs []string 332 logf := func(ctx context.Context, format string, args ...interface{}) { 333 t.Logf(format, args...) 334 logs = append(logs, fmt.Sprintf(format, args...)) 335 } 336 337 // setup. strategies are first in - first apply. 338 339 bLog := dslog.NewLogger("before: ", logf) 340 client.AppendMiddleware(bLog) 341 defer func() { 342 // stop logging before cleanUp func called. 343 client.RemoveMiddleware(bLog) 344 }() 345 346 ch := New( 347 WithLogger(logf), 348 WithExcludeKinds("DataA"), 349 ) 350 client.AppendMiddleware(ch) 351 defer func() { 352 // stop logging before cleanUp func called. 353 client.RemoveMiddleware(ch) 354 }() 355 356 aLog := dslog.NewLogger("after: ", logf) 357 client.AppendMiddleware(aLog) 358 defer func() { 359 // stop logging before cleanUp func called. 360 client.RemoveMiddleware(aLog) 361 }() 362 363 // exec. 364 365 type Data struct { 366 Name string 367 } 368 369 { // Put. cache target. 370 key := client.IDKey("DataA", 111, nil) 371 objBefore := &Data{Name: "A"} 372 _, err := client.Put(ctx, key, objBefore) 373 if err != nil { 374 t.Fatal(err) 375 } 376 377 obj := &Data{} 378 err = client.Get(ctx, key, obj) 379 if err != nil { 380 t.Fatal(err) 381 } 382 if v := obj.Name; v != "A" { 383 t.Errorf("unexpected: %v", v) 384 } 385 386 err = client.Delete(ctx, key) 387 if err != nil { 388 t.Fatal(err) 389 } 390 } 391 { // Put. cache ignored. 392 key := client.IDKey("DataB", 111, nil) 393 objBefore := &Data{Name: "B"} 394 _, err := client.Put(ctx, key, objBefore) 395 if err != nil { 396 t.Fatal(err) 397 } 398 399 obj := &Data{} 400 err = client.Get(ctx, key, obj) 401 if err != nil { 402 t.Fatal(err) 403 } 404 if v := obj.Name; v != "B" { 405 t.Errorf("unexpected: %v", v) 406 } 407 408 err = client.Delete(ctx, key) 409 if err != nil { 410 t.Fatal(err) 411 } 412 } 413 { // Put. cache target & ignored. 414 keyInc := client.IDKey("DataA", 111, nil) 415 keyExc := client.IDKey("DataB", 111, nil) 416 417 list := []*Data{{Name: "A"}, {Name: "B"}} 418 _, err := client.PutMulti(ctx, []datastore.Key{keyInc, keyExc}, list) 419 if err != nil { 420 t.Fatal(err) 421 } 422 423 list = make([]*Data, 2) 424 err = client.GetMulti(ctx, []datastore.Key{keyInc, keyExc}, list) 425 if err != nil { 426 t.Fatal(err) 427 } 428 if v := len(list); v != 2 { 429 t.Fatalf("unexpected: %v", v) 430 } 431 if v := list[0].Name; v != "A" { 432 t.Errorf("unexpected: %v", v) 433 } 434 if v := list[1].Name; v != "B" { 435 t.Errorf("unexpected: %v", v) 436 } 437 438 err = client.DeleteMulti(ctx, []datastore.Key{keyInc, keyExc}) 439 if err != nil { 440 t.Fatal(err) 441 } 442 } 443 { // Put. partially hit 444 keyIncA := client.IDKey("DataA", 111, nil) 445 keyIncB := client.IDKey("DataA", 222, nil) 446 keyExcA := client.IDKey("DataB", 111, nil) 447 keyExcB := client.IDKey("DataB", 222, nil) 448 449 list := []*Data{{Name: "A1"}, {Name: "A2"}, {Name: "B1"}, {Name: "B2"}} 450 _, err := client.PutMulti(ctx, []datastore.Key{keyIncA, keyIncB, keyExcA, keyExcB}, list) 451 if err != nil { 452 t.Fatal(err) 453 } 454 455 ch.DeleteCache(ctx, keyIncB) 456 ch.DeleteCache(ctx, keyExcB) 457 458 list = make([]*Data, 4) 459 err = client.GetMulti(ctx, []datastore.Key{keyIncA, keyIncB, keyExcA, keyExcB}, list) 460 if err != nil { 461 t.Fatal(err) 462 } 463 if v := len(list); v != 4 { 464 t.Fatalf("unexpected: %v", v) 465 } 466 if v := list[0].Name; v != "A1" { 467 t.Errorf("unexpected: %v", v) 468 } 469 if v := list[1].Name; v != "A2" { 470 t.Errorf("unexpected: %v", v) 471 } 472 if v := list[2].Name; v != "B1" { 473 t.Errorf("unexpected: %v", v) 474 } 475 if v := list[3].Name; v != "B2" { 476 t.Errorf("unexpected: %v", v) 477 } 478 479 err = client.DeleteMulti(ctx, []datastore.Key{keyIncA, keyIncB, keyExcA, keyExcB}) 480 if err != nil { 481 t.Fatal(err) 482 } 483 } 484 485 expected := heredoc.Doc(` 486 before: PutMultiWithoutTx #1, len(keys)=1, keys=[/DataA,111] 487 after: PutMultiWithoutTx #1, len(keys)=1, keys=[/DataA,111] 488 after: PutMultiWithoutTx #1, keys=[/DataA,111] 489 before: PutMultiWithoutTx #1, keys=[/DataA,111] 490 before: GetMultiWithoutTx #2, len(keys)=1, keys=[/DataA,111] 491 after: GetMultiWithoutTx #2, len(keys)=1, keys=[/DataA,111] 492 before: DeleteMultiWithoutTx #3, len(keys)=1, keys=[/DataA,111] 493 after: DeleteMultiWithoutTx #3, len(keys)=1, keys=[/DataA,111] 494 before: PutMultiWithoutTx #4, len(keys)=1, keys=[/DataB,111] 495 after: PutMultiWithoutTx #4, len(keys)=1, keys=[/DataB,111] 496 after: PutMultiWithoutTx #4, keys=[/DataB,111] 497 dsmiddleware/localcache.SetMulti: len=1 498 dsmiddleware/localcache.SetMulti: idx=0 key=/DataB,111 len(ps)=1 499 before: PutMultiWithoutTx #4, keys=[/DataB,111] 500 before: GetMultiWithoutTx #5, len(keys)=1, keys=[/DataB,111] 501 dsmiddleware/localcache.GetMulti: len=1 502 dsmiddleware/localcache.GetMulti: idx=0 key=/DataB,111 503 dsmiddleware/localcache.GetMulti: idx=0, hit key=/DataB,111 len(ps)=1 504 before: DeleteMultiWithoutTx #6, len(keys)=1, keys=[/DataB,111] 505 after: DeleteMultiWithoutTx #5, len(keys)=1, keys=[/DataB,111] 506 dsmiddleware/localcache.DeleteMulti: len=1 507 dsmiddleware/localcache.DeleteMulti: idx=0 key=/DataB,111 508 before: PutMultiWithoutTx #7, len(keys)=2, keys=[/DataA,111, /DataB,111] 509 after: PutMultiWithoutTx #6, len(keys)=2, keys=[/DataA,111, /DataB,111] 510 after: PutMultiWithoutTx #6, keys=[/DataA,111, /DataB,111] 511 dsmiddleware/localcache.SetMulti: len=1 512 dsmiddleware/localcache.SetMulti: idx=0 key=/DataB,111 len(ps)=1 513 before: PutMultiWithoutTx #7, keys=[/DataA,111, /DataB,111] 514 before: GetMultiWithoutTx #8, len(keys)=2, keys=[/DataA,111, /DataB,111] 515 dsmiddleware/localcache.GetMulti: len=1 516 dsmiddleware/localcache.GetMulti: idx=0 key=/DataB,111 517 dsmiddleware/localcache.GetMulti: idx=0, hit key=/DataB,111 len(ps)=1 518 after: GetMultiWithoutTx #7, len(keys)=1, keys=[/DataA,111] 519 before: DeleteMultiWithoutTx #9, len(keys)=2, keys=[/DataA,111, /DataB,111] 520 after: DeleteMultiWithoutTx #8, len(keys)=2, keys=[/DataA,111, /DataB,111] 521 dsmiddleware/localcache.DeleteMulti: len=1 522 dsmiddleware/localcache.DeleteMulti: idx=0 key=/DataB,111 523 before: PutMultiWithoutTx #10, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222] 524 after: PutMultiWithoutTx #9, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222] 525 after: PutMultiWithoutTx #9, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222] 526 dsmiddleware/localcache.SetMulti: len=2 527 dsmiddleware/localcache.SetMulti: idx=0 key=/DataB,111 len(ps)=1 528 dsmiddleware/localcache.SetMulti: idx=1 key=/DataB,222 len(ps)=1 529 before: PutMultiWithoutTx #10, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222] 530 dsmiddleware/localcache.DeleteCache: key=/DataA,222 531 dsmiddleware/localcache.DeleteCache: key=/DataB,222 532 before: GetMultiWithoutTx #11, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222] 533 dsmiddleware/localcache.GetMulti: len=2 534 dsmiddleware/localcache.GetMulti: idx=0 key=/DataB,111 535 dsmiddleware/localcache.GetMulti: idx=1 key=/DataB,222 536 dsmiddleware/localcache.GetMulti: idx=0, hit key=/DataB,111 len(ps)=1 537 dsmiddleware/localcache.GetMulti: idx=1, missed key=/DataB,222 538 after: GetMultiWithoutTx #10, len(keys)=3, keys=[/DataA,111, /DataA,222, /DataB,222] 539 dsmiddleware/localcache.SetMulti: len=1 540 dsmiddleware/localcache.SetMulti: idx=0 key=/DataB,222 len(ps)=1 541 before: DeleteMultiWithoutTx #12, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222] 542 after: DeleteMultiWithoutTx #11, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222] 543 dsmiddleware/localcache.DeleteMulti: len=2 544 dsmiddleware/localcache.DeleteMulti: idx=0 key=/DataB,111 545 dsmiddleware/localcache.DeleteMulti: idx=1 key=/DataB,222 546 `) 547 548 if v := strings.Join(logs, "\n") + "\n"; v != expected { 549 t.Errorf("unexpected: %v", v) 550 } 551 } 552 553 func TestLocalCache_WithKeyFilter(t *testing.T) { 554 ctx, client, cleanUp := testutils.SetupCloudDatastore(t) 555 defer cleanUp() 556 557 var logs []string 558 logf := func(ctx context.Context, format string, args ...interface{}) { 559 t.Logf(format, args...) 560 logs = append(logs, fmt.Sprintf(format, args...)) 561 } 562 563 // setup. strategies are first in - first apply. 564 565 bLog := dslog.NewLogger("before: ", logf) 566 client.AppendMiddleware(bLog) 567 defer func() { 568 // stop logging before cleanUp func called. 569 client.RemoveMiddleware(bLog) 570 }() 571 572 ch := New( 573 WithLogger(logf), 574 WithKeyFilter(func(ctx context.Context, key datastore.Key) bool { 575 return key.ID() != 111 576 }), 577 ) 578 client.AppendMiddleware(ch) 579 defer func() { 580 // stop logging before cleanUp func called. 581 client.RemoveMiddleware(ch) 582 }() 583 584 aLog := dslog.NewLogger("after: ", logf) 585 client.AppendMiddleware(aLog) 586 defer func() { 587 // stop logging before cleanUp func called. 588 client.RemoveMiddleware(aLog) 589 }() 590 591 // exec. 592 593 type Data struct { 594 Name string 595 } 596 597 { // Put. cache target. 598 key := client.IDKey("DataA", 222, nil) 599 objBefore := &Data{Name: "A"} 600 _, err := client.Put(ctx, key, objBefore) 601 if err != nil { 602 t.Fatal(err) 603 } 604 605 obj := &Data{} 606 err = client.Get(ctx, key, obj) 607 if err != nil { 608 t.Fatal(err) 609 } 610 if v := obj.Name; v != "A" { 611 t.Errorf("unexpected: %v", v) 612 } 613 614 err = client.Delete(ctx, key) 615 if err != nil { 616 t.Fatal(err) 617 } 618 } 619 { // Put. cache ignored. 620 key := client.IDKey("DataB", 111, nil) 621 objBefore := &Data{Name: "B"} 622 _, err := client.Put(ctx, key, objBefore) 623 if err != nil { 624 t.Fatal(err) 625 } 626 627 obj := &Data{} 628 err = client.Get(ctx, key, obj) 629 if err != nil { 630 t.Fatal(err) 631 } 632 if v := obj.Name; v != "B" { 633 t.Errorf("unexpected: %v", v) 634 } 635 636 err = client.Delete(ctx, key) 637 if err != nil { 638 t.Fatal(err) 639 } 640 } 641 { // Put. cache target & ignored. 642 keyIgnore := client.IDKey("DataA", 111, nil) 643 keyTarget := client.IDKey("DataB", 222, nil) 644 645 list := []*Data{{Name: "A"}, {Name: "B"}} 646 _, err := client.PutMulti(ctx, []datastore.Key{keyIgnore, keyTarget}, list) 647 if err != nil { 648 t.Fatal(err) 649 } 650 651 list = make([]*Data, 2) 652 err = client.GetMulti(ctx, []datastore.Key{keyIgnore, keyTarget}, list) 653 if err != nil { 654 t.Fatal(err) 655 } 656 if v := len(list); v != 2 { 657 t.Fatalf("unexpected: %v", v) 658 } 659 if v := list[0].Name; v != "A" { 660 t.Errorf("unexpected: %v", v) 661 } 662 if v := list[1].Name; v != "B" { 663 t.Errorf("unexpected: %v", v) 664 } 665 666 err = client.DeleteMulti(ctx, []datastore.Key{keyIgnore, keyTarget}) 667 if err != nil { 668 t.Fatal(err) 669 } 670 } 671 { // Put. partially hit 672 keyIgnoreA := client.IDKey("DataA", 111, nil) 673 keyIgnoreB := client.IDKey("DataB", 111, nil) 674 keyTargetA := client.IDKey("DataA", 222, nil) 675 keyTargetB := client.IDKey("DataB", 222, nil) 676 677 list := []*Data{{Name: "A1"}, {Name: "A2"}, {Name: "B1"}, {Name: "B2"}} 678 _, err := client.PutMulti(ctx, []datastore.Key{keyIgnoreA, keyTargetA, keyIgnoreB, keyTargetB}, list) 679 if err != nil { 680 t.Fatal(err) 681 } 682 683 ch.DeleteCache(ctx, keyIgnoreA) 684 ch.DeleteCache(ctx, keyTargetA) 685 686 list = make([]*Data, 4) 687 err = client.GetMulti(ctx, []datastore.Key{keyIgnoreA, keyTargetA, keyIgnoreB, keyTargetB}, list) 688 if err != nil { 689 t.Fatal(err) 690 } 691 if v := len(list); v != 4 { 692 t.Fatalf("unexpected: %v", v) 693 } 694 if v := list[0].Name; v != "A1" { 695 t.Errorf("unexpected: %v", v) 696 } 697 if v := list[1].Name; v != "A2" { 698 t.Errorf("unexpected: %v", v) 699 } 700 if v := list[2].Name; v != "B1" { 701 t.Errorf("unexpected: %v", v) 702 } 703 if v := list[3].Name; v != "B2" { 704 t.Errorf("unexpected: %v", v) 705 } 706 707 err = client.DeleteMulti(ctx, []datastore.Key{keyIgnoreA, keyTargetA, keyIgnoreB, keyTargetB}) 708 if err != nil { 709 t.Fatal(err) 710 } 711 } 712 713 expected := heredoc.Doc(` 714 before: PutMultiWithoutTx #1, len(keys)=1, keys=[/DataA,222] 715 after: PutMultiWithoutTx #1, len(keys)=1, keys=[/DataA,222] 716 after: PutMultiWithoutTx #1, keys=[/DataA,222] 717 dsmiddleware/localcache.SetMulti: len=1 718 dsmiddleware/localcache.SetMulti: idx=0 key=/DataA,222 len(ps)=1 719 before: PutMultiWithoutTx #1, keys=[/DataA,222] 720 before: GetMultiWithoutTx #2, len(keys)=1, keys=[/DataA,222] 721 dsmiddleware/localcache.GetMulti: len=1 722 dsmiddleware/localcache.GetMulti: idx=0 key=/DataA,222 723 dsmiddleware/localcache.GetMulti: idx=0, hit key=/DataA,222 len(ps)=1 724 before: DeleteMultiWithoutTx #3, len(keys)=1, keys=[/DataA,222] 725 after: DeleteMultiWithoutTx #2, len(keys)=1, keys=[/DataA,222] 726 dsmiddleware/localcache.DeleteMulti: len=1 727 dsmiddleware/localcache.DeleteMulti: idx=0 key=/DataA,222 728 before: PutMultiWithoutTx #4, len(keys)=1, keys=[/DataB,111] 729 after: PutMultiWithoutTx #3, len(keys)=1, keys=[/DataB,111] 730 after: PutMultiWithoutTx #3, keys=[/DataB,111] 731 before: PutMultiWithoutTx #4, keys=[/DataB,111] 732 before: GetMultiWithoutTx #5, len(keys)=1, keys=[/DataB,111] 733 after: GetMultiWithoutTx #4, len(keys)=1, keys=[/DataB,111] 734 before: DeleteMultiWithoutTx #6, len(keys)=1, keys=[/DataB,111] 735 after: DeleteMultiWithoutTx #5, len(keys)=1, keys=[/DataB,111] 736 before: PutMultiWithoutTx #7, len(keys)=2, keys=[/DataA,111, /DataB,222] 737 after: PutMultiWithoutTx #6, len(keys)=2, keys=[/DataA,111, /DataB,222] 738 after: PutMultiWithoutTx #6, keys=[/DataA,111, /DataB,222] 739 dsmiddleware/localcache.SetMulti: len=1 740 dsmiddleware/localcache.SetMulti: idx=0 key=/DataB,222 len(ps)=1 741 before: PutMultiWithoutTx #7, keys=[/DataA,111, /DataB,222] 742 before: GetMultiWithoutTx #8, len(keys)=2, keys=[/DataA,111, /DataB,222] 743 dsmiddleware/localcache.GetMulti: len=1 744 dsmiddleware/localcache.GetMulti: idx=0 key=/DataB,222 745 dsmiddleware/localcache.GetMulti: idx=0, hit key=/DataB,222 len(ps)=1 746 after: GetMultiWithoutTx #7, len(keys)=1, keys=[/DataA,111] 747 before: DeleteMultiWithoutTx #9, len(keys)=2, keys=[/DataA,111, /DataB,222] 748 after: DeleteMultiWithoutTx #8, len(keys)=2, keys=[/DataA,111, /DataB,222] 749 dsmiddleware/localcache.DeleteMulti: len=1 750 dsmiddleware/localcache.DeleteMulti: idx=0 key=/DataB,222 751 before: PutMultiWithoutTx #10, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222] 752 after: PutMultiWithoutTx #9, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222] 753 after: PutMultiWithoutTx #9, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222] 754 dsmiddleware/localcache.SetMulti: len=2 755 dsmiddleware/localcache.SetMulti: idx=0 key=/DataA,222 len(ps)=1 756 dsmiddleware/localcache.SetMulti: idx=1 key=/DataB,222 len(ps)=1 757 before: PutMultiWithoutTx #10, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222] 758 dsmiddleware/localcache.DeleteCache: key=/DataA,111 759 dsmiddleware/localcache.DeleteCache: key=/DataA,222 760 before: GetMultiWithoutTx #11, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222] 761 dsmiddleware/localcache.GetMulti: len=2 762 dsmiddleware/localcache.GetMulti: idx=0 key=/DataA,222 763 dsmiddleware/localcache.GetMulti: idx=1 key=/DataB,222 764 dsmiddleware/localcache.GetMulti: idx=0, missed key=/DataA,222 765 dsmiddleware/localcache.GetMulti: idx=1, hit key=/DataB,222 len(ps)=1 766 after: GetMultiWithoutTx #10, len(keys)=3, keys=[/DataA,111, /DataA,222, /DataB,111] 767 dsmiddleware/localcache.SetMulti: len=1 768 dsmiddleware/localcache.SetMulti: idx=0 key=/DataA,222 len(ps)=1 769 before: DeleteMultiWithoutTx #12, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222] 770 after: DeleteMultiWithoutTx #11, len(keys)=4, keys=[/DataA,111, /DataA,222, /DataB,111, /DataB,222] 771 dsmiddleware/localcache.DeleteMulti: len=2 772 dsmiddleware/localcache.DeleteMulti: idx=0 key=/DataA,222 773 dsmiddleware/localcache.DeleteMulti: idx=1 key=/DataB,222 774 `) 775 776 if v := strings.Join(logs, "\n") + "\n"; v != expected { 777 t.Errorf("unexpected: %v", v) 778 } 779 } 780 781 func TestLocalCache_FlushLocalCache(t *testing.T) { 782 ctx, client, cleanUp := testutils.SetupCloudDatastore(t) 783 defer cleanUp() 784 785 ch := New() 786 client.AppendMiddleware(ch) 787 defer func() { 788 // stop logging before cleanUp func called. 789 client.RemoveMiddleware(ch) 790 }() 791 792 type Data struct { 793 Name string 794 } 795 796 // Put. add to cache. 797 key := client.IDKey("Data", 111, nil) 798 objBefore := &Data{Name: "Data"} 799 _, err := client.Put(ctx, key, objBefore) 800 if err != nil { 801 t.Fatal(err) 802 } 803 804 if v := ch.HasCache(key); !v { 805 t.Fatalf("unexpected: %v", v) 806 } 807 808 ch.FlushLocalCache() 809 810 if v := ch.HasCache(key); v { 811 t.Fatalf("unexpected: %v", v) 812 } 813 } 814 815 func TestLocalCache_Query(t *testing.T) { 816 ctx, client, cleanUp := testutils.SetupCloudDatastore(t) 817 defer cleanUp() 818 819 var logs []string 820 logf := func(ctx context.Context, format string, args ...interface{}) { 821 t.Logf(format, args...) 822 logs = append(logs, fmt.Sprintf(format, args...)) 823 } 824 825 // setup. strategies are first in - first apply. 826 827 bLog := dslog.NewLogger("before: ", logf) 828 client.AppendMiddleware(bLog) 829 defer func() { 830 // stop logging before cleanUp func called. 831 client.RemoveMiddleware(bLog) 832 }() 833 834 ch := New() 835 client.AppendMiddleware(ch) 836 defer func() { 837 // stop logging before cleanUp func called. 838 client.RemoveMiddleware(ch) 839 }() 840 841 aLog := dslog.NewLogger("after: ", logf) 842 client.AppendMiddleware(aLog) 843 defer func() { 844 // stop logging before cleanUp func called. 845 client.RemoveMiddleware(aLog) 846 }() 847 848 // exec. 849 850 type Data struct { 851 Name string 852 } 853 854 const size = 3 855 856 keys := make([]datastore.Key, size) 857 list := make([]*Data, size) 858 for i := 0; i < size; i++ { 859 keys[i] = client.NameKey("Data", fmt.Sprintf("#%d", i+1), nil) 860 list[i] = &Data{ 861 Name: fmt.Sprintf("#%d", i+1), 862 } 863 } 864 _, err := client.PutMulti(ctx, keys, list) 865 if err != nil { 866 t.Fatal(err) 867 } 868 869 q := client.NewQuery("Data").Order("-Name") 870 871 // Run 872 iter := client.Run(ctx, q) 873 874 // Next 875 cnt := 0 876 for { 877 obj := &Data{} 878 key, err := iter.Next(obj) 879 if err == iterator.Done { 880 break 881 } else if err != nil { 882 t.Fatal(err) 883 } 884 if v := obj.Name; v == "" || v != key.Name() { 885 t.Errorf("unexpected: %v", cnt) 886 } 887 cnt++ 888 } 889 if cnt != size { 890 t.Errorf("unexpected: %v", cnt) 891 } 892 893 // GetAll 894 list = nil 895 _, err = client.GetAll(ctx, q, &list) 896 if err != nil { 897 t.Fatal(err) 898 } 899 900 expected := heredoc.Doc(` 901 before: PutMultiWithoutTx #1, len(keys)=3, keys=[/Data,#1, /Data,#2, /Data,#3] 902 after: PutMultiWithoutTx #1, len(keys)=3, keys=[/Data,#1, /Data,#2, /Data,#3] 903 after: PutMultiWithoutTx #1, keys=[/Data,#1, /Data,#2, /Data,#3] 904 before: PutMultiWithoutTx #1, keys=[/Data,#1, /Data,#2, /Data,#3] 905 before: Run #2, q=v1:Data&or=-Name 906 after: Run #2, q=v1:Data&or=-Name 907 before: Next #3, q=v1:Data&or=-Name 908 after: Next #3, q=v1:Data&or=-Name 909 after: Next #3, key=/Data,#3 910 before: Next #3, key=/Data,#3 911 before: Next #4, q=v1:Data&or=-Name 912 after: Next #4, q=v1:Data&or=-Name 913 after: Next #4, key=/Data,#2 914 before: Next #4, key=/Data,#2 915 before: Next #5, q=v1:Data&or=-Name 916 after: Next #5, q=v1:Data&or=-Name 917 after: Next #5, key=/Data,#1 918 before: Next #5, key=/Data,#1 919 before: Next #6, q=v1:Data&or=-Name 920 after: Next #6, q=v1:Data&or=-Name 921 after: Next #6, err=no more items in iterator 922 before: Next #6, err=no more items in iterator 923 before: GetAll #7, q=v1:Data&or=-Name 924 after: GetAll #7, q=v1:Data&or=-Name 925 after: GetAll #7, len(keys)=3, keys=[/Data,#3, /Data,#2, /Data,#1] 926 before: GetAll #7, len(keys)=3, keys=[/Data,#3, /Data,#2, /Data,#1] 927 `) 928 929 if v := strings.Join(logs, "\n") + "\n"; v != expected { 930 t.Errorf("unexpected: %v", v) 931 } 932 } 933 934 func TestLocalCache_Transaction(t *testing.T) { 935 ctx, client, cleanUp := testutils.SetupCloudDatastore(t) 936 defer cleanUp() 937 938 var logs []string 939 logf := func(ctx context.Context, format string, args ...interface{}) { 940 t.Logf(format, args...) 941 logs = append(logs, fmt.Sprintf(format, args...)) 942 } 943 944 // setup. strategies are first in - first apply. 945 946 bLog := dslog.NewLogger("before: ", logf) 947 client.AppendMiddleware(bLog) 948 defer func() { 949 // stop logging before cleanUp func called. 950 client.RemoveMiddleware(bLog) 951 }() 952 953 ch := New() 954 client.AppendMiddleware(ch) 955 defer func() { 956 // stop logging before cleanUp func called. 957 client.RemoveMiddleware(ch) 958 }() 959 960 aLog := dslog.NewLogger("after: ", logf) 961 client.AppendMiddleware(aLog) 962 defer func() { 963 // stop logging before cleanUp func called. 964 client.RemoveMiddleware(aLog) 965 }() 966 967 // exec. 968 969 type Data struct { 970 Name string 971 } 972 973 key := client.NameKey("Data", "a", nil) 974 975 // put to cache 976 _, err := client.Put(ctx, key, &Data{Name: "Before"}) 977 if err != nil { 978 t.Fatal(err) 979 } 980 if v := ch.HasCache(key); !v { 981 t.Fatalf("unexpected: %v", v) 982 } 983 984 { // Rollback 985 tx, err := client.NewTransaction(ctx) 986 if err != nil { 987 t.Fatal(err) 988 } 989 990 // don't put to cache before commit 991 key2 := client.NameKey("Data", "b", nil) 992 _, err = tx.Put(key2, &Data{Name: "After"}) 993 if err != nil { 994 t.Fatal(err) 995 } 996 if v := ch.HasCache(key2); v { 997 t.Fatalf("unexpected: %v", v) 998 } 999 1000 obj := &Data{} 1001 err = tx.Get(key, obj) 1002 if err != nil { 1003 t.Fatal(err) 1004 } 1005 1006 // don't delete from cache before commit 1007 err = tx.Delete(key) 1008 if err != nil { 1009 t.Fatal(err) 1010 } 1011 if v := ch.HasCache(key); !v { 1012 t.Fatalf("unexpected: %v", v) 1013 } 1014 1015 // rollback. 1016 err = tx.Rollback() 1017 if err != nil { 1018 t.Fatal(err) 1019 } 1020 if v := ch.CacheLen(); v != 1 { 1021 t.Fatalf("unexpected: %v", v) 1022 } 1023 } 1024 1025 { // Commit 1026 tx, err := client.NewTransaction(ctx) 1027 if err != nil { 1028 t.Fatal(err) 1029 } 1030 1031 // don't put to cache before commit 1032 key2 := client.IncompleteKey("Data", nil) 1033 pKey, err := tx.Put(key2, &Data{Name: "After"}) 1034 if err != nil { 1035 t.Fatal(err) 1036 } 1037 if v := ch.CacheLen(); v != 1 { 1038 t.Fatalf("unexpected: %v", v) 1039 } 1040 1041 obj := &Data{} 1042 err = tx.Get(key, obj) 1043 if err != nil { 1044 t.Fatal(err) 1045 } 1046 1047 // don't delete from cache before commit 1048 err = tx.Delete(key) 1049 if err != nil { 1050 t.Fatal(err) 1051 } 1052 if v := ch.HasCache(key); !v { 1053 t.Fatalf("unexpected: %v", v) 1054 } 1055 1056 // commit. 1057 commit, err := tx.Commit() 1058 if err != nil { 1059 t.Fatal(err) 1060 } 1061 1062 key3 := commit.Key(pKey) 1063 if v := key3.Name(); v != key2.Name() { 1064 t.Errorf("unexpected: %v", v) 1065 } 1066 // committed, but don't put to cache in tx. 1067 if v := ch.HasCache(key3); v { 1068 t.Fatalf("unexpected: %v", v) 1069 } 1070 1071 if v := ch.CacheLen(); v != 0 { 1072 for _, keyStr := range ch.CacheKeys() { 1073 key, err := client.DecodeKey(keyStr) 1074 if err != nil { 1075 t.Fatal(err) 1076 } 1077 t.Log(key.String()) 1078 } 1079 t.Fatalf("unexpected: %v", v) 1080 } 1081 } 1082 1083 var expected *regexp.Regexp 1084 { 1085 expectedPattern := heredoc.Doc(` 1086 before: PutMultiWithoutTx #1, len(keys)=1, keys=[/Data,a] 1087 after: PutMultiWithoutTx #1, len(keys)=1, keys=[/Data,a] 1088 after: PutMultiWithoutTx #1, keys=[/Data,a] 1089 before: PutMultiWithoutTx #1, keys=[/Data,a] 1090 before: PutMultiWithTx #2, len(keys)=1, keys=[/Data,b] 1091 after: PutMultiWithTx #2, len(keys)=1, keys=[/Data,b] 1092 before: GetMultiWithTx #3, len(keys)=1, keys=[/Data,a] 1093 after: GetMultiWithTx #3, len(keys)=1, keys=[/Data,a] 1094 before: DeleteMultiWithTx #4, len(keys)=1, keys=[/Data,a] 1095 after: DeleteMultiWithTx #4, len(keys)=1, keys=[/Data,a] 1096 before: PostRollback #5 1097 after: PostRollback #5 1098 before: PutMultiWithTx #6, len(keys)=1, keys=[/Data,0] 1099 after: PutMultiWithTx #6, len(keys)=1, keys=[/Data,0] 1100 before: GetMultiWithTx #7, len(keys)=1, keys=[/Data,a] 1101 after: GetMultiWithTx #7, len(keys)=1, keys=[/Data,a] 1102 before: DeleteMultiWithTx #8, len(keys)=1, keys=[/Data,a] 1103 after: DeleteMultiWithTx #8, len(keys)=1, keys=[/Data,a] 1104 before: PostCommit #9 Put keys=[/Data,@####@] 1105 after: PostCommit #9 Put keys=[/Data,@####@] 1106 `) 1107 ss := strings.Split(expectedPattern, "@####@") 1108 var buf bytes.Buffer 1109 for idx, s := range ss { 1110 buf.WriteString(regexp.QuoteMeta(s)) 1111 if idx != (len(ss) - 1) { 1112 buf.WriteString("[0-9]+") 1113 } 1114 } 1115 expected = regexp.MustCompile(buf.String()) 1116 } 1117 1118 if v := strings.Join(logs, "\n") + "\n"; !expected.MatchString(v) { 1119 t.Errorf("unexpected: %v", v) 1120 } 1121 } 1122 1123 func TestLocalCache_MultiError(t *testing.T) { 1124 ctx, client, cleanUp := testutils.SetupCloudDatastore(t) 1125 defer cleanUp() 1126 1127 var logs []string 1128 logf := func(ctx context.Context, format string, args ...interface{}) { 1129 t.Logf(format, args...) 1130 logs = append(logs, fmt.Sprintf(format, args...)) 1131 } 1132 1133 // setup. strategies are first in - first apply. 1134 1135 bLog := dslog.NewLogger("before: ", logf) 1136 client.AppendMiddleware(bLog) 1137 defer func() { 1138 // stop logging before cleanUp func called. 1139 client.RemoveMiddleware(bLog) 1140 }() 1141 1142 ch := New( 1143 WithLogger(logf), 1144 ) 1145 client.AppendMiddleware(ch) 1146 defer func() { 1147 // stop logging before cleanUp func called. 1148 client.RemoveMiddleware(ch) 1149 }() 1150 1151 aLog := dslog.NewLogger("after: ", logf) 1152 client.AppendMiddleware(aLog) 1153 defer func() { 1154 // stop logging before cleanUp func called. 1155 client.RemoveMiddleware(aLog) 1156 }() 1157 1158 // exec. 1159 1160 type Data struct { 1161 Name string 1162 } 1163 1164 const size = 10 1165 1166 keys := make([]datastore.Key, 0, size) 1167 list := make([]*Data, 0, size) 1168 for i := 1; i <= size; i++ { 1169 keys = append(keys, client.IDKey("Data", int64(i), nil)) 1170 list = append(list, &Data{ 1171 Name: fmt.Sprintf("#%d", i), 1172 }) 1173 } 1174 1175 _, err := client.PutMulti(ctx, keys, list) 1176 if err != nil { 1177 t.Fatal(err) 1178 } 1179 1180 for _, key := range keys { 1181 if key.ID()%2 == 0 { 1182 // delete cache id=2, 4, 6, 8, 10 1183 ch.DeleteCache(ctx, key) 1184 } 1185 if key.ID()%3 == 0 { 1186 // Delete entity where out of aememcache scope 1187 // delete entity id=3, 6, 9 1188 client.RemoveMiddleware(ch) 1189 err := client.Delete(ctx, key) 1190 if err != nil { 1191 t.Fatal(err) 1192 } 1193 client.RemoveMiddleware(aLog) 1194 client.AppendMiddleware(ch) 1195 client.AppendMiddleware(aLog) 1196 } 1197 } 1198 1199 list = make([]*Data, size) 1200 err = client.GetMulti(ctx, keys, list) 1201 merr, ok := err.(datastore.MultiError) 1202 if !ok { 1203 t.Fatal(err) 1204 } 1205 1206 if v := len(merr); v != size { 1207 t.Fatalf("unexpected: %v", v) 1208 } 1209 for idx, err := range merr { 1210 key := keys[idx] 1211 if key.ID()%2 == 0 && key.ID()%3 == 0 { 1212 // not exists on memcache & datastore both 1213 if err != datastore.ErrNoSuchEntity { 1214 t.Error(err) 1215 } 1216 } else { 1217 if v := list[idx].Name; v != fmt.Sprintf("#%d", idx+1) { 1218 t.Errorf("unexpected: %v", v) 1219 } 1220 } 1221 } 1222 1223 expected := heredoc.Doc(` 1224 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] 1225 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] 1226 after: PutMultiWithoutTx #1, keys=[/Data,1, /Data,2, /Data,3, /Data,4, /Data,5, /Data,6, /Data,7, /Data,8, /Data,9, /Data,10] 1227 dsmiddleware/localcache.SetMulti: len=10 1228 dsmiddleware/localcache.SetMulti: idx=0 key=/Data,1 len(ps)=1 1229 dsmiddleware/localcache.SetMulti: idx=1 key=/Data,2 len(ps)=1 1230 dsmiddleware/localcache.SetMulti: idx=2 key=/Data,3 len(ps)=1 1231 dsmiddleware/localcache.SetMulti: idx=3 key=/Data,4 len(ps)=1 1232 dsmiddleware/localcache.SetMulti: idx=4 key=/Data,5 len(ps)=1 1233 dsmiddleware/localcache.SetMulti: idx=5 key=/Data,6 len(ps)=1 1234 dsmiddleware/localcache.SetMulti: idx=6 key=/Data,7 len(ps)=1 1235 dsmiddleware/localcache.SetMulti: idx=7 key=/Data,8 len(ps)=1 1236 dsmiddleware/localcache.SetMulti: idx=8 key=/Data,9 len(ps)=1 1237 dsmiddleware/localcache.SetMulti: idx=9 key=/Data,10 len(ps)=1 1238 before: PutMultiWithoutTx #1, keys=[/Data,1, /Data,2, /Data,3, /Data,4, /Data,5, /Data,6, /Data,7, /Data,8, /Data,9, /Data,10] 1239 dsmiddleware/localcache.DeleteCache: key=/Data,2 1240 before: DeleteMultiWithoutTx #2, len(keys)=1, keys=[/Data,3] 1241 after: DeleteMultiWithoutTx #2, len(keys)=1, keys=[/Data,3] 1242 dsmiddleware/localcache.DeleteCache: key=/Data,4 1243 dsmiddleware/localcache.DeleteCache: key=/Data,6 1244 before: DeleteMultiWithoutTx #3, len(keys)=1, keys=[/Data,6] 1245 after: DeleteMultiWithoutTx #3, len(keys)=1, keys=[/Data,6] 1246 dsmiddleware/localcache.DeleteCache: key=/Data,8 1247 before: DeleteMultiWithoutTx #4, len(keys)=1, keys=[/Data,9] 1248 after: DeleteMultiWithoutTx #4, len(keys)=1, keys=[/Data,9] 1249 dsmiddleware/localcache.DeleteCache: key=/Data,10 1250 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] 1251 dsmiddleware/localcache.GetMulti: len=10 1252 dsmiddleware/localcache.GetMulti: idx=0 key=/Data,1 1253 dsmiddleware/localcache.GetMulti: idx=1 key=/Data,2 1254 dsmiddleware/localcache.GetMulti: idx=2 key=/Data,3 1255 dsmiddleware/localcache.GetMulti: idx=3 key=/Data,4 1256 dsmiddleware/localcache.GetMulti: idx=4 key=/Data,5 1257 dsmiddleware/localcache.GetMulti: idx=5 key=/Data,6 1258 dsmiddleware/localcache.GetMulti: idx=6 key=/Data,7 1259 dsmiddleware/localcache.GetMulti: idx=7 key=/Data,8 1260 dsmiddleware/localcache.GetMulti: idx=8 key=/Data,9 1261 dsmiddleware/localcache.GetMulti: idx=9 key=/Data,10 1262 dsmiddleware/localcache.GetMulti: idx=0, hit key=/Data,1 len(ps)=1 1263 dsmiddleware/localcache.GetMulti: idx=1, missed key=/Data,2 1264 dsmiddleware/localcache.GetMulti: idx=2, hit key=/Data,3 len(ps)=1 1265 dsmiddleware/localcache.GetMulti: idx=3, missed key=/Data,4 1266 dsmiddleware/localcache.GetMulti: idx=4, hit key=/Data,5 len(ps)=1 1267 dsmiddleware/localcache.GetMulti: idx=5, missed key=/Data,6 1268 dsmiddleware/localcache.GetMulti: idx=6, hit key=/Data,7 len(ps)=1 1269 dsmiddleware/localcache.GetMulti: idx=7, missed key=/Data,8 1270 dsmiddleware/localcache.GetMulti: idx=8, hit key=/Data,9 len(ps)=1 1271 dsmiddleware/localcache.GetMulti: idx=9, missed key=/Data,10 1272 after: GetMultiWithoutTx #5, len(keys)=5, keys=[/Data,2, /Data,4, /Data,6, /Data,8, /Data,10] 1273 after: GetMultiWithoutTx #5, err=datastore: no such entity 1274 dsmiddleware/localcache.SetMulti: len=4 1275 dsmiddleware/localcache.SetMulti: idx=0 key=/Data,2 len(ps)=1 1276 dsmiddleware/localcache.SetMulti: idx=1 key=/Data,4 len(ps)=1 1277 dsmiddleware/localcache.SetMulti: idx=2 key=/Data,8 len(ps)=1 1278 dsmiddleware/localcache.SetMulti: idx=3 key=/Data,10 len(ps)=1 1279 before: GetMultiWithoutTx #5, err=datastore: no such entity 1280 `) 1281 1282 if v := strings.Join(logs, "\n") + "\n"; v != expected { 1283 t.Errorf("unexpected: %v", v) 1284 } 1285 }