// TestTxnCoordSenderErrorWithIntent validates that if a transactional request // returns an error but also indicates a Writing transaction, the coordinator // tracks it just like a successful request. func TestTxnCoordSenderErrorWithIntent(t *testing.T) { defer leaktest.AfterTest(t)() stopper := stop.NewStopper() defer stopper.Stop() manual := hlc.NewManualClock(0) clock := hlc.NewClock(manual.UnixNano) clock.SetMaxOffset(20) testCases := []struct { roachpb.Error errMsg string }{ {*roachpb.NewError(roachpb.NewTransactionRetryError()), "retry txn"}, {*roachpb.NewError(roachpb.NewTransactionPushError(roachpb.Transaction{ TxnMeta: enginepb.TxnMeta{ ID: uuid.NewV4(), }})), "failed to push"}, {*roachpb.NewErrorf("testError"), "testError"}, } for i, test := range testCases { func() { senderFunc := func(_ context.Context, ba roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error) { txn := ba.Txn.Clone() txn.Writing = true pErr := &roachpb.Error{} *pErr = test.Error pErr.SetTxn(&txn) return nil, pErr } ambient := log.AmbientContext{Tracer: tracing.NewTracer()} ts := NewTxnCoordSender( ambient, senderFn(senderFunc), clock, false, stopper, MakeTxnMetrics(metric.TestSampleInterval), ) var ba roachpb.BatchRequest key := roachpb.Key("test") ba.Add(&roachpb.BeginTransactionRequest{Span: roachpb.Span{Key: key}}) ba.Add(&roachpb.PutRequest{Span: roachpb.Span{Key: key}}) ba.Add(&roachpb.EndTransactionRequest{}) ba.Txn = &roachpb.Transaction{Name: "test"} _, pErr := ts.Send(context.Background(), ba) if !testutils.IsPError(pErr, test.errMsg) { t.Errorf("%d: error did not match %s: %v", i, test.errMsg, pErr) } defer teardownHeartbeats(ts) ts.Lock() defer ts.Unlock() if len(ts.txns) != 1 { t.Errorf("%d: expected transaction to be tracked", i) } }() } }
// TestStoreRangeMergeLastRange verifies that merging the last range // fails. func TestStoreRangeMergeLastRange(t *testing.T) { defer leaktest.AfterTest(t)() storeCfg := storage.TestStoreConfig(nil) storeCfg.TestingKnobs.DisableSplitQueue = true store, stopper := createTestStoreWithConfig(t, storeCfg) defer stopper.Stop() // Merge last range. args := adminMergeArgs(roachpb.KeyMin) if _, pErr := client.SendWrapped(context.Background(), rg1(store), &args); !testutils.IsPError(pErr, "cannot merge final range") { t.Fatalf("expected 'cannot merge final range' error; got %s", pErr) } }
// TestPushTransactionsWithNonPendingIntent verifies that maybePushTransactions // returns an error when a non-pending intent is passed. func TestPushTransactionsWithNonPendingIntent(t *testing.T) { defer leaktest.AfterTest(t)() tc := testContext{} tc.Start(t) defer tc.Stop() intents := []roachpb.Intent{{Span: roachpb.Span{Key: roachpb.Key("a")}, Status: roachpb.ABORTED}} if _, pErr := tc.store.intentResolver.maybePushTransactions( context.Background(), intents, roachpb.Header{}, roachpb.PUSH_TOUCH, true); !testutils.IsPError(pErr, "unexpected aborted/resolved intent") { t.Errorf("expected error on aborted/resolved intent, but got %s", pErr) } }
// 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) } }
// TestStoreRangeMergeNonCollocated attempts to merge two ranges // that are not on the same stores. func TestStoreRangeMergeNonCollocated(t *testing.T) { defer leaktest.AfterTest(t)() mtc := startMultiTestContext(t, 4) defer mtc.Stop() store := mtc.stores[0] // Split into 3 ranges argsSplit := adminSplitArgs(roachpb.KeyMin, []byte("d")) if _, pErr := client.SendWrapped(context.Background(), rg1(store), &argsSplit); pErr != nil { t.Fatalf("Can't split range %s", pErr) } argsSplit = adminSplitArgs(roachpb.KeyMin, []byte("b")) if _, pErr := client.SendWrapped(context.Background(), rg1(store), &argsSplit); pErr != nil { t.Fatalf("Can't split range %s", pErr) } rangeA := store.LookupReplica([]byte("a"), nil) rangeADesc := rangeA.Desc() rangeB := store.LookupReplica([]byte("c"), nil) rangeBDesc := rangeB.Desc() rangeC := store.LookupReplica([]byte("e"), nil) rangeCDesc := rangeC.Desc() if bytes.Equal(rangeADesc.StartKey, rangeBDesc.StartKey) { log.Errorf(context.TODO(), "split ranges keys are equal %q!=%q", rangeADesc.StartKey, rangeBDesc.StartKey) } if bytes.Equal(rangeBDesc.StartKey, rangeCDesc.StartKey) { log.Errorf(context.TODO(), "split ranges keys are equal %q!=%q", rangeBDesc.StartKey, rangeCDesc.StartKey) } if bytes.Equal(rangeADesc.StartKey, rangeCDesc.StartKey) { log.Errorf(context.TODO(), "split ranges keys are equal %q!=%q", rangeADesc.StartKey, rangeCDesc.StartKey) } // Replicate the ranges to different sets of stores. Ranges A and C // are collocated, but B is different. mtc.replicateRange(rangeA.RangeID, 1, 2) mtc.replicateRange(rangeB.RangeID, 1, 3) mtc.replicateRange(rangeC.RangeID, 1, 2) // Attempt to merge. rangeADesc = rangeA.Desc() argsMerge := adminMergeArgs(roachpb.Key(rangeADesc.StartKey)) if _, pErr := rangeA.AdminMerge(context.Background(), argsMerge, rangeADesc); !testutils.IsPError(pErr, "ranges not collocated") { t.Fatalf("did not got expected error; got %s", pErr) } }
// Test that an error encountered by a read-only "NonKV" command is not // swallowed, and doesn't otherwise cause a panic. // We had a bug cause by the fact that errors for these commands aren't passed // through the epilogue returned by replica.beginCommands() and were getting // swallowed. func TestErrorHandlingForNonKVCommand(t *testing.T) { defer leaktest.AfterTest(t)() cmdFilter := func(fArgs storagebase.FilterArgs) *roachpb.Error { if fArgs.Hdr.UserPriority == 42 { return roachpb.NewErrorf("injected error") } return nil } srv, _, _ := serverutils.StartServer(t, base.TestServerArgs{ Knobs: base.TestingKnobs{ Store: &storage.StoreTestingKnobs{ TestingCommandFilter: cmdFilter, }, }, }) s := srv.(*server.TestServer) defer s.Stopper().Stop() // Send the lease request. key := roachpb.Key("a") leaseReq := roachpb.LeaseInfoRequest{ Span: roachpb.Span{ Key: key, }, } _, pErr := client.SendWrappedWith( context.Background(), s.DistSender(), roachpb.Header{UserPriority: 42}, &leaseReq, ) if !testutils.IsPError(pErr, "injected error") { t.Fatalf("expected error %q, got: %s", "injected error", pErr) } }
// TestRequestToUninitializedRange tests the behavior when a request // is sent to a node which should be a replica of the correct range // but has not yet received its initial snapshot. This would // previously panic due to a malformed error response from the server, // as seen in https://github.com/cockroachdb/cockroach/issues/6027. // // Prior to the other changes in the commit that introduced it, this // test would reliable trigger the panic from #6027. However, it // relies on some hacky tricks to both trigger the panic and shut down // cleanly. If this test needs a lot of maintenance in the future we // should be willing to get rid of it. func TestRequestToUninitializedRange(t *testing.T) { defer leaktest.AfterTest(t)() srv, _, _ := serverutils.StartServer(t, base.TestServerArgs{ StoreSpecs: []base.StoreSpec{ base.DefaultTestStoreSpec, base.DefaultTestStoreSpec, }, }) defer srv.Stopper().Stop() s := srv.(*server.TestServer) // Choose a range ID that is much larger than any that would be // created by initial splits. const rangeID = roachpb.RangeID(1000) // Set up a range with replicas on two stores of the same node. This // ensures that the DistSender will consider both replicas healthy // and will try to talk to both (so we can get a non-retryable error // from the second store). replica1 := roachpb.ReplicaDescriptor{ NodeID: 1, StoreID: 1, ReplicaID: 1, } replica2 := roachpb.ReplicaDescriptor{ NodeID: 1, StoreID: 2, ReplicaID: 2, } // HACK: remove the second store from the node to generate a // non-retryable error when we try to talk to it. store2, err := s.Stores().GetStore(2) if err != nil { t.Fatal(err) } s.Stores().RemoveStore(store2) // Create the uninitialized range by sending an isolated raft // message to the first store. conn, err := s.RPCContext().GRPCDial(s.ServingAddr()) if err != nil { t.Fatal(err) } raftClient := storage.NewMultiRaftClient(conn) ctx, cancel := context.WithCancel(context.Background()) defer cancel() stream, err := raftClient.RaftMessageBatch(ctx) if err != nil { t.Fatal(err) } msg := storage.RaftMessageRequestBatch{ Requests: []storage.RaftMessageRequest{ { RangeID: rangeID, ToReplica: replica1, FromReplica: replica2, Message: raftpb.Message{ Type: raftpb.MsgApp, To: 1, }, }, }, } if err := stream.Send(&msg); err != nil { t.Fatal(err) } // Make sure the replica was created. store1, err := s.Stores().GetStore(1) if err != nil { t.Fatal(err) } util.SucceedsSoon(t, func() error { if replica, err := store1.GetReplica(rangeID); err != nil { return errors.Errorf("failed to look up replica: %s", err) } else if replica.IsInitialized() { return errors.Errorf("expected replica to be uninitialized") } return nil }) // Create our own DistSender so we can force some requests to the // bogus range. The DistSender needs to be in scope for its own // MockRangeDescriptorDB closure. var sender *kv.DistSender sender = kv.NewDistSender(kv.DistSenderConfig{ Clock: s.Clock(), RPCContext: s.RPCContext(), RangeDescriptorDB: kv.MockRangeDescriptorDB( func(key roachpb.RKey, useReverseScan bool, ) ([]roachpb.RangeDescriptor, []roachpb.RangeDescriptor, *roachpb.Error) { if key.Equal(roachpb.RKeyMin) { // Pass through requests for the first range to the real sender. desc, err := sender.FirstRange() if err != nil { return nil, nil, roachpb.NewError(err) } return []roachpb.RangeDescriptor{*desc}, nil, nil } return []roachpb.RangeDescriptor{{ RangeID: rangeID, StartKey: roachpb.RKey(keys.Meta2Prefix), EndKey: roachpb.RKeyMax, Replicas: []roachpb.ReplicaDescriptor{replica1, replica2}, }}, nil, nil }), }, s.Gossip()) // Only inconsistent reads triggered the panic in #6027. hdr := roachpb.Header{ ReadConsistency: roachpb.INCONSISTENT, } req := roachpb.NewGet(roachpb.Key("asdf")) // Repeat the test a few times: due to the randomization between the // two replicas, each attempt only had a 50% chance of triggering // the panic. for i := 0; i < 5; i++ { _, pErr := client.SendWrappedWith(context.Background(), sender, hdr, req) // Each attempt fails with "store 2 not found" because that is the // non-retryable error. if !testutils.IsPError(pErr, "store 2 not found") { t.Fatal(pErr) } } }