func Fuzz(data []byte) int { defer func() { if r := recover(); r != nil { if !panicExpected(r) { panic(r) } } }() // Run in insecure mode to avoid dealing with TLS. ctx := server.MakeTestContext() ctx.Insecure = true s := server.StartTestServerWithContext(nil, &ctx) defer s.Stop() conn, err := net.Dial("tcp", s.ServingAddr()) if err != nil { panic(err) } defer conn.Close() // Connect to the PGWire v3 server. This connection message includes information // to match with pgwire.Match and bypass sql/pgwire.parseOptions: // - 4 byte prefix for the buffer's length // - 4 bytes for the version30 value // - 1 byte for a null terminator to skip session args const version30 = 196608 var connectBuf [9]byte binary.BigEndian.PutUint32(connectBuf[:4], uint32(len(connectBuf))) binary.BigEndian.PutUint32(connectBuf[4:8], version30) if err != nil { panic(err) } conn.Write(connectBuf[:]) time.Sleep(1 * time.Millisecond) // Send a pgwire "typed message" (see sql/pgwire.readBuffer.{readTypedMsg, readUntypedMsg}). // The message includes: // - 1 byte for the type // - 4 bytes for the message's length (minus the type prefix) // - the rest of message // // TODO(nvanbenschoten) investigate sending multiple messages. sendBuf := data if len(data) > 1 { newLen := len(data) + 4 sendBuf = make([]byte, newLen) sendBuf[0] = data[0] binary.BigEndian.PutUint32(sendBuf[1:5], uint32(newLen-1)) copy(sendBuf[5:], data[1:]) } conn.Write(sendBuf) time.Sleep(1 * time.Millisecond) return 0 }
// TestPropagateTxnOnError verifies that DistSender.sendChunk properly // propagates the txn data to a next iteration. Use txn.Writing field to // verify that. func TestPropagateTxnOnError(t *testing.T) { defer leaktest.AfterTest(t)() // Set up a filter to so that the first CPut operation will // get a ReadWithinUncertaintyIntervalError. targetKey := roachpb.Key("b") var numGets int32 ctx := server.NewTestContext() ctx.TestingKnobs.StoreTestingKnobs.TestingCommandFilter = func(fArgs storageutils.FilterArgs) *roachpb.Error { _, ok := fArgs.Req.(*roachpb.ConditionalPutRequest) if ok && fArgs.Req.Header().Key.Equal(targetKey) { if atomic.AddInt32(&numGets, 1) == 1 { z := roachpb.ZeroTimestamp pErr := roachpb.NewReadWithinUncertaintyIntervalError(z, z) return roachpb.NewErrorWithTxn(pErr, fArgs.Hdr.Txn) } } return nil } s := server.StartTestServerWithContext(t, ctx) defer s.Stop() db := setupMultipleRanges(t, s, "b") // Set the initial value on the target key "b". origVal := "val" if pErr := db.Put(targetKey, origVal); pErr != nil { t.Fatal(pErr) } // The following txn creates a batch request that is split // into two requests: Put and CPut. The CPut operation will // get a ReadWithinUncertaintyIntervalError and the txn will be // retried. epoch := 0 if pErr := db.Txn(func(txn *client.Txn) *roachpb.Error { epoch++ if epoch >= 2 { // Writing must be true since we ran the BeginTransaction command. if !txn.Proto.Writing { t.Errorf("unexpected non-writing txn") } } else { // Writing must be false since we haven't run any write command. if txn.Proto.Writing { t.Errorf("unexpected writing txn") } } b := txn.NewBatch() b.Put("a", "val") b.CPut(targetKey, "new_val", origVal) pErr := txn.CommitInBatch(b) if epoch == 1 { if _, ok := pErr.GetDetail().(*roachpb.ReadWithinUncertaintyIntervalError); ok { if !pErr.GetTxn().Writing { t.Errorf("unexpected non-writing txn on error") } } else { t.Errorf("expected ReadWithinUncertaintyIntervalError, but got: %s", pErr) } } return pErr }); pErr != nil { t.Errorf("unexpected error on transactional Puts: %s", pErr) } if epoch != 2 { t.Errorf("unexpected epoch; the txn must be retried exactly once, but got %d", epoch) } }