// TestReplicaDataIterator creates three ranges {"a"-"b" (pre), "b"-"c" // (main test range), "c"-"d" (post)} and fills each with data. It // first verifies the contents of the "b"-"c" range, then deletes it // and verifies it's empty. Finally, it verifies the pre and post // ranges still contain the expected data. func TestReplicaDataIterator(t *testing.T) { defer leaktest.AfterTest(t) tc := testContext{ bootstrapMode: bootstrapRangeOnly, } tc.Start(t) defer tc.Stop() // See notes in EmptyRange test method for adjustment to descriptor. newDesc := *tc.rng.Desc() newDesc.StartKey = roachpb.RKey("b") newDesc.EndKey = roachpb.RKey("c") if err := tc.rng.setDesc(&newDesc); err != nil { t.Fatal(err) } // Create two more ranges, one before the test range and one after. preRng := createRange(tc.store, 2, roachpb.RKeyMin, roachpb.RKey("b")) if err := tc.store.AddReplicaTest(preRng); err != nil { t.Fatal(err) } postRng := createRange(tc.store, 3, roachpb.RKey("c"), roachpb.RKeyMax) if err := tc.store.AddReplicaTest(postRng); err != nil { t.Fatal(err) } // Create range data for all three ranges. preKeys := createRangeData(preRng, t) curKeys := createRangeData(tc.rng, t) postKeys := createRangeData(postRng, t) iter := newReplicaDataIterator(tc.rng.Desc(), tc.rng.store.Engine()) defer iter.Close() i := 0 for ; iter.Valid(); iter.Next() { if err := iter.Error(); err != nil { t.Fatal(err) } if i >= len(curKeys) { t.Fatal("there are more keys in the iteration than expected") } if key := iter.Key(); !key.Equal(curKeys[i]) { k1, ts1, _, err := engine.MVCCDecodeKey(key) if err != nil { t.Fatal(err) } k2, ts2, _, err := engine.MVCCDecodeKey(curKeys[i]) if err != nil { t.Fatal(err) } t.Errorf("%d: expected %q(%d); got %q(%d)", i, k2, ts2, k1, ts1) } i++ } if i != len(curKeys) { t.Fatal("there are fewer keys in the iteration than expected") } // Destroy range and verify that its data has been completely cleared. if err := tc.rng.Destroy(); err != nil { t.Fatal(err) } iter = newReplicaDataIterator(tc.rng.Desc(), tc.rng.store.Engine()) defer iter.Close() if iter.Valid() { // If the range is destroyed, only a tombstone key should be there. k1, _, _, err := engine.MVCCDecodeKey(iter.Key()) if err != nil { t.Fatal(err) } if tombstoneKey := keys.RaftTombstoneKey(tc.rng.Desc().RangeID); !bytes.Equal(k1, tombstoneKey) { t.Errorf("expected a tombstone key %q, but found %q", tombstoneKey, k1) } if iter.Next(); iter.Valid() { t.Errorf("expected a destroyed replica to have only a tombstone key, but found more") } } else { t.Errorf("expected a tombstone key, but got an empty iteration") } // Verify the keys in pre & post ranges. for _, test := range []struct { r *Replica keys []roachpb.EncodedKey }{ {preRng, preKeys}, {postRng, postKeys}, } { iter = newReplicaDataIterator(test.r.Desc(), test.r.store.Engine()) defer iter.Close() i = 0 for ; iter.Valid(); iter.Next() { k1, ts1, _, err := engine.MVCCDecodeKey(iter.Key()) if err != nil { t.Fatal(err) } if bytes.HasPrefix(k1, keys.StatusPrefix) { // Some data is written into the system prefix by Store.BootstrapRange, // but it is not in our expected key list so skip it. // TODO(bdarnell): validate this data instead of skipping it. continue } if key := iter.Key(); !key.Equal(test.keys[i]) { k2, ts2, _, err := engine.MVCCDecodeKey(test.keys[i]) if err != nil { t.Fatal(err) } t.Errorf("%d: key mismatch %q(%d) != %q(%d)", i, k1, ts1, k2, ts2) } i++ } if i != len(curKeys) { t.Fatal("there are fewer keys in the iteration than expected") } } }
// TestReplicaDataIterator creates three ranges {"a"-"b" (pre), "b"-"c" // (main test range), "c"-"d" (post)} and fills each with data. It // first verifies the contents of the "b"-"c" range. Next, it makes sure // a replicated-only iterator does not show any unreplicated keys from // the range. Then, it deletes the range and verifies it's empty. Finally, // it verifies the pre and post ranges still contain the expected data. func TestReplicaDataIterator(t *testing.T) { defer leaktest.AfterTest(t)() ctx := TestStoreContext() // Disable Raft processing for this test as it mucks with low-level details // of replica storage in an unsafe way. ctx.TestingKnobs.DisableProcessRaft = true tc := testContext{ bootstrapMode: bootstrapRangeOnly, } tc.StartWithStoreContext(t, ctx) defer tc.Stop() // See notes in EmptyRange test method for adjustment to descriptor. newDesc := *tc.rng.Desc() newDesc.StartKey = roachpb.RKey("b") newDesc.EndKey = roachpb.RKey("c") if err := tc.rng.setDesc(&newDesc); err != nil { t.Fatal(err) } // Create two more ranges, one before the test range and one after. preRng := createReplica(tc.store, 2, roachpb.RKeyMin, roachpb.RKey("b")) if err := tc.store.AddReplicaTest(preRng); err != nil { t.Fatal(err) } postRng := createReplica(tc.store, 3, roachpb.RKey("c"), roachpb.RKeyMax) if err := tc.store.AddReplicaTest(postRng); err != nil { t.Fatal(err) } // Create range data for all three ranges. preKeys := createRangeData(t, preRng) curKeys := createRangeData(t, tc.rng) postKeys := createRangeData(t, postRng) // Verify the contents of the "b"-"c" range. iter := NewReplicaDataIterator(tc.rng.Desc(), tc.rng.store.Engine(), false /* !replicatedOnly */) defer iter.Close() i := 0 for ; iter.Valid(); iter.Next() { if err := iter.Error(); err != nil { t.Fatal(err) } if i >= len(curKeys) { t.Fatal("there are more keys in the iteration than expected") } if key := iter.Key(); !key.Equal(curKeys[i]) { k1, ts1 := key.Key, key.Timestamp k2, ts2 := curKeys[i].Key, curKeys[i].Timestamp t.Errorf("%d: expected %q(%d); got %q(%d)", i, k2, ts2, k1, ts1) } i++ } if i != len(curKeys) { t.Fatal("there are fewer keys in the iteration than expected") } // Verify that the replicated-only iterator ignores unreplicated keys. unreplicatedPrefix := keys.MakeRangeIDUnreplicatedPrefix(tc.rng.RangeID) iter = NewReplicaDataIterator(tc.rng.Desc(), tc.rng.store.Engine(), true /* replicatedOnly */) defer iter.Close() for ; iter.Valid(); iter.Next() { if err := iter.Error(); err != nil { t.Fatal(err) } if bytes.HasPrefix(iter.Key().Key, unreplicatedPrefix) { t.Fatalf("unexpected unreplicated key: %s", iter.Key().Key) } } // Destroy range and verify that its data has been completely cleared. if err := tc.rng.Destroy(*tc.rng.Desc()); err != nil { t.Fatal(err) } iter = NewReplicaDataIterator(tc.rng.Desc(), tc.rng.store.Engine(), false /* !replicatedOnly */) defer iter.Close() if iter.Valid() { // If the range is destroyed, only a tombstone key should be there. k1 := iter.Key().Key if tombstoneKey := keys.RaftTombstoneKey(tc.rng.RangeID); !bytes.Equal(k1, tombstoneKey) { t.Errorf("expected a tombstone key %q, but found %q", tombstoneKey, k1) } if iter.Next(); iter.Valid() { t.Errorf("expected a destroyed replica to have only a tombstone key, but found more") } } else { t.Errorf("expected a tombstone key, but got an empty iteration") } // Verify the keys in pre & post ranges. for j, test := range []struct { r *Replica keys []engine.MVCCKey }{ {preRng, preKeys}, {postRng, postKeys}, } { iter = NewReplicaDataIterator(test.r.Desc(), test.r.store.Engine(), false /* !replicatedOnly */) defer iter.Close() i = 0 for ; iter.Valid(); iter.Next() { k1, ts1 := iter.Key().Key, iter.Key().Timestamp if bytes.HasPrefix(k1, keys.StatusPrefix) { // Some data is written into the system prefix by Store.BootstrapRange, // but it is not in our expected key list so skip it. // TODO(bdarnell): validate this data instead of skipping it. continue } if key := iter.Key(); !key.Equal(test.keys[i]) { k2, ts2 := test.keys[i].Key, test.keys[i].Timestamp t.Errorf("%d/%d: key mismatch %q(%d) != %q(%d) [%x]", j, i, k1, ts1, k2, ts2, []byte(k2)) } i++ } if i != len(curKeys) { t.Fatal("there are fewer keys in the iteration than expected") } } }