// TestTxnMultipleCoord checks that a coordinator uses the Writing flag to // enforce that only one coordinator can be used for transactional writes. func TestTxnMultipleCoord(t *testing.T) { defer leaktest.AfterTest(t)() s, sender := createTestDB(t) defer s.Stop() testCases := []struct { args roachpb.Request writing bool ok bool }{ {roachpb.NewGet(roachpb.Key("a")), true, false}, {roachpb.NewGet(roachpb.Key("a")), false, true}, {roachpb.NewPut(roachpb.Key("a"), roachpb.Value{}), false, false}, // transactional write before begin {roachpb.NewPut(roachpb.Key("a"), roachpb.Value{}), true, false}, // must have switched coordinators } for i, tc := range testCases { txn := roachpb.NewTransaction("test", roachpb.Key("a"), 1, roachpb.SERIALIZABLE, s.Clock.Now(), s.Clock.MaxOffset().Nanoseconds()) txn.Writing = tc.writing reply, pErr := client.SendWrappedWith(sender, nil, roachpb.Header{ Txn: txn, }, tc.args) if pErr == nil != tc.ok { t.Errorf("%d: %T (writing=%t): success_expected=%t, but got: %v", i, tc.args, tc.writing, tc.ok, pErr) } if pErr != nil { continue } txn = reply.Header().Txn // The transaction should come back rw if it started rw or if we just // wrote. isWrite := roachpb.IsTransactionWrite(tc.args) if (tc.writing || isWrite) != txn.Writing { t.Errorf("%d: unexpected writing state: %s", i, txn) } if !isWrite { continue } // Abort for clean shutdown. if _, pErr := client.SendWrappedWith(sender, nil, roachpb.Header{ Txn: txn, }, &roachpb.EndTransactionRequest{ Commit: false, }); pErr != nil { t.Fatal(pErr) } } }
// TestTxnMultipleCoord checks that a coordinator uses the Writing flag to // enforce that only one coordinator can be used for transactional writes. func TestTxnMultipleCoord(t *testing.T) { defer leaktest.AfterTest(t) s := createTestDB(t) defer s.Stop() for i, tc := range []struct { args roachpb.Request writing bool ok bool }{ {roachpb.NewGet(roachpb.Key("a")), true, true}, {roachpb.NewGet(roachpb.Key("a")), false, true}, {roachpb.NewPut(roachpb.Key("a"), roachpb.Value{}), false, true}, {roachpb.NewPut(roachpb.Key("a"), roachpb.Value{}), true, false}, } { { txn := newTxn(s.Clock, roachpb.Key("a")) txn.Writing = tc.writing tc.args.Header().Txn = txn } reply, err := client.SendWrapped(s.Sender, nil, tc.args) if err == nil != tc.ok { t.Errorf("%d: %T (writing=%t): success_expected=%t, but got: %v", i, tc.args, tc.writing, tc.ok, err) } if err != nil { continue } txn := reply.Header().Txn // The transaction should come back rw if it started rw or if we just // wrote. isWrite := roachpb.IsTransactionWrite(tc.args) if (tc.writing || isWrite) != txn.Writing { t.Errorf("%d: unexpected writing state: %s", i, txn) } if !isWrite { continue } // Abort for clean shutdown. if _, err := client.SendWrapped(s.Sender, nil, &roachpb.EndTransactionRequest{ RequestHeader: roachpb.RequestHeader{ Txn: txn, }, Commit: false, }); err != nil { t.Fatal(err) } } }
// Get retrieves the value for a key. A new result will be appended to the // batch which will contain a single row. // // r, err := db.Get("a") // // string(r.Rows[0].Key) == "a" // // key can be either a byte slice or a string. func (b *Batch) Get(key interface{}) { k, err := marshalKey(key) if err != nil { b.initResult(0, 1, err) return } b.reqs = append(b.reqs, roachpb.NewGet(k)) b.initResult(1, 1, nil) }
// Get retrieves the value for a key. A new result will be appended to the // batch which will contain a single row. // // r, err := db.Get("a") // // string(r.Rows[0].Key) == "a" // // key can be either a byte slice or a string. func (b *Batch) Get(key interface{}) { k, err := marshalKey(key) if err != nil { b.initResult(0, 1, notRaw, err) return } b.appendReqs(roachpb.NewGet(k)) b.initResult(1, 1, notRaw, nil) }
func TestNodeEventFeed(t *testing.T) { defer leaktest.AfterTest(t) nodeDesc := roachpb.NodeDescriptor{ NodeID: roachpb.NodeID(99), } // A testCase corresponds to a single Store event type. Each case contains a // method which publishes a single event to the given storeEventPublisher, // and an expected result interface which should match the produced // event. testCases := []struct { publishTo func(status.NodeEventFeed) expected interface{} }{ { publishTo: func(nef status.NodeEventFeed) { nef.StartNode(nodeDesc, 100) }, expected: &status.StartNodeEvent{ Desc: nodeDesc, StartedAt: 100, }, }, { publishTo: func(nef status.NodeEventFeed) { nef.CallComplete(wrap(roachpb.NewGet(roachpb.Key("abc"))), 0, nil) }, expected: &status.CallSuccessEvent{ NodeID: roachpb.NodeID(1), Method: roachpb.Get, }, }, { publishTo: func(nef status.NodeEventFeed) { nef.CallComplete(wrap(roachpb.NewPut(roachpb.Key("abc"), roachpb.MakeValueFromString("def"))), 0, nil) }, expected: &status.CallSuccessEvent{ NodeID: roachpb.NodeID(1), Method: roachpb.Put, }, }, { publishTo: func(nef status.NodeEventFeed) { nef.CallComplete(wrap(roachpb.NewGet(roachpb.Key("abc"))), 0, roachpb.NewErrorf("error")) }, expected: &status.CallErrorEvent{ NodeID: roachpb.NodeID(1), Method: roachpb.Batch, }, }, { publishTo: func(nef status.NodeEventFeed) { nef.CallComplete(wrap(roachpb.NewGet(roachpb.Key("abc"))), time.Minute, &roachpb.Error{ Detail: &roachpb.ErrorDetail{ WriteIntent: &roachpb.WriteIntentError{}, }, Index: &roachpb.ErrPosition{Index: 0}, Message: "boo", }) }, expected: &status.CallErrorEvent{ NodeID: roachpb.NodeID(1), Method: roachpb.Get, Duration: time.Minute, }, }, } // Compile expected events into a single slice. expectedEvents := make([]interface{}, len(testCases)) for i := range testCases { expectedEvents[i] = testCases[i].expected } events := make([]interface{}, 0, len(expectedEvents)) // Run test cases directly through a feed. stopper := stop.NewStopper() defer stopper.Stop() feed := util.NewFeed(stopper) feed.Subscribe(func(event interface{}) { events = append(events, event) }) nodefeed := status.NewNodeEventFeed(roachpb.NodeID(1), feed) for _, tc := range testCases { tc.publishTo(nodefeed) } feed.Flush() if a, e := events, expectedEvents; !reflect.DeepEqual(a, e) { t.Errorf("received incorrect events.\nexpected: %v\nactual: %v", e, a) } }
// 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)() s := server.TestServer{StoresPerNode: 2} if err := s.Start(); err != nil { t.Fatalf("Could not start server: %v", err) } defer s.Stop() // 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.RaftMessage(ctx) if err != nil { t.Fatal(err) } msg := storage.RaftMessageRequest{ GroupID: 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 util.Errorf("failed to look up replica: %s", err) } else if replica.IsInitialized() { return util.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.DistSenderContext{ Clock: s.Clock(), RPCContext: s.RPCContext(), RangeDescriptorDB: kv.MockRangeDescriptorDB( func(key roachpb.RKey, considerIntents, 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(sender, context.Background(), 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) } } }