// 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 proto.Request writing bool ok bool }{ {proto.NewGet(proto.Key("a")), true, true}, {proto.NewGet(proto.Key("a")), false, true}, {proto.NewPut(proto.Key("a"), proto.Value{}), false, true}, {proto.NewPut(proto.Key("a"), proto.Value{}), true, false}, } { { txn := newTxn(s.Clock, proto.Key("a")) txn.Writing = tc.writing tc.args.Header().Txn = txn } reply, err := batchutil.SendWrapped(s.Sender, 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 := proto.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 := batchutil.SendWrapped(s.Sender, &proto.EndTransactionRequest{ RequestHeader: proto.RequestHeader{ Key: txn.Key, Timestamp: txn.Timestamp, 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, proto.NewGet(k)) b.initResult(1, 1, nil) }
func TestNodeEventFeed(t *testing.T) { defer leaktest.AfterTest(t) nodeDesc := proto.NodeDescriptor{ NodeID: proto.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(proto.NewGet(proto.Key("abc"))), nil) }, expected: &status.CallSuccessEvent{ NodeID: proto.NodeID(1), Method: proto.Get, }, }, { publishTo: func(nef status.NodeEventFeed) { nef.CallComplete(wrap(proto.NewPut(proto.Key("abc"), proto.Value{Bytes: []byte("def")})), nil) }, expected: &status.CallSuccessEvent{ NodeID: proto.NodeID(1), Method: proto.Put, }, }, { publishTo: func(nef status.NodeEventFeed) { nef.CallComplete(wrap(proto.NewGet(proto.Key("abc"))), proto.NewError(util.Errorf("error"))) }, expected: &status.CallErrorEvent{ NodeID: proto.NodeID(1), Method: proto.Batch, }, }, { publishTo: func(nef status.NodeEventFeed) { nef.CallComplete(wrap(proto.NewGet(proto.Key("abc"))), &proto.Error{ Index: &proto.ErrPosition{Index: 0}, Message: "boo", }) }, expected: &status.CallErrorEvent{ NodeID: proto.NodeID(1), Method: proto.Get, }, }, } // 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(proto.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) } }