go.etcd.io/etcd@v3.3.27+incompatible/mvcc/key_index_test.go (about) 1 // Copyright 2015 The etcd Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package mvcc 16 17 import ( 18 "reflect" 19 "testing" 20 ) 21 22 func TestKeyIndexGet(t *testing.T) { 23 // key: "foo" 24 // rev: 16 25 // generations: 26 // {empty} 27 // {{14, 0}[1], {14, 1}[2], {16, 0}(t)[3]} 28 // {{8, 0}[1], {10, 0}[2], {12, 0}(t)[3]} 29 // {{2, 0}[1], {4, 0}[2], {6, 0}(t)[3]} 30 ki := newTestKeyIndex() 31 ki.compact(4, make(map[revision]struct{})) 32 33 tests := []struct { 34 rev int64 35 36 wmod revision 37 wcreat revision 38 wver int64 39 werr error 40 }{ 41 {17, revision{}, revision{}, 0, ErrRevisionNotFound}, 42 {16, revision{}, revision{}, 0, ErrRevisionNotFound}, 43 44 // get on generation 3 45 {15, revision{14, 1}, revision{14, 0}, 2, nil}, 46 {14, revision{14, 1}, revision{14, 0}, 2, nil}, 47 48 {13, revision{}, revision{}, 0, ErrRevisionNotFound}, 49 {12, revision{}, revision{}, 0, ErrRevisionNotFound}, 50 51 // get on generation 2 52 {11, revision{10, 0}, revision{8, 0}, 2, nil}, 53 {10, revision{10, 0}, revision{8, 0}, 2, nil}, 54 {9, revision{8, 0}, revision{8, 0}, 1, nil}, 55 {8, revision{8, 0}, revision{8, 0}, 1, nil}, 56 57 {7, revision{}, revision{}, 0, ErrRevisionNotFound}, 58 {6, revision{}, revision{}, 0, ErrRevisionNotFound}, 59 60 // get on generation 1 61 {5, revision{4, 0}, revision{2, 0}, 2, nil}, 62 {4, revision{4, 0}, revision{2, 0}, 2, nil}, 63 64 {3, revision{}, revision{}, 0, ErrRevisionNotFound}, 65 {2, revision{}, revision{}, 0, ErrRevisionNotFound}, 66 {1, revision{}, revision{}, 0, ErrRevisionNotFound}, 67 {0, revision{}, revision{}, 0, ErrRevisionNotFound}, 68 } 69 70 for i, tt := range tests { 71 mod, creat, ver, err := ki.get(tt.rev) 72 if err != tt.werr { 73 t.Errorf("#%d: err = %v, want %v", i, err, tt.werr) 74 } 75 if mod != tt.wmod { 76 t.Errorf("#%d: modified = %+v, want %+v", i, mod, tt.wmod) 77 } 78 if creat != tt.wcreat { 79 t.Errorf("#%d: created = %+v, want %+v", i, creat, tt.wcreat) 80 } 81 if ver != tt.wver { 82 t.Errorf("#%d: version = %d, want %d", i, ver, tt.wver) 83 } 84 } 85 } 86 87 func TestKeyIndexSince(t *testing.T) { 88 ki := newTestKeyIndex() 89 ki.compact(4, make(map[revision]struct{})) 90 91 allRevs := []revision{{4, 0}, {6, 0}, {8, 0}, {10, 0}, {12, 0}, {14, 1}, {16, 0}} 92 tests := []struct { 93 rev int64 94 95 wrevs []revision 96 }{ 97 {17, nil}, 98 {16, allRevs[6:]}, 99 {15, allRevs[6:]}, 100 {14, allRevs[5:]}, 101 {13, allRevs[5:]}, 102 {12, allRevs[4:]}, 103 {11, allRevs[4:]}, 104 {10, allRevs[3:]}, 105 {9, allRevs[3:]}, 106 {8, allRevs[2:]}, 107 {7, allRevs[2:]}, 108 {6, allRevs[1:]}, 109 {5, allRevs[1:]}, 110 {4, allRevs}, 111 {3, allRevs}, 112 {2, allRevs}, 113 {1, allRevs}, 114 {0, allRevs}, 115 } 116 117 for i, tt := range tests { 118 revs := ki.since(tt.rev) 119 if !reflect.DeepEqual(revs, tt.wrevs) { 120 t.Errorf("#%d: revs = %+v, want %+v", i, revs, tt.wrevs) 121 } 122 } 123 } 124 125 func TestKeyIndexPut(t *testing.T) { 126 ki := &keyIndex{key: []byte("foo")} 127 ki.put(5, 0) 128 129 wki := &keyIndex{ 130 key: []byte("foo"), 131 modified: revision{5, 0}, 132 generations: []generation{{created: revision{5, 0}, ver: 1, revs: []revision{{main: 5}}}}, 133 } 134 if !reflect.DeepEqual(ki, wki) { 135 t.Errorf("ki = %+v, want %+v", ki, wki) 136 } 137 138 ki.put(7, 0) 139 140 wki = &keyIndex{ 141 key: []byte("foo"), 142 modified: revision{7, 0}, 143 generations: []generation{{created: revision{5, 0}, ver: 2, revs: []revision{{main: 5}, {main: 7}}}}, 144 } 145 if !reflect.DeepEqual(ki, wki) { 146 t.Errorf("ki = %+v, want %+v", ki, wki) 147 } 148 } 149 150 func TestKeyIndexRestore(t *testing.T) { 151 ki := &keyIndex{key: []byte("foo")} 152 ki.restore(revision{5, 0}, revision{7, 0}, 2) 153 154 wki := &keyIndex{ 155 key: []byte("foo"), 156 modified: revision{7, 0}, 157 generations: []generation{{created: revision{5, 0}, ver: 2, revs: []revision{{main: 7}}}}, 158 } 159 if !reflect.DeepEqual(ki, wki) { 160 t.Errorf("ki = %+v, want %+v", ki, wki) 161 } 162 } 163 164 func TestKeyIndexTombstone(t *testing.T) { 165 ki := &keyIndex{key: []byte("foo")} 166 ki.put(5, 0) 167 168 err := ki.tombstone(7, 0) 169 if err != nil { 170 t.Errorf("unexpected tombstone error: %v", err) 171 } 172 173 wki := &keyIndex{ 174 key: []byte("foo"), 175 modified: revision{7, 0}, 176 generations: []generation{{created: revision{5, 0}, ver: 2, revs: []revision{{main: 5}, {main: 7}}}, {}}, 177 } 178 if !reflect.DeepEqual(ki, wki) { 179 t.Errorf("ki = %+v, want %+v", ki, wki) 180 } 181 182 ki.put(8, 0) 183 ki.put(9, 0) 184 err = ki.tombstone(15, 0) 185 if err != nil { 186 t.Errorf("unexpected tombstone error: %v", err) 187 } 188 189 wki = &keyIndex{ 190 key: []byte("foo"), 191 modified: revision{15, 0}, 192 generations: []generation{ 193 {created: revision{5, 0}, ver: 2, revs: []revision{{main: 5}, {main: 7}}}, 194 {created: revision{8, 0}, ver: 3, revs: []revision{{main: 8}, {main: 9}, {main: 15}}}, 195 {}, 196 }, 197 } 198 if !reflect.DeepEqual(ki, wki) { 199 t.Errorf("ki = %+v, want %+v", ki, wki) 200 } 201 202 err = ki.tombstone(16, 0) 203 if err != ErrRevisionNotFound { 204 t.Errorf("tombstone error = %v, want %v", err, ErrRevisionNotFound) 205 } 206 } 207 208 func TestKeyIndexCompactAndKeep(t *testing.T) { 209 tests := []struct { 210 compact int64 211 212 wki *keyIndex 213 wam map[revision]struct{} 214 }{ 215 { 216 1, 217 &keyIndex{ 218 key: []byte("foo"), 219 modified: revision{16, 0}, 220 generations: []generation{ 221 {created: revision{2, 0}, ver: 3, revs: []revision{{main: 2}, {main: 4}, {main: 6}}}, 222 {created: revision{8, 0}, ver: 3, revs: []revision{{main: 8}, {main: 10}, {main: 12}}}, 223 {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14}, {main: 14, sub: 1}, {main: 16}}}, 224 {}, 225 }, 226 }, 227 map[revision]struct{}{}, 228 }, 229 { 230 2, 231 &keyIndex{ 232 key: []byte("foo"), 233 modified: revision{16, 0}, 234 generations: []generation{ 235 {created: revision{2, 0}, ver: 3, revs: []revision{{main: 2}, {main: 4}, {main: 6}}}, 236 {created: revision{8, 0}, ver: 3, revs: []revision{{main: 8}, {main: 10}, {main: 12}}}, 237 {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14}, {main: 14, sub: 1}, {main: 16}}}, 238 {}, 239 }, 240 }, 241 map[revision]struct{}{ 242 {main: 2}: {}, 243 }, 244 }, 245 { 246 3, 247 &keyIndex{ 248 key: []byte("foo"), 249 modified: revision{16, 0}, 250 generations: []generation{ 251 {created: revision{2, 0}, ver: 3, revs: []revision{{main: 2}, {main: 4}, {main: 6}}}, 252 {created: revision{8, 0}, ver: 3, revs: []revision{{main: 8}, {main: 10}, {main: 12}}}, 253 {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14}, {main: 14, sub: 1}, {main: 16}}}, 254 {}, 255 }, 256 }, 257 map[revision]struct{}{ 258 {main: 2}: {}, 259 }, 260 }, 261 { 262 4, 263 &keyIndex{ 264 key: []byte("foo"), 265 modified: revision{16, 0}, 266 generations: []generation{ 267 {created: revision{2, 0}, ver: 3, revs: []revision{{main: 4}, {main: 6}}}, 268 {created: revision{8, 0}, ver: 3, revs: []revision{{main: 8}, {main: 10}, {main: 12}}}, 269 {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14}, {main: 14, sub: 1}, {main: 16}}}, 270 {}, 271 }, 272 }, 273 map[revision]struct{}{ 274 {main: 4}: {}, 275 }, 276 }, 277 { 278 5, 279 &keyIndex{ 280 key: []byte("foo"), 281 modified: revision{16, 0}, 282 generations: []generation{ 283 {created: revision{2, 0}, ver: 3, revs: []revision{{main: 4}, {main: 6}}}, 284 {created: revision{8, 0}, ver: 3, revs: []revision{{main: 8}, {main: 10}, {main: 12}}}, 285 {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14}, {main: 14, sub: 1}, {main: 16}}}, 286 {}, 287 }, 288 }, 289 map[revision]struct{}{ 290 {main: 4}: {}, 291 }, 292 }, 293 { 294 6, 295 &keyIndex{ 296 key: []byte("foo"), 297 modified: revision{16, 0}, 298 generations: []generation{ 299 {created: revision{8, 0}, ver: 3, revs: []revision{{main: 8}, {main: 10}, {main: 12}}}, 300 {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14}, {main: 14, sub: 1}, {main: 16}}}, 301 {}, 302 }, 303 }, 304 map[revision]struct{}{}, 305 }, 306 { 307 7, 308 &keyIndex{ 309 key: []byte("foo"), 310 modified: revision{16, 0}, 311 generations: []generation{ 312 {created: revision{8, 0}, ver: 3, revs: []revision{{main: 8}, {main: 10}, {main: 12}}}, 313 {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14}, {main: 14, sub: 1}, {main: 16}}}, 314 {}, 315 }, 316 }, 317 map[revision]struct{}{}, 318 }, 319 { 320 8, 321 &keyIndex{ 322 key: []byte("foo"), 323 modified: revision{16, 0}, 324 generations: []generation{ 325 {created: revision{8, 0}, ver: 3, revs: []revision{{main: 8}, {main: 10}, {main: 12}}}, 326 {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14}, {main: 14, sub: 1}, {main: 16}}}, 327 {}, 328 }, 329 }, 330 map[revision]struct{}{ 331 {main: 8}: {}, 332 }, 333 }, 334 { 335 9, 336 &keyIndex{ 337 key: []byte("foo"), 338 modified: revision{16, 0}, 339 generations: []generation{ 340 {created: revision{8, 0}, ver: 3, revs: []revision{{main: 8}, {main: 10}, {main: 12}}}, 341 {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14}, {main: 14, sub: 1}, {main: 16}}}, 342 {}, 343 }, 344 }, 345 map[revision]struct{}{ 346 {main: 8}: {}, 347 }, 348 }, 349 { 350 10, 351 &keyIndex{ 352 key: []byte("foo"), 353 modified: revision{16, 0}, 354 generations: []generation{ 355 {created: revision{8, 0}, ver: 3, revs: []revision{{main: 10}, {main: 12}}}, 356 {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14}, {main: 14, sub: 1}, {main: 16}}}, 357 {}, 358 }, 359 }, 360 map[revision]struct{}{ 361 {main: 10}: {}, 362 }, 363 }, 364 { 365 11, 366 &keyIndex{ 367 key: []byte("foo"), 368 modified: revision{16, 0}, 369 generations: []generation{ 370 {created: revision{8, 0}, ver: 3, revs: []revision{{main: 10}, {main: 12}}}, 371 {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14}, {main: 14, sub: 1}, {main: 16}}}, 372 {}, 373 }, 374 }, 375 map[revision]struct{}{ 376 {main: 10}: {}, 377 }, 378 }, 379 { 380 12, 381 &keyIndex{ 382 key: []byte("foo"), 383 modified: revision{16, 0}, 384 generations: []generation{ 385 {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14}, {main: 14, sub: 1}, {main: 16}}}, 386 {}, 387 }, 388 }, 389 map[revision]struct{}{}, 390 }, 391 { 392 13, 393 &keyIndex{ 394 key: []byte("foo"), 395 modified: revision{16, 0}, 396 generations: []generation{ 397 {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14}, {main: 14, sub: 1}, {main: 16}}}, 398 {}, 399 }, 400 }, 401 map[revision]struct{}{}, 402 }, 403 { 404 14, 405 &keyIndex{ 406 key: []byte("foo"), 407 modified: revision{16, 0}, 408 generations: []generation{ 409 {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14, sub: 1}, {main: 16}}}, 410 {}, 411 }, 412 }, 413 map[revision]struct{}{ 414 {main: 14, sub: 1}: {}, 415 }, 416 }, 417 { 418 15, 419 &keyIndex{ 420 key: []byte("foo"), 421 modified: revision{16, 0}, 422 generations: []generation{ 423 {created: revision{14, 0}, ver: 3, revs: []revision{{main: 14, sub: 1}, {main: 16}}}, 424 {}, 425 }, 426 }, 427 map[revision]struct{}{ 428 {main: 14, sub: 1}: {}, 429 }, 430 }, 431 { 432 16, 433 &keyIndex{ 434 key: []byte("foo"), 435 modified: revision{16, 0}, 436 generations: []generation{ 437 {}, 438 }, 439 }, 440 map[revision]struct{}{}, 441 }, 442 } 443 444 // Continuous Compaction and finding Keep 445 ki := newTestKeyIndex() 446 for i, tt := range tests { 447 am := make(map[revision]struct{}) 448 kiclone := cloneKeyIndex(ki) 449 ki.keep(tt.compact, am) 450 if !reflect.DeepEqual(ki, kiclone) { 451 t.Errorf("#%d: ki = %+v, want %+v", i, ki, kiclone) 452 } 453 if !reflect.DeepEqual(am, tt.wam) { 454 t.Errorf("#%d: am = %+v, want %+v", i, am, tt.wam) 455 } 456 am = make(map[revision]struct{}) 457 ki.compact(tt.compact, am) 458 if !reflect.DeepEqual(ki, tt.wki) { 459 t.Errorf("#%d: ki = %+v, want %+v", i, ki, tt.wki) 460 } 461 if !reflect.DeepEqual(am, tt.wam) { 462 t.Errorf("#%d: am = %+v, want %+v", i, am, tt.wam) 463 } 464 } 465 466 // Jump Compaction and finding Keep 467 ki = newTestKeyIndex() 468 for i, tt := range tests { 469 if (i%2 == 0 && i < 6) || (i%2 == 1 && i > 6) { 470 am := make(map[revision]struct{}) 471 kiclone := cloneKeyIndex(ki) 472 ki.keep(tt.compact, am) 473 if !reflect.DeepEqual(ki, kiclone) { 474 t.Errorf("#%d: ki = %+v, want %+v", i, ki, kiclone) 475 } 476 if !reflect.DeepEqual(am, tt.wam) { 477 t.Errorf("#%d: am = %+v, want %+v", i, am, tt.wam) 478 } 479 am = make(map[revision]struct{}) 480 ki.compact(tt.compact, am) 481 if !reflect.DeepEqual(ki, tt.wki) { 482 t.Errorf("#%d: ki = %+v, want %+v", i, ki, tt.wki) 483 } 484 if !reflect.DeepEqual(am, tt.wam) { 485 t.Errorf("#%d: am = %+v, want %+v", i, am, tt.wam) 486 } 487 } 488 } 489 490 kiClone := newTestKeyIndex() 491 // Once Compaction and finding Keep 492 for i, tt := range tests { 493 ki := newTestKeyIndex() 494 am := make(map[revision]struct{}) 495 ki.keep(tt.compact, am) 496 if !reflect.DeepEqual(ki, kiClone) { 497 t.Errorf("#%d: ki = %+v, want %+v", i, ki, kiClone) 498 } 499 if !reflect.DeepEqual(am, tt.wam) { 500 t.Errorf("#%d: am = %+v, want %+v", i, am, tt.wam) 501 } 502 am = make(map[revision]struct{}) 503 ki.compact(tt.compact, am) 504 if !reflect.DeepEqual(ki, tt.wki) { 505 t.Errorf("#%d: ki = %+v, want %+v", i, ki, tt.wki) 506 } 507 if !reflect.DeepEqual(am, tt.wam) { 508 t.Errorf("#%d: am = %+v, want %+v", i, am, tt.wam) 509 } 510 } 511 } 512 513 func cloneKeyIndex(ki *keyIndex) *keyIndex { 514 generations := make([]generation, len(ki.generations)) 515 for i, gen := range ki.generations { 516 generations[i] = *cloneGeneration(&gen) 517 } 518 return &keyIndex{ki.key, ki.modified, generations} 519 } 520 521 func cloneGeneration(g *generation) *generation { 522 if g.revs == nil { 523 return &generation{g.ver, g.created, nil} 524 } 525 tmp := make([]revision, len(g.revs)) 526 copy(tmp, g.revs) 527 return &generation{g.ver, g.created, tmp} 528 } 529 530 // test that compact on version that higher than last modified version works well 531 func TestKeyIndexCompactOnFurtherRev(t *testing.T) { 532 ki := &keyIndex{key: []byte("foo")} 533 ki.put(1, 0) 534 ki.put(2, 0) 535 am := make(map[revision]struct{}) 536 ki.compact(3, am) 537 538 wki := &keyIndex{ 539 key: []byte("foo"), 540 modified: revision{2, 0}, 541 generations: []generation{ 542 {created: revision{1, 0}, ver: 2, revs: []revision{{main: 2}}}, 543 }, 544 } 545 wam := map[revision]struct{}{ 546 {main: 2}: {}, 547 } 548 if !reflect.DeepEqual(ki, wki) { 549 t.Errorf("ki = %+v, want %+v", ki, wki) 550 } 551 if !reflect.DeepEqual(am, wam) { 552 t.Errorf("am = %+v, want %+v", am, wam) 553 } 554 } 555 556 func TestKeyIndexIsEmpty(t *testing.T) { 557 tests := []struct { 558 ki *keyIndex 559 w bool 560 }{ 561 { 562 &keyIndex{ 563 key: []byte("foo"), 564 generations: []generation{{}}, 565 }, 566 true, 567 }, 568 { 569 &keyIndex{ 570 key: []byte("foo"), 571 modified: revision{2, 0}, 572 generations: []generation{ 573 {created: revision{1, 0}, ver: 2, revs: []revision{{main: 2}}}, 574 }, 575 }, 576 false, 577 }, 578 } 579 for i, tt := range tests { 580 g := tt.ki.isEmpty() 581 if g != tt.w { 582 t.Errorf("#%d: isEmpty = %v, want %v", i, g, tt.w) 583 } 584 } 585 } 586 587 func TestKeyIndexFindGeneration(t *testing.T) { 588 ki := newTestKeyIndex() 589 590 tests := []struct { 591 rev int64 592 wg *generation 593 }{ 594 {0, nil}, 595 {1, nil}, 596 {2, &ki.generations[0]}, 597 {3, &ki.generations[0]}, 598 {4, &ki.generations[0]}, 599 {5, &ki.generations[0]}, 600 {6, nil}, 601 {7, nil}, 602 {8, &ki.generations[1]}, 603 {9, &ki.generations[1]}, 604 {10, &ki.generations[1]}, 605 {11, &ki.generations[1]}, 606 {12, nil}, 607 {13, nil}, 608 } 609 for i, tt := range tests { 610 g := ki.findGeneration(tt.rev) 611 if g != tt.wg { 612 t.Errorf("#%d: generation = %+v, want %+v", i, g, tt.wg) 613 } 614 } 615 } 616 617 func TestKeyIndexLess(t *testing.T) { 618 ki := &keyIndex{key: []byte("foo")} 619 620 tests := []struct { 621 ki *keyIndex 622 w bool 623 }{ 624 {&keyIndex{key: []byte("doo")}, false}, 625 {&keyIndex{key: []byte("foo")}, false}, 626 {&keyIndex{key: []byte("goo")}, true}, 627 } 628 for i, tt := range tests { 629 g := ki.Less(tt.ki) 630 if g != tt.w { 631 t.Errorf("#%d: Less = %v, want %v", i, g, tt.w) 632 } 633 } 634 } 635 636 func TestGenerationIsEmpty(t *testing.T) { 637 tests := []struct { 638 g *generation 639 w bool 640 }{ 641 {nil, true}, 642 {&generation{}, true}, 643 {&generation{revs: []revision{{main: 1}}}, false}, 644 } 645 for i, tt := range tests { 646 g := tt.g.isEmpty() 647 if g != tt.w { 648 t.Errorf("#%d: isEmpty = %v, want %v", i, g, tt.w) 649 } 650 } 651 } 652 653 func TestGenerationWalk(t *testing.T) { 654 g := &generation{ 655 ver: 3, 656 created: revision{2, 0}, 657 revs: []revision{{main: 2}, {main: 4}, {main: 6}}, 658 } 659 tests := []struct { 660 f func(rev revision) bool 661 wi int 662 }{ 663 {func(rev revision) bool { return rev.main >= 7 }, 2}, 664 {func(rev revision) bool { return rev.main >= 6 }, 1}, 665 {func(rev revision) bool { return rev.main >= 5 }, 1}, 666 {func(rev revision) bool { return rev.main >= 4 }, 0}, 667 {func(rev revision) bool { return rev.main >= 3 }, 0}, 668 {func(rev revision) bool { return rev.main >= 2 }, -1}, 669 } 670 for i, tt := range tests { 671 idx := g.walk(tt.f) 672 if idx != tt.wi { 673 t.Errorf("#%d: index = %d, want %d", i, idx, tt.wi) 674 } 675 } 676 } 677 678 func newTestKeyIndex() *keyIndex { 679 // key: "foo" 680 // rev: 16 681 // generations: 682 // {empty} 683 // {{14, 0}[1], {14, 1}[2], {16, 0}(t)[3]} 684 // {{8, 0}[1], {10, 0}[2], {12, 0}(t)[3]} 685 // {{2, 0}[1], {4, 0}[2], {6, 0}(t)[3]} 686 687 ki := &keyIndex{key: []byte("foo")} 688 ki.put(2, 0) 689 ki.put(4, 0) 690 ki.tombstone(6, 0) 691 ki.put(8, 0) 692 ki.put(10, 0) 693 ki.tombstone(12, 0) 694 ki.put(14, 0) 695 ki.put(14, 1) 696 ki.tombstone(16, 0) 697 return ki 698 }