// loadAppliedIndex returns the Raft applied index and the lease applied index. func loadAppliedIndex( ctx context.Context, reader engine.Reader, rangeID roachpb.RangeID, ) (uint64, uint64, error) { var appliedIndex uint64 v, _, err := engine.MVCCGet(ctx, reader, keys.RaftAppliedIndexKey(rangeID), hlc.ZeroTimestamp, true, nil) if err != nil { return 0, 0, err } if v != nil { int64AppliedIndex, err := v.GetInt() if err != nil { return 0, 0, err } appliedIndex = uint64(int64AppliedIndex) } // TODO(tschottdorf): code duplication. var leaseAppliedIndex uint64 v, _, err = engine.MVCCGet(ctx, reader, keys.LeaseAppliedIndexKey(rangeID), hlc.ZeroTimestamp, true, nil) if err != nil { return 0, 0, err } if v != nil { int64LeaseAppliedIndex, err := v.GetInt() if err != nil { return 0, 0, err } leaseAppliedIndex = uint64(int64LeaseAppliedIndex) } return appliedIndex, leaseAppliedIndex, nil }
func loadLastIndex( ctx context.Context, reader engine.Reader, rangeID roachpb.RangeID, ) (uint64, error) { var lastIndex uint64 v, _, err := engine.MVCCGet(ctx, reader, keys.RaftLastIndexKey(rangeID), hlc.ZeroTimestamp, true /* consistent */, nil) if err != nil { return 0, err } if v != nil { int64LastIndex, err := v.GetInt() if err != nil { return 0, err } lastIndex = uint64(int64LastIndex) } else { // The log is empty, which means we are either starting from scratch // or the entire log has been truncated away. lastEnt, err := loadTruncatedState(ctx, reader, rangeID) if err != nil { return 0, err } lastIndex = lastEnt.Index } return lastIndex, nil }
// TestRangeCommandClockUpdate verifies that followers update their // clocks when executing a command, even if the lease holder's clock is far // in the future. func TestRangeCommandClockUpdate(t *testing.T) { defer leaktest.AfterTest(t)() const numNodes = 3 var manuals []*hlc.ManualClock var clocks []*hlc.Clock for i := 0; i < numNodes; i++ { manuals = append(manuals, hlc.NewManualClock(1)) clocks = append(clocks, hlc.NewClock(manuals[i].UnixNano)) clocks[i].SetMaxOffset(100 * time.Millisecond) } mtc := &multiTestContext{clocks: clocks} mtc.Start(t, numNodes) defer mtc.Stop() mtc.replicateRange(1, 1, 2) // Advance the lease holder's clock ahead of the followers (by more than // MaxOffset but less than the range lease) and execute a command. manuals[0].Increment(int64(500 * time.Millisecond)) incArgs := incrementArgs([]byte("a"), 5) ts := clocks[0].Now() if _, err := client.SendWrappedWith(context.Background(), rg1(mtc.stores[0]), roachpb.Header{Timestamp: ts}, &incArgs); err != nil { t.Fatal(err) } // Wait for that command to execute on all the followers. util.SucceedsSoon(t, func() error { values := []int64{} for _, eng := range mtc.engines { val, _, err := engine.MVCCGet(context.Background(), eng, roachpb.Key("a"), clocks[0].Now(), true, nil) if err != nil { return err } values = append(values, mustGetInt(val)) } if !reflect.DeepEqual(values, []int64{5, 5, 5}) { return errors.Errorf("expected (5, 5, 5), got %v", values) } return nil }) // Verify that all the followers have accepted the clock update from // node 0 even though it comes from outside the usual max offset. now := clocks[0].Now() for i, clock := range clocks { // Only compare the WallTimes: it's normal for clock 0 to be a few logical ticks ahead. if clock.Now().WallTime < now.WallTime { t.Errorf("clock %d is behind clock 0: %s vs %s", i, clock.Now(), now) } } }
// TestRejectFutureCommand verifies that lease holders reject commands that // would cause a large time jump. func TestRejectFutureCommand(t *testing.T) { defer leaktest.AfterTest(t)() manual := hlc.NewManualClock(123) clock := hlc.NewClock(manual.UnixNano, 100*time.Millisecond) mtc := &multiTestContext{clock: clock} mtc.Start(t, 1) defer mtc.Stop() ts1 := clock.Now() key := roachpb.Key("a") incArgs := incrementArgs(key, 5) // Commands with a future timestamp that is within the MaxOffset // bound will be accepted and will cause the clock to advance. const numCmds = 3 clockOffset := clock.MaxOffset() / numCmds for i := int64(1); i <= numCmds; i++ { ts := ts1.Add(i*clockOffset.Nanoseconds(), 0) if _, err := client.SendWrappedWith(context.Background(), rg1(mtc.stores[0]), roachpb.Header{Timestamp: ts}, &incArgs); err != nil { t.Fatal(err) } } ts2 := clock.Now() if expAdvance, advance := ts2.GoTime().Sub(ts1.GoTime()), numCmds*clockOffset; advance != expAdvance { t.Fatalf("expected clock to advance %s; got %s", expAdvance, advance) } // Once the accumulated offset reaches MaxOffset, commands will be rejected. _, pErr := client.SendWrappedWith(context.Background(), rg1(mtc.stores[0]), roachpb.Header{Timestamp: ts1.Add(clock.MaxOffset().Nanoseconds()+1, 0)}, &incArgs) if !testutils.IsPError(pErr, "rejecting command with timestamp in the future") { t.Fatalf("unexpected error %v", pErr) } // The clock did not advance and the final command was not executed. ts3 := clock.Now() if advance := ts3.GoTime().Sub(ts2.GoTime()); advance != 0 { t.Fatalf("expected clock not to advance, but it advanced by %s", advance) } val, _, err := engine.MVCCGet(context.Background(), mtc.engines[0], key, ts3, true, nil) if err != nil { t.Fatal(err) } if a, e := mustGetInt(val), incArgs.Increment*numCmds; a != e { t.Errorf("expected %d, got %d", e, a) } }
// TestRejectFutureCommand verifies that lease holders reject commands that // would cause a large time jump. func TestRejectFutureCommand(t *testing.T) { defer leaktest.AfterTest(t)() const maxOffset = 100 * time.Millisecond manual := hlc.NewManualClock(0) clock := hlc.NewClock(manual.UnixNano) clock.SetMaxOffset(maxOffset) mtc := &multiTestContext{clock: clock} mtc.Start(t, 1) defer mtc.Stop() startTime := manual.UnixNano() // Commands with a future timestamp that is within the MaxOffset // bound will be accepted and will cause the clock to advance. for i := int64(0); i < 3; i++ { incArgs := incrementArgs([]byte("a"), 5) ts := hlc.ZeroTimestamp.Add(startTime+((i+1)*30)*int64(time.Millisecond), 0) if _, err := client.SendWrappedWith(context.Background(), rg1(mtc.stores[0]), roachpb.Header{Timestamp: ts}, &incArgs); err != nil { t.Fatal(err) } } if now := clock.Now(); now.WallTime != int64(90*time.Millisecond) { t.Fatalf("expected clock to advance to 90ms; got %s", now) } // Once the accumulated offset reaches MaxOffset, commands will be rejected. incArgs := incrementArgs([]byte("a"), 11) ts := hlc.ZeroTimestamp.Add(int64((time.Duration(startTime)+maxOffset+1)*time.Millisecond), 0) if _, err := client.SendWrappedWith(context.Background(), rg1(mtc.stores[0]), roachpb.Header{Timestamp: ts}, &incArgs); err == nil { t.Fatalf("expected clock offset error but got nil") } // The clock remained at 90ms and the final command was not executed. if now := clock.Now(); now.WallTime != int64(90*time.Millisecond) { t.Errorf("expected clock to stay at 90ms; got %s", now) } val, _, err := engine.MVCCGet(context.Background(), mtc.engines[0], roachpb.Key("a"), clock.Now(), true, nil) if err != nil { t.Fatal(err) } if v := mustGetInt(val); v != 15 { t.Errorf("expected 15, got %v", v) } }
func loadFrozenStatus( ctx context.Context, reader engine.Reader, rangeID roachpb.RangeID, ) (storagebase.ReplicaState_FrozenEnum, error) { var zero storagebase.ReplicaState_FrozenEnum val, _, err := engine.MVCCGet(ctx, reader, keys.RangeFrozenStatusKey(rangeID), hlc.ZeroTimestamp, true, nil) if err != nil { return zero, err } if val == nil { return storagebase.ReplicaState_UNFROZEN, nil } if frozen, err := val.GetBool(); err != nil { return zero, err } else if frozen { return storagebase.ReplicaState_FROZEN, nil } return storagebase.ReplicaState_UNFROZEN, nil }
// TestStoreRangeMergeWithData attempts to merge two collocate ranges // each containing data. func TestStoreRangeMergeWithData(t *testing.T) { defer leaktest.AfterTest(t)() storeCfg := storage.TestStoreConfig(nil) storeCfg.TestingKnobs.DisableSplitQueue = true store, stopper := createTestStoreWithConfig(t, storeCfg) defer stopper.Stop() content := roachpb.Key("testing!") aDesc, bDesc, err := createSplitRanges(store) if err != nil { t.Fatal(err) } // Write some values left and right of the proposed split key. pArgs := putArgs([]byte("aaa"), content) if _, err := client.SendWrapped(context.Background(), rg1(store), &pArgs); err != nil { t.Fatal(err) } pArgs = putArgs([]byte("ccc"), content) if _, err := client.SendWrappedWith(context.Background(), rg1(store), roachpb.Header{ RangeID: bDesc.RangeID, }, &pArgs); err != nil { t.Fatal(err) } // Confirm the values are there. gArgs := getArgs([]byte("aaa")) if reply, err := client.SendWrapped(context.Background(), rg1(store), &gArgs); err != nil { t.Fatal(err) } else if replyBytes, err := reply.(*roachpb.GetResponse).Value.GetBytes(); err != nil { t.Fatal(err) } else if !bytes.Equal(replyBytes, content) { t.Fatalf("actual value %q did not match expected value %q", replyBytes, content) } gArgs = getArgs([]byte("ccc")) if reply, err := client.SendWrappedWith(context.Background(), rg1(store), roachpb.Header{ RangeID: bDesc.RangeID, }, &gArgs); err != nil { t.Fatal(err) } else if replyBytes, err := reply.(*roachpb.GetResponse).Value.GetBytes(); err != nil { t.Fatal(err) } else if !bytes.Equal(replyBytes, content) { t.Fatalf("actual value %q did not match expected value %q", replyBytes, content) } // Merge the b range back into the a range. args := adminMergeArgs(roachpb.KeyMin) if _, err := client.SendWrapped(context.Background(), rg1(store), &args); err != nil { t.Fatal(err) } // Verify no intents remains on range descriptor keys. for _, key := range []roachpb.Key{keys.RangeDescriptorKey(aDesc.StartKey), keys.RangeDescriptorKey(bDesc.StartKey)} { if _, _, err := engine.MVCCGet(context.Background(), store.Engine(), key, store.Clock().Now(), true, nil); err != nil { t.Fatal(err) } } // Verify the merge by looking up keys from both ranges. rangeA := store.LookupReplica([]byte("a"), nil) rangeB := store.LookupReplica([]byte("c"), nil) rangeADesc := rangeA.Desc() rangeBDesc := rangeB.Desc() if !reflect.DeepEqual(rangeA, rangeB) { t.Fatalf("ranges were not merged %+v=%+v", rangeADesc, rangeBDesc) } if !bytes.Equal(rangeADesc.StartKey, roachpb.RKeyMin) { t.Fatalf("The start key is not equal to KeyMin %q=%q", rangeADesc.StartKey, roachpb.RKeyMin) } if !bytes.Equal(rangeADesc.EndKey, roachpb.RKeyMax) { t.Fatalf("The end key is not equal to KeyMax %q=%q", rangeADesc.EndKey, roachpb.RKeyMax) } // Try to get values from after the merge. gArgs = getArgs([]byte("aaa")) if reply, err := client.SendWrapped(context.Background(), rg1(store), &gArgs); err != nil { t.Fatal(err) } else if replyBytes, err := reply.(*roachpb.GetResponse).Value.GetBytes(); err != nil { t.Fatal(err) } else if !bytes.Equal(replyBytes, content) { t.Fatalf("actual value %q did not match expected value %q", replyBytes, content) } gArgs = getArgs([]byte("ccc")) if reply, err := client.SendWrappedWith(context.Background(), rg1(store), roachpb.Header{ RangeID: rangeB.RangeID, }, &gArgs); err != nil { t.Fatal(err) } else if replyBytes, err := reply.(*roachpb.GetResponse).Value.GetBytes(); err != nil { t.Fatal(err) } else if !bytes.Equal(replyBytes, content) { t.Fatalf("actual value %q did not match expected value %q", replyBytes, content) } // Put new values after the merge on both sides. pArgs = putArgs([]byte("aaaa"), content) if _, err := client.SendWrapped(context.Background(), rg1(store), &pArgs); err != nil { t.Fatal(err) } pArgs = putArgs([]byte("cccc"), content) if _, err := client.SendWrappedWith(context.Background(), rg1(store), roachpb.Header{ RangeID: rangeB.RangeID, }, &pArgs); err != nil { t.Fatal(err) } // Try to get the newly placed values. gArgs = getArgs([]byte("aaaa")) if reply, err := client.SendWrapped(context.Background(), rg1(store), &gArgs); err != nil { t.Fatal(err) } else if replyBytes, err := reply.(*roachpb.GetResponse).Value.GetBytes(); err != nil { t.Fatal(err) } else if !bytes.Equal(replyBytes, content) { t.Fatalf("actual value %q did not match expected value %q", replyBytes, content) } gArgs = getArgs([]byte("cccc")) if reply, err := client.SendWrapped(context.Background(), rg1(store), &gArgs); err != nil { t.Fatal(err) } else if replyBytes, err := reply.(*roachpb.GetResponse).Value.GetBytes(); err != nil { t.Fatal(err) } else if !bytes.Equal(replyBytes, content) { t.Fatalf("actual value %q did not match expected value %q", replyBytes, content) } }