// 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 { call proto.Call writing bool ok bool }{ {proto.GetCall(proto.Key("a")), true, true}, {proto.GetCall(proto.Key("a")), false, true}, {proto.PutCall(proto.Key("a"), proto.Value{}), false, true}, {proto.PutCall(proto.Key("a"), proto.Value{}), true, false}, } { { txn := newTxn(s.Clock, proto.Key("a")) txn.Writing = tc.writing tc.call.Args.Header().Txn = txn } err := sendCall(s.Sender, tc.call) if err == nil != tc.ok { t.Errorf("%d: %T (writing=%t): success_expected=%t, but got: %v", i, tc.call.Args, tc.writing, tc.ok, err) } if err != nil { continue } txn := tc.call.Reply.Header().Txn // The transaction should come back rw if it started rw or if we just // wrote. isWrite := proto.IsTransactionWrite(tc.call.Args) if (tc.writing || isWrite) != txn.Writing { t.Errorf("%d: unexpected writing state: %s", i, txn) } if !isWrite { continue } // Abort for clean shutdown. etReply := &proto.EndTransactionResponse{} if err := sendCall(s.Sender, proto.Call{ Args: &proto.EndTransactionRequest{ RequestHeader: proto.RequestHeader{ Key: txn.Key, Timestamp: txn.Timestamp, Txn: txn, }, Commit: false, }, Reply: etReply, }); err != nil { log.Warning(err) 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, a string, a fmt.Stringer or an // encoding.BinaryMarshaler. func (b *Batch) Get(key interface{}) { k, err := marshalKey(key) if err != nil { b.initResult(0, 1, err) return } b.calls = append(b.calls, proto.GetCall(proto.Key(k))) b.initResult(1, 1, nil) }
// GetStruct retrieves the specified columns in the structured table identified // by obj. The primary key columns within obj are used to identify which row to // retrieve. The obj type must have previously been bound to a table using // BindModel. If columns is empty all of the columns are retrieved. Obj must be // a pointer to the model type. func (b *Batch) GetStruct(obj interface{}, columns ...string) { v := reflect.ValueOf(obj) m, err := b.DB.getModel(v.Type(), true) if err != nil { b.initResult(0, 0, err) return } v = reflect.Indirect(v) primaryKey, err := m.encodePrimaryKey(v) if err != nil { b.initResult(0, 0, err) return } if len(columns) == 0 { columns = m.otherColumnNames } else { lowerStrings(columns) } var calls []proto.Call for _, colName := range columns { col, ok := m.columnsByName[colName] if !ok { b.initResult(0, 0, fmt.Errorf("%s: unable to find column %s", m.name, colName)) return } key := m.encodeColumnKey(primaryKey, col.ID) if log.V(2) { log.Infof("Get %q", key) } c := proto.GetCall(proto.Key(key)) c.Post = func() error { reply := c.Reply.(*proto.GetResponse) return unmarshalValue(reply.Value, v.FieldByIndex(col.field.Index)) } calls = append(calls, c) } b.calls = append(b.calls, calls...) b.initResult(len(calls), len(calls), 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 { name string publishTo func(status.NodeEventFeed) expected interface{} }{ { name: "Start", publishTo: func(nef status.NodeEventFeed) { nef.StartNode(nodeDesc, 100) }, expected: &status.StartNodeEvent{ Desc: nodeDesc, StartedAt: 100, }, }, { name: "Get", publishTo: func(nef status.NodeEventFeed) { call := proto.GetCall(proto.Key("abc")) nef.CallComplete(call.Args, call.Reply) }, expected: &status.CallSuccessEvent{ NodeID: proto.NodeID(1), Method: proto.Get, }, }, { name: "Put", publishTo: func(nef status.NodeEventFeed) { call := proto.PutCall(proto.Key("abc"), proto.Value{Bytes: []byte("def")}) nef.CallComplete(call.Args, call.Reply) }, expected: &status.CallSuccessEvent{ NodeID: proto.NodeID(1), Method: proto.Put, }, }, { name: "Get Error", publishTo: func(nef status.NodeEventFeed) { call := proto.GetCall(proto.Key("abc")) call.Reply.Header().SetGoError(util.Errorf("error")) nef.CallComplete(call.Args, call.Reply) }, 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) } }
func TestNodeEventFeed(t *testing.T) { defer leaktest.AfterTest(t) // 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 { name string publishTo func(status.NodeEventFeed) expected interface{} }{ { name: "Get", publishTo: func(nef status.NodeEventFeed) { call := proto.GetCall(proto.Key("abc")) nef.CallComplete(call.Args, call.Reply) }, expected: &status.CallSuccessEvent{ NodeID: proto.NodeID(1), Method: proto.Get, }, }, { name: "Put", publishTo: func(nef status.NodeEventFeed) { call := proto.PutCall(proto.Key("abc"), proto.Value{Bytes: []byte("def")}) nef.CallComplete(call.Args, call.Reply) }, expected: &status.CallSuccessEvent{ NodeID: proto.NodeID(1), Method: proto.Put, }, }, { name: "Get Error", publishTo: func(nef status.NodeEventFeed) { call := proto.GetCall(proto.Key("abc")) call.Reply.Header().SetGoError(util.Errorf("error")) nef.CallComplete(call.Args, call.Reply) }, 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 } // assertEventsEqual verifies that the given set of events is equal to the // expectedEvents. verifyEventSlice := func(source string, events []interface{}) { if a, e := len(events), len(expectedEvents); a != e { t.Errorf("%s had wrong number of events %d, expected %d", source, a, e) return } for i := range events { if a, e := events[i], expectedEvents[i]; !reflect.DeepEqual(a, e) { t.Errorf("%s had wrong event for case %s: got %v, expected %v", source, testCases[i].name, a, e) } } } // Run test cases directly through a feed. stopper, feed, consumers := startConsumerSet(3) nodefeed := status.NewNodeEventFeed(proto.NodeID(1), feed) for _, tc := range testCases { tc.publishTo(nodefeed) } feed.Close() waitForStopper(t, stopper) for i, c := range consumers { verifyEventSlice(fmt.Sprintf("feed direct consumer %d", i), c.received) } }