// TestTimestampCacheEqualTimestamp verifies that in the event of two // non-overlapping transactions with equal timestamps, the returned // timestamp is not owned by either one. func TestTimestampCacheEqualTimestamps(t *testing.T) { defer leaktest.AfterTest(t)() manual := hlc.NewManualClock(123) clock := hlc.NewClock(manual.UnixNano, time.Nanosecond) tc := newTimestampCache(clock) txn1 := uuid.MakeV4() txn2 := uuid.MakeV4() // Add two non-overlapping transactions at the same timestamp. ts1 := clock.Now() tc.add(roachpb.Key("a"), roachpb.Key("b"), ts1, &txn1, true) tc.add(roachpb.Key("b"), roachpb.Key("c"), ts1, &txn2, true) // When querying either side separately, the transaction ID is returned. if ts, txn, _ := tc.GetMaxRead(roachpb.Key("a"), roachpb.Key("b")); !ts.Equal(ts1) { t.Errorf("expected 'a'-'b' to have timestamp %s, but found %s", ts1, ts) } else if *txn != txn1 { t.Errorf("expected 'a'-'b' to have txn id %s, but found %s", txn1, txn) } if ts, txn, _ := tc.GetMaxRead(roachpb.Key("b"), roachpb.Key("c")); !ts.Equal(ts1) { t.Errorf("expected 'b'-'c' to have timestamp %s, but found %s", ts1, ts) } else if *txn != txn2 { t.Errorf("expected 'b'-'c' to have txn id %s, but found %s", txn2, txn) } // Querying a span that overlaps both returns a nil txn ID; neither // can proceed here. if ts, txn, _ := tc.GetMaxRead(roachpb.Key("a"), roachpb.Key("c")); !ts.Equal(ts1) { t.Errorf("expected 'a'-'c' to have timestamp %s, but found %s", ts1, ts) } else if txn != nil { t.Errorf("expected 'a'-'c' to have nil txn id, but found %s", txn) } }
func TestTxnEqual(t *testing.T) { u1, u2 := uuid.MakeV4(), uuid.MakeV4() tc := []struct { txn1, txn2 *Transaction eq bool }{ {nil, nil, true}, {&Transaction{}, nil, false}, {&Transaction{TxnMeta: enginepb.TxnMeta{ID: &u1}}, &Transaction{TxnMeta: enginepb.TxnMeta{ID: &u2}}, false}, } for i, c := range tc { if c.txn1.Equal(c.txn2) != c.txn2.Equal(c.txn1) || c.txn1.Equal(c.txn2) != c.eq { t.Errorf("%d: wanted %t", i, c.eq) } } }
// runTestFlow runs a flow with the given processors and returns the results. // Any errors stop the current test. func runTestFlow( t *testing.T, srv serverutils.TestServerInterface, procs ...distsqlrun.ProcessorSpec, ) sqlbase.EncDatumRows { kvDB := srv.KVClient().(*client.DB) distSQLSrv := srv.DistSQLServer().(*distsqlrun.ServerImpl) req := distsqlrun.SetupFlowRequest{ Txn: client.NewTxn(context.TODO(), *kvDB).Proto, Flow: distsqlrun.FlowSpec{ FlowID: distsqlrun.FlowID{UUID: uuid.MakeV4()}, Processors: procs, }, } var rowBuf distsqlrun.RowBuffer flow, err := distSQLSrv.SetupSyncFlow(context.TODO(), &req, &rowBuf) if err != nil { t.Fatal(err) } flow.Start(func() {}) flow.Wait() flow.Cleanup() if rowBuf.Err != nil { t.Fatal(rowBuf.Err) } if !rowBuf.Closed { t.Errorf("output not closed") } return rowBuf.Rows }
// createRangeData creates sample range data in all possible areas of // the key space. Returns a slice of the encoded keys of all created // data. func createRangeData(t *testing.T, r *Replica) []engine.MVCCKey { ts0 := hlc.ZeroTimestamp ts := hlc.Timestamp{WallTime: 1} desc := r.Desc() keyTSs := []struct { key roachpb.Key ts hlc.Timestamp }{ {keys.AbortCacheKey(r.RangeID, testTxnID), ts0}, {keys.AbortCacheKey(r.RangeID, testTxnID2), ts0}, {keys.RangeFrozenStatusKey(r.RangeID), ts0}, {keys.RangeLastGCKey(r.RangeID), ts0}, {keys.RaftAppliedIndexKey(r.RangeID), ts0}, {keys.RaftTruncatedStateKey(r.RangeID), ts0}, {keys.RangeLeaseKey(r.RangeID), ts0}, {keys.LeaseAppliedIndexKey(r.RangeID), ts0}, {keys.RangeStatsKey(r.RangeID), ts0}, {keys.RangeTxnSpanGCThresholdKey(r.RangeID), ts0}, {keys.RaftHardStateKey(r.RangeID), ts0}, {keys.RaftLastIndexKey(r.RangeID), ts0}, {keys.RaftLogKey(r.RangeID, 1), ts0}, {keys.RaftLogKey(r.RangeID, 2), ts0}, {keys.RangeLastReplicaGCTimestampKey(r.RangeID), ts0}, {keys.RangeLastVerificationTimestampKeyDeprecated(r.RangeID), ts0}, {keys.RangeDescriptorKey(desc.StartKey), ts}, {keys.TransactionKey(roachpb.Key(desc.StartKey), uuid.MakeV4()), ts0}, {keys.TransactionKey(roachpb.Key(desc.StartKey.Next()), uuid.MakeV4()), ts0}, {keys.TransactionKey(fakePrevKey(desc.EndKey), uuid.MakeV4()), ts0}, // TODO(bdarnell): KeyMin.Next() results in a key in the reserved system-local space. // Once we have resolved https://github.com/cockroachdb/cockroach/issues/437, // replace this with something that reliably generates the first valid key in the range. //{r.Desc().StartKey.Next(), ts}, // The following line is similar to StartKey.Next() but adds more to the key to // avoid falling into the system-local space. {append(append([]byte{}, desc.StartKey...), '\x02'), ts}, {fakePrevKey(r.Desc().EndKey), ts}, } keys := []engine.MVCCKey{} for _, keyTS := range keyTSs { if err := engine.MVCCPut(context.Background(), r.store.Engine(), nil, keyTS.key, keyTS.ts, roachpb.MakeValueFromString("value"), nil); err != nil { t.Fatal(err) } keys = append(keys, engine.MVCCKey{Key: keyTS.key, Timestamp: keyTS.ts}) } return keys }
func TestTxnIDEqual(t *testing.T) { txn1, txn2 := uuid.MakeV4(), uuid.MakeV4() txn1Copy := txn1 testCases := []struct { a, b *uuid.UUID expEqual bool }{ {&txn1, &txn1, true}, {&txn1, &txn2, false}, {&txn1, &txn1Copy, true}, } for i, test := range testCases { if eq := TxnIDEqual(test.a, test.b); eq != test.expEqual { t.Errorf("%d: expected %q == %q: %t; got %t", i, test.a, test.b, test.expEqual, eq) } } }
func TestCloneProto(t *testing.T) { u := uuid.MakeV4() testCases := []struct { pb proto.Message shouldPanic bool }{ {&roachpb.StoreIdent{}, false}, {&roachpb.StoreIdent{ClusterID: uuid.MakeV4()}, true}, {&enginepb.TxnMeta{}, false}, {&enginepb.TxnMeta{ID: &u}, true}, {&roachpb.Transaction{}, false}, {&config.ZoneConfig{RangeMinBytes: 123, RangeMaxBytes: 456}, false}, } for _, tc := range testCases { var clone proto.Message var panicObj interface{} func() { defer func() { panicObj = recover() }() clone = protoutil.Clone(tc.pb) }() if tc.shouldPanic { if panicObj == nil { t.Errorf("%T: expected panic but didn't get one", tc.pb) } } else { if panicObj != nil { if panicStr := fmt.Sprint(panicObj); !strings.Contains(panicStr, "attempt to clone") { t.Errorf("%T: got unexpected panic %s", tc.pb, panicStr) } } } if panicObj == nil { realClone := proto.Clone(tc.pb) if !reflect.DeepEqual(clone, realClone) { t.Errorf("%T: clone did not equal original. expected:\n%+v\ngot:\n%+v", tc.pb, realClone, clone) } } } }
func TestKeyAddress(t *testing.T) { testCases := []struct { key roachpb.Key expAddress roachpb.RKey }{ {roachpb.Key{}, roachpb.RKeyMin}, {roachpb.Key("123"), roachpb.RKey("123")}, {RangeDescriptorKey(roachpb.RKey("foo")), roachpb.RKey("foo")}, {TransactionKey(roachpb.Key("baz"), uuid.MakeV4()), roachpb.RKey("baz")}, {TransactionKey(roachpb.KeyMax, uuid.MakeV4()), roachpb.RKeyMax}, {RangeDescriptorKey(roachpb.RKey(TransactionKey(roachpb.Key("doubleBaz"), uuid.MakeV4()))), roachpb.RKey("doubleBaz")}, {nil, nil}, } for i, test := range testCases { if keyAddr, err := Addr(test.key); err != nil { t.Errorf("%d: %v", i, err) } else if !keyAddr.Equal(test.expAddress) { t.Errorf("%d: expected address for key %q doesn't match %q", i, test.key, test.expAddress) } } }
// NewLeaseManager allocates a new LeaseManager. func NewLeaseManager(db *DB, clock *hlc.Clock, options LeaseManagerOptions) *LeaseManager { if options.ClientID == "" { options.ClientID = uuid.MakeV4().String() } if options.LeaseDuration <= 0 { options.LeaseDuration = DefaultLeaseDuration } return &LeaseManager{ db: db, clock: clock, clientID: options.ClientID, leaseDuration: options.LeaseDuration, } }
// TestTimestampCacheReadVsWrite verifies that the timestamp cache // can differentiate between read and write timestamp. func TestTimestampCacheReadVsWrite(t *testing.T) { defer leaktest.AfterTest(t)() manual := hlc.NewManualClock(123) clock := hlc.NewClock(manual.UnixNano, time.Nanosecond) tc := newTimestampCache(clock) // Add read-only non-txn entry at current time. ts1 := clock.Now() tc.add(roachpb.Key("a"), roachpb.Key("b"), ts1, nil, true) // Add two successive txn entries; one read-only and one read-write. txn1ID := uuid.MakeV4() txn2ID := uuid.MakeV4() ts2 := clock.Now() tc.add(roachpb.Key("a"), nil, ts2, &txn1ID, true) ts3 := clock.Now() tc.add(roachpb.Key("a"), nil, ts3, &txn2ID, false) rTS, _, rOK := tc.GetMaxRead(roachpb.Key("a"), nil) wTS, _, wOK := tc.GetMaxWrite(roachpb.Key("a"), nil) if !rTS.Equal(ts2) || !wTS.Equal(ts3) || !rOK || !wOK { t.Errorf("expected %s %s; got %s %s; rOK=%t, wOK=%t", ts2, ts3, rTS, wTS, rOK, wOK) } }
// CreateLocal creates a new local cockroach cluster. The stopper is used to // gracefully shutdown the channel (e.g. when a signal arrives). The cluster // must be started before being used and keeps logs in the specified logDir, if // supplied. func CreateLocal( ctx context.Context, cfg TestConfig, logDir string, privileged bool, stopper *stop.Stopper, ) *LocalCluster { select { case <-stopper.ShouldStop(): // The stopper was already closed, exit early. os.Exit(1) default: } if *cockroachImage == builderImageFull && !exists(*cockroachBinary) { log.Fatalf(ctx, "\"%s\": does not exist", *cockroachBinary) } cli, err := client.NewEnvClient() maybePanic(err) retryingClient := retryingDockerClient{ resilientDockerClient: resilientDockerClient{APIClient: cli}, attempts: 10, timeout: 10 * time.Second, } clusterID := uuid.MakeV4() clusterIDS := clusterID.Short() // Only pass a nonzero logDir down to LocalCluster when instructed to keep // logs. var uniqueLogDir string if logDir != "" { uniqueLogDir = fmt.Sprintf("%s-%s", logDir, clusterIDS) } return &LocalCluster{ clusterID: clusterIDS, client: retryingClient, config: cfg, stopper: stopper, // TODO(tschottdorf): deadlocks will occur if these channels fill up. events: make(chan Event, 1000), expectedEvents: make(chan Event, 1000), logDir: uniqueLogDir, privileged: privileged, } }
func TestKeyAddressError(t *testing.T) { testCases := map[string][]roachpb.Key{ "store-local key .* is not addressable": { StoreIdentKey(), StoreGossipKey(), }, "local range ID key .* is not addressable": { AbortCacheKey(0, uuid.MakeV4()), RaftTombstoneKey(0), RaftAppliedIndexKey(0), RaftTruncatedStateKey(0), RangeLeaseKey(0), RangeStatsKey(0), RaftHardStateKey(0), RaftLastIndexKey(0), RaftLogPrefix(0), RaftLogKey(0, 0), RangeLastReplicaGCTimestampKey(0), RangeLastVerificationTimestampKeyDeprecated(0), RangeDescriptorKey(roachpb.RKey(RangeLastVerificationTimestampKeyDeprecated(0))), }, "local key .* malformed": { makeKey(localPrefix, roachpb.Key("z")), }, } for regexp, keyList := range testCases { for _, key := range keyList { if addr, err := Addr(key); err == nil { t.Errorf("expected addressing key %q to throw error, but it returned address %q", key, addr) } else if !testutils.IsError(err, regexp) { t.Errorf("expected addressing key %q to throw error matching %s, but got error %v", key, regexp, err) } } } }
// NewTransaction creates a new transaction. The transaction key is // composed using the specified baseKey (for locality with data // affected by the transaction) and a random ID to guarantee // uniqueness. The specified user-level priority is combined with a // randomly chosen value to yield a final priority, used to settle // write conflicts in a way that avoids starvation of long-running // transactions (see Replica.PushTxn). func NewTransaction( name string, baseKey Key, userPriority UserPriority, isolation enginepb.IsolationType, now hlc.Timestamp, maxOffset int64, ) *Transaction { u := uuid.MakeV4() return &Transaction{ TxnMeta: enginepb.TxnMeta{ Key: baseKey, ID: &u, Isolation: isolation, Timestamp: now, Priority: MakePriority(userPriority), Sequence: 1, }, Name: name, OrigTimestamp: now, MaxTimestamp: now.Add(maxOffset, 0), } }
func TestTransactionUpdate(t *testing.T) { txn := nonZeroTxn if err := zerofields.NoZeroField(txn); err != nil { t.Fatal(err) } var txn2 Transaction txn2.Update(&txn) if err := zerofields.NoZeroField(txn2); err != nil { t.Fatal(err) } u := uuid.MakeV4() var txn3 Transaction txn3.ID = &u txn3.Name = "carl" txn3.Isolation = enginepb.SNAPSHOT txn3.Update(&txn) if err := zerofields.NoZeroField(txn3); err != nil { t.Fatal(err) } }
// snapshot creates an OutgoingSnapshot containing a rocksdb snapshot for the given range. func snapshot( ctx context.Context, snapType string, snap engine.Reader, rangeID roachpb.RangeID, eCache *raftEntryCache, startKey roachpb.RKey, ) (OutgoingSnapshot, error) { var desc roachpb.RangeDescriptor // We ignore intents on the range descriptor (consistent=false) because we // know they cannot be committed yet; operations that modify range // descriptors resolve their own intents when they commit. ok, err := engine.MVCCGetProto(ctx, snap, keys.RangeDescriptorKey(startKey), hlc.MaxTimestamp, false /* !consistent */, nil, &desc) if err != nil { return OutgoingSnapshot{}, errors.Errorf("failed to get desc: %s", err) } if !ok { return OutgoingSnapshot{}, errors.Errorf("couldn't find range descriptor") } var snapData roachpb.RaftSnapshotData // Store RangeDescriptor as metadata, it will be retrieved by ApplySnapshot() snapData.RangeDescriptor = desc // Read the range metadata from the snapshot instead of the members // of the Range struct because they might be changed concurrently. appliedIndex, _, err := loadAppliedIndex(ctx, snap, rangeID) if err != nil { return OutgoingSnapshot{}, err } // Synthesize our raftpb.ConfState from desc. var cs raftpb.ConfState for _, rep := range desc.Replicas { cs.Nodes = append(cs.Nodes, uint64(rep.ReplicaID)) } term, err := term(ctx, snap, rangeID, eCache, appliedIndex) if err != nil { return OutgoingSnapshot{}, errors.Errorf("failed to fetch term of %d: %s", appliedIndex, err) } state, err := loadState(ctx, snap, &desc) if err != nil { return OutgoingSnapshot{}, err } // Intentionally let this iterator and the snapshot escape so that the // streamer can send chunks from it bit by bit. iter := NewReplicaDataIterator(&desc, snap, true /* replicatedOnly */) snapUUID := uuid.MakeV4() log.Infof(ctx, "generated %s snapshot %s at index %d", snapType, snapUUID.Short(), appliedIndex) return OutgoingSnapshot{ EngineSnap: snap, Iter: iter, State: state, SnapUUID: snapUUID, RaftSnap: raftpb.Snapshot{ Data: snapUUID.GetBytes(), Metadata: raftpb.SnapshotMetadata{ Index: appliedIndex, Term: term, ConfState: cs, }, }, }, nil }
func TestTruncate(t *testing.T) { defer leaktest.AfterTest(t)() loc := func(s string) string { return string(keys.RangeDescriptorKey(roachpb.RKey(s))) } locPrefix := func(s string) string { return string(keys.MakeRangeKeyPrefix(roachpb.RKey(s))) } testCases := []struct { keys [][2]string expKeys [][2]string from, to string desc [2]string // optional, defaults to {from,to} err string }{ { // Keys inside of active range. keys: [][2]string{{"a", "q"}, {"c"}, {"b, e"}, {"q"}}, expKeys: [][2]string{{"a", "q"}, {"c"}, {"b, e"}, {"q"}}, from: "a", to: "q\x00", }, { // Keys outside of active range. keys: [][2]string{{"a"}, {"a", "b"}, {"q"}, {"q", "z"}}, expKeys: [][2]string{{}, {}, {}, {}}, from: "b", to: "q", }, { // Range-local keys inside of active range. keys: [][2]string{{loc("b")}, {loc("c")}}, expKeys: [][2]string{{loc("b")}, {loc("c")}}, from: "b", to: "e", }, { // Range-local key outside of active range. keys: [][2]string{{loc("a")}}, expKeys: [][2]string{{}}, from: "b", to: "e", }, { // Range-local range contained in active range. keys: [][2]string{{loc("b"), loc("e") + "\x00"}}, expKeys: [][2]string{{loc("b"), loc("e") + "\x00"}}, from: "b", to: "e\x00", }, { // Range-local range not contained in active range. keys: [][2]string{{loc("a"), loc("b")}}, expKeys: [][2]string{{}}, from: "c", to: "e", }, { // Range-local range not contained in active range. keys: [][2]string{{loc("a"), locPrefix("b")}, {loc("e"), loc("f")}}, expKeys: [][2]string{{}, {}}, from: "b", to: "e", }, { // Range-local range partially contained in active range. keys: [][2]string{{loc("a"), loc("b")}}, expKeys: [][2]string{{loc("a"), locPrefix("b")}}, from: "a", to: "b", }, { // Range-local range partially contained in active range. keys: [][2]string{{loc("a"), loc("b")}}, expKeys: [][2]string{{locPrefix("b"), loc("b")}}, from: "b", to: "e", }, { // Range-local range contained in active range. keys: [][2]string{{locPrefix("b"), loc("b")}}, expKeys: [][2]string{{locPrefix("b"), loc("b")}}, from: "b", to: "c", }, { // Mixed range-local vs global key range. keys: [][2]string{{loc("c"), "d\x00"}}, from: "b", to: "e", err: "local key mixed with global key", }, { // Key range touching and intersecting active range. keys: [][2]string{{"a", "b"}, {"a", "c"}, {"p", "q"}, {"p", "r"}, {"a", "z"}}, expKeys: [][2]string{{}, {"b", "c"}, {"p", "q"}, {"p", "q"}, {"b", "q"}}, from: "b", to: "q", }, // Active key range is intersection of descriptor and [from,to). { keys: [][2]string{{"c", "q"}}, expKeys: [][2]string{{"d", "p"}}, from: "a", to: "z", desc: [2]string{"d", "p"}, }, { keys: [][2]string{{"c", "q"}}, expKeys: [][2]string{{"d", "p"}}, from: "d", to: "p", desc: [2]string{"a", "z"}, }, } for i, test := range testCases { goldenOriginal := roachpb.BatchRequest{} for _, ks := range test.keys { if len(ks[1]) > 0 { u := uuid.MakeV4() goldenOriginal.Add(&roachpb.ResolveIntentRangeRequest{ Span: roachpb.Span{Key: roachpb.Key(ks[0]), EndKey: roachpb.Key(ks[1])}, IntentTxn: enginepb.TxnMeta{ID: &u}, }) } else { goldenOriginal.Add(&roachpb.GetRequest{ Span: roachpb.Span{Key: roachpb.Key(ks[0])}, }) } } original := roachpb.BatchRequest{Requests: make([]roachpb.RequestUnion, len(goldenOriginal.Requests))} for i, request := range goldenOriginal.Requests { original.Requests[i].SetValue(request.GetInner().ShallowCopy()) } desc := &roachpb.RangeDescriptor{ StartKey: roachpb.RKey(test.desc[0]), EndKey: roachpb.RKey(test.desc[1]), } if len(desc.StartKey) == 0 { desc.StartKey = roachpb.RKey(test.from) } if len(desc.EndKey) == 0 { desc.EndKey = roachpb.RKey(test.to) } rs := roachpb.RSpan{Key: roachpb.RKey(test.from), EndKey: roachpb.RKey(test.to)} rs, err := rs.Intersect(desc) if err != nil { t.Errorf("%d: intersection failure: %v", i, err) continue } ba, num, err := truncate(original, rs) if err != nil || test.err != "" { if !testutils.IsError(err, test.err) { t.Errorf("%d: %v (expected: %q)", i, err, test.err) } continue } var reqs int for j, arg := range ba.Requests { req := arg.GetInner() if _, ok := req.(*roachpb.NoopRequest); ok { continue } if h := req.Header(); !bytes.Equal(h.Key, roachpb.Key(test.expKeys[j][0])) || !bytes.Equal(h.EndKey, roachpb.Key(test.expKeys[j][1])) { t.Errorf("%d.%d: range mismatch: actual [%q,%q), wanted [%q,%q)", i, j, h.Key, h.EndKey, test.expKeys[j][0], test.expKeys[j][1]) } else if _, ok := req.(*roachpb.NoopRequest); ok != (len(h.Key) == 0) { t.Errorf("%d.%d: expected NoopRequest, got %T", i, j, req) } else if len(h.Key) != 0 { reqs++ } } if reqs != num { t.Errorf("%d: counted %d requests, but truncation indicated %d", i, reqs, num) } if !reflect.DeepEqual(original, goldenOriginal) { t.Errorf("%d: truncation mutated original:\nexpected: %s\nactual: %s", i, goldenOriginal, original) } } }
// PlanAndRun generates a physical plan from a planNode tree and executes it. It // assumes that the tree is supported (see CheckSupport). // // Note that errors that happen while actually running the flow are reported to // recv, not returned by this function. func (dsp *distSQLPlanner) PlanAndRun( ctx context.Context, txn *client.Txn, tree planNode, recv *distSQLReceiver, ) error { // Trigger limit propagation. tree.SetLimitHint(math.MaxInt64, true) planCtx := planningCtx{ ctx: ctx, spanIter: dsp.spanResolver.NewSpanResolverIterator(), nodeAddresses: make(map[roachpb.NodeID]string), } thisNodeID := dsp.nodeDesc.NodeID planCtx.nodeAddresses[thisNodeID] = dsp.nodeDesc.Address.String() plan, err := dsp.createPlanForNode(&planCtx, tree) if err != nil { return err } // If we don't already have a single result router on this node, add a final // stage. if len(plan.resultRouters) != 1 || plan.processors[plan.resultRouters[0]].node != thisNodeID { dsp.addSingleGroupStage( &plan, thisNodeID, distsql.ProcessorCoreUnion{Noop: &distsql.NoopCoreSpec{}}, ) if len(plan.resultRouters) != 1 { panic(fmt.Sprintf("%d results after single group stage", len(plan.resultRouters))) } } // Set up the endpoints for p.streams. dsp.populateEndpoints(&planCtx, &plan) // Set up the endpoint for the final result. finalOut := &plan.processors[plan.resultRouters[0]].spec.Output[0] finalOut.Streams = append(finalOut.Streams, distsql.StreamEndpointSpec{ Type: distsql.StreamEndpointSpec_SYNC_RESPONSE, }) recv.resultToStreamColMap = plan.planToStreamColMap // Split the processors by nodeID to create the FlowSpecs. flowID := distsql.FlowID{UUID: uuid.MakeV4()} nodeIDMap := make(map[roachpb.NodeID]int) // nodeAddresses contains addresses for the nodes that were referenced during // planning, so we're likely going to have this many nodes (and we have one // flow per node). nodeIDs := make([]roachpb.NodeID, 0, len(planCtx.nodeAddresses)) flows := make([]distsql.FlowSpec, 0, len(planCtx.nodeAddresses)) for _, p := range plan.processors { idx, ok := nodeIDMap[p.node] if !ok { flow := distsql.FlowSpec{FlowID: flowID} idx = len(flows) flows = append(flows, flow) nodeIDs = append(nodeIDs, p.node) nodeIDMap[p.node] = idx } flows[idx].Processors = append(flows[idx].Processors, p.spec) } // Start the flows on all other nodes. for i, nodeID := range nodeIDs { if nodeID == thisNodeID { // Skip this node. continue } req := distsql.SetupFlowRequest{ Txn: txn.Proto, Flow: flows[i], } if err := distsql.SetFlowRequestTrace(ctx, &req); err != nil { return err } conn, err := dsp.rpcContext.GRPCDial(planCtx.nodeAddresses[nodeID]) if err != nil { return err } client := distsql.NewDistSQLClient(conn) // TODO(radu): we are not waiting for the flows to complete, but we are // still waiting for a round trip; we should start the flows in parallel, at // least if there are enough of them. if resp, err := client.SetupFlow(context.Background(), &req); err != nil { return err } else if resp.Error != nil { return resp.Error.GoError() } } localReq := distsql.SetupFlowRequest{ Txn: txn.Proto, Flow: flows[nodeIDMap[thisNodeID]], } if err := distsql.SetFlowRequestTrace(ctx, &localReq); err != nil { return err } flow, err := dsp.distSQLSrv.SetupSyncFlow(ctx, &localReq, recv) if err != nil { return err } // TODO(radu): this should go through the flow scheduler. flow.Start(func() {}) flow.Wait() flow.Cleanup() return nil }
// 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(123) clock := hlc.NewClock(manual.UnixNano, 20*time.Nanosecond) u := uuid.MakeV4() testCases := []struct { roachpb.Error errMsg string }{ {*roachpb.NewError(roachpb.NewTransactionRetryError()), "retry txn"}, { *roachpb.NewError(roachpb.NewTransactionPushError(roachpb.Transaction{ TxnMeta: enginepb.TxnMeta{ID: &u}}), ), "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) } }() } }
// bootstrapCluster bootstraps a multiple stores using the provided // engines and cluster ID. The first bootstrapped store contains a // single range spanning all keys. Initial range lookup metadata is // populated for the range. Returns the cluster ID. func bootstrapCluster(engines []engine.Engine, txnMetrics kv.TxnMetrics) (uuid.UUID, error) { clusterID := uuid.MakeV4() stopper := stop.NewStopper() defer stopper.Stop() cfg := storage.StoreConfig{} cfg.ScanInterval = 10 * time.Minute cfg.MetricsSampleInterval = time.Duration(math.MaxInt64) cfg.ConsistencyCheckInterval = 10 * time.Minute cfg.Clock = hlc.NewClock(hlc.UnixNano) cfg.AmbientCtx.Tracer = tracing.NewTracer() // Create a KV DB with a local sender. stores := storage.NewStores(cfg.AmbientCtx, cfg.Clock) sender := kv.NewTxnCoordSender(cfg.AmbientCtx, stores, cfg.Clock, false, stopper, txnMetrics) cfg.DB = client.NewDB(sender) cfg.Transport = storage.NewDummyRaftTransport() for i, eng := range engines { sIdent := roachpb.StoreIdent{ ClusterID: clusterID, NodeID: FirstNodeID, StoreID: roachpb.StoreID(i + 1), } // The bootstrapping store will not connect to other nodes so its // StoreConfig doesn't really matter. s := storage.NewStore(cfg, eng, &roachpb.NodeDescriptor{NodeID: FirstNodeID}) // Verify the store isn't already part of a cluster. if s.Ident.ClusterID != *uuid.EmptyUUID { return uuid.UUID{}, errors.Errorf("storage engine already belongs to a cluster (%s)", s.Ident.ClusterID) } // Bootstrap store to persist the store ident. if err := s.Bootstrap(sIdent); err != nil { return uuid.UUID{}, err } // Create first range, writing directly to engine. Note this does // not create the range, just its data. Only do this if this is the // first store. if i == 0 { initialValues := GetBootstrapSchema().GetInitialValues() if err := s.BootstrapRange(initialValues); err != nil { return uuid.UUID{}, err } } if err := s.Start(context.Background(), stopper); err != nil { return uuid.UUID{}, err } stores.AddStore(s) ctx := context.TODO() // Initialize node and store ids. Only initialize the node once. if i == 0 { if nodeID, err := allocateNodeID(ctx, cfg.DB); nodeID != sIdent.NodeID || err != nil { return uuid.UUID{}, errors.Errorf("expected to initialize node id allocator to %d, got %d: %s", sIdent.NodeID, nodeID, err) } } if storeID, err := allocateStoreIDs(ctx, sIdent.NodeID, 1, cfg.DB); storeID != sIdent.StoreID || err != nil { return uuid.UUID{}, errors.Errorf("expected to initialize store id allocator to %d, got %d: %s", sIdent.StoreID, storeID, err) } } return clusterID, nil }
func TestFlowRegistry(t *testing.T) { defer leaktest.AfterTest(t)() reg := makeFlowRegistry() id1 := FlowID{uuid.MakeV4()} f1 := &Flow{} id2 := FlowID{uuid.MakeV4()} f2 := &Flow{} id3 := FlowID{uuid.MakeV4()} f3 := &Flow{} id4 := FlowID{uuid.MakeV4()} f4 := &Flow{} // A basic duration; needs to be significantly larger than possible delays // in scheduling goroutines. jiffy := 10 * time.Millisecond // -- Lookup, register, lookup, unregister, lookup. -- if f := reg.LookupFlow(id1, 0); f != nil { t.Error("looked up unregistered flow") } reg.RegisterFlow(id1, f1, nil) if f := reg.LookupFlow(id1, 0); f != f1 { t.Error("couldn't lookup previously registered flow") } reg.UnregisterFlow(id1) if f := reg.LookupFlow(id1, 0); f != nil { t.Error("looked up unregistered flow") } // -- Lookup with timeout, register in the meantime. -- go func() { time.Sleep(jiffy) reg.RegisterFlow(id1, f1, nil) }() if f := reg.LookupFlow(id1, 10*jiffy); f != f1 { t.Error("couldn't lookup registered flow (with wait)") } if f := reg.LookupFlow(id1, 0); f != f1 { t.Error("couldn't lookup registered flow") } // -- Multiple lookups before register. -- var wg sync.WaitGroup wg.Add(2) go func() { if f := reg.LookupFlow(id2, 10*jiffy); f != f2 { t.Error("couldn't lookup registered flow (with wait)") } wg.Done() }() go func() { if f := reg.LookupFlow(id2, 10*jiffy); f != f2 { t.Error("couldn't lookup registered flow (with wait)") } wg.Done() }() time.Sleep(jiffy) reg.RegisterFlow(id2, f2, nil) wg.Wait() // -- Multiple lookups, with the first one failing. -- var wg1 sync.WaitGroup var wg2 sync.WaitGroup wg1.Add(1) wg2.Add(1) go func() { if f := reg.LookupFlow(id3, jiffy); f != nil { t.Error("expected lookup to fail") } wg1.Done() }() go func() { if f := reg.LookupFlow(id3, 10*jiffy); f != f3 { t.Error("couldn't lookup registered flow (with wait)") } wg2.Done() }() wg1.Wait() reg.RegisterFlow(id3, f3, nil) wg2.Wait() // -- Lookup with huge timeout, register in the meantime. -- go func() { time.Sleep(jiffy) reg.RegisterFlow(id4, f4, nil) }() // This should return in a jiffy. if f := reg.LookupFlow(id4, time.Hour); f != f4 { t.Error("couldn't lookup registered flow (with wait)") } }
func TestClusterFlow(t *testing.T) { defer leaktest.AfterTest(t)() const numRows = 100 args := base.TestClusterArgs{ReplicationMode: base.ReplicationManual} tc := serverutils.StartTestCluster(t, 3, args) defer tc.Stopper().Stop() sumDigitsFn := func(row int) parser.Datum { sum := 0 for row > 0 { sum += row % 10 row /= 10 } return parser.NewDInt(parser.DInt(sum)) } sqlutils.CreateTable(t, tc.ServerConn(0), "t", "num INT PRIMARY KEY, digitsum INT, numstr STRING, INDEX s (digitsum)", numRows, sqlutils.ToRowFn(sqlutils.RowIdxFn, sumDigitsFn, sqlutils.RowEnglishFn)) kvDB := tc.Server(0).KVClient().(*client.DB) desc := sqlbase.GetTableDescriptor(kvDB, "test", "t") makeIndexSpan := func(start, end int) TableReaderSpan { var span roachpb.Span prefix := roachpb.Key(sqlbase.MakeIndexKeyPrefix(desc, desc.Indexes[0].ID)) span.Key = append(prefix, encoding.EncodeVarintAscending(nil, int64(start))...) span.EndKey = append(span.EndKey, prefix...) span.EndKey = append(span.EndKey, encoding.EncodeVarintAscending(nil, int64(end))...) return TableReaderSpan{Span: span} } // Set up table readers on three hosts feeding data into a join reader on // the third host. This is a basic test for the distributed flow // infrastructure, including local and remote streams. // // Note that the ranges won't necessarily be local to the table readers, but // that doesn't matter for the purposes of this test. // Start a span (useful to look at spans using Lighstep). sp, err := tracing.JoinOrNew(tracing.NewTracer(), nil, "cluster test") if err != nil { t.Fatal(err) } ctx := opentracing.ContextWithSpan(context.Background(), sp) defer sp.Finish() tr1 := TableReaderSpec{ Table: *desc, IndexIdx: 1, OutputColumns: []uint32{0, 1}, Spans: []TableReaderSpan{makeIndexSpan(0, 8)}, } tr2 := TableReaderSpec{ Table: *desc, IndexIdx: 1, OutputColumns: []uint32{0, 1}, Spans: []TableReaderSpan{makeIndexSpan(8, 12)}, } tr3 := TableReaderSpec{ Table: *desc, IndexIdx: 1, OutputColumns: []uint32{0, 1}, Spans: []TableReaderSpan{makeIndexSpan(12, 100)}, } jr := JoinReaderSpec{ Table: *desc, OutputColumns: []uint32{2}, } txn := client.NewTxn(ctx, *kvDB) fid := FlowID{uuid.MakeV4()} req1 := &SetupFlowRequest{Txn: txn.Proto} req1.Flow = FlowSpec{ FlowID: fid, Processors: []ProcessorSpec{{ Core: ProcessorCoreUnion{TableReader: &tr1}, Output: []OutputRouterSpec{{ Type: OutputRouterSpec_MIRROR, Streams: []StreamEndpointSpec{ {StreamID: 0, Mailbox: &MailboxSpec{TargetAddr: tc.Server(2).ServingAddr()}}, }, }}, }}, } req2 := &SetupFlowRequest{Txn: txn.Proto} req2.Flow = FlowSpec{ FlowID: fid, Processors: []ProcessorSpec{{ Core: ProcessorCoreUnion{TableReader: &tr2}, Output: []OutputRouterSpec{{ Type: OutputRouterSpec_MIRROR, Streams: []StreamEndpointSpec{ {StreamID: 1, Mailbox: &MailboxSpec{TargetAddr: tc.Server(2).ServingAddr()}}, }, }}, }}, } req3 := &SetupFlowRequest{Txn: txn.Proto} req3.Flow = FlowSpec{ FlowID: fid, Processors: []ProcessorSpec{ { Core: ProcessorCoreUnion{TableReader: &tr3}, Output: []OutputRouterSpec{{ Type: OutputRouterSpec_MIRROR, Streams: []StreamEndpointSpec{ {StreamID: StreamID(2)}, }, }}, }, { Input: []InputSyncSpec{{ Type: InputSyncSpec_ORDERED, Ordering: Ordering{Columns: []Ordering_Column{{1, Ordering_Column_ASC}}}, Streams: []StreamEndpointSpec{ {StreamID: 0, Mailbox: &MailboxSpec{}}, {StreamID: 1, Mailbox: &MailboxSpec{}}, {StreamID: StreamID(2)}, }, }}, Core: ProcessorCoreUnion{JoinReader: &jr}, Output: []OutputRouterSpec{{ Type: OutputRouterSpec_MIRROR, Streams: []StreamEndpointSpec{{Mailbox: &MailboxSpec{SimpleResponse: true}}}, }}}, }, } if err := SetFlowRequestTrace(ctx, req1); err != nil { t.Fatal(err) } if err := SetFlowRequestTrace(ctx, req2); err != nil { t.Fatal(err) } if err := SetFlowRequestTrace(ctx, req3); err != nil { t.Fatal(err) } var clients []DistSQLClient for i := 0; i < 3; i++ { s := tc.Server(i) conn, err := s.RPCContext().GRPCDial(s.ServingAddr()) if err != nil { t.Fatal(err) } clients = append(clients, NewDistSQLClient(conn)) } if log.V(1) { log.Infof(ctx, "Setting up flow on 0") } if resp, err := clients[0].SetupFlow(ctx, req1); err != nil { t.Fatal(err) } else if resp.Error != nil { t.Fatal(resp.Error) } if log.V(1) { log.Infof(ctx, "Setting up flow on 1") } if resp, err := clients[1].SetupFlow(ctx, req2); err != nil { t.Fatal(err) } else if resp.Error != nil { t.Fatal(resp.Error) } if log.V(1) { log.Infof(ctx, "Running flow on 2") } stream, err := clients[2].RunSimpleFlow(ctx, req3) if err != nil { t.Fatal(err) } var decoder StreamDecoder var rows sqlbase.EncDatumRows for { msg, err := stream.Recv() if err != nil { if err == io.EOF { break } t.Fatal(err) } err = decoder.AddMessage(msg) if err != nil { t.Fatal(err) } rows = testGetDecodedRows(t, &decoder, rows) } if done, trailerErr := decoder.IsDone(); !done { t.Fatal("stream not done") } else if trailerErr != nil { t.Fatal("error in the stream trailer:", trailerErr) } // The result should be all the numbers in string form, ordered by the // digit sum (and then by number). var results []string for sum := 1; sum <= 50; sum++ { for i := 1; i <= numRows; i++ { if int(*sumDigitsFn(i).(*parser.DInt)) == sum { results = append(results, fmt.Sprintf("['%s']", sqlutils.IntToEnglish(i))) } } } expected := strings.Join(results, " ") expected = "[" + expected + "]" if rowStr := rows.String(); rowStr != expected { t.Errorf("Result: %s\n Expected: %s\n", rowStr, expected) } }
} var emptyTxn Transaction ts := hlc.ZeroTimestamp.Add(1, 2) emptyTxn.UpdateObservedTimestamp(NodeID(1), ts) if actTS, _ := emptyTxn.GetObservedTimestamp(NodeID(1)); !actTS.Equal(ts) { t.Fatalf("unexpected: %s (wanted %s)", actTS, ts) } } var nonZeroTxn = Transaction{ TxnMeta: enginepb.TxnMeta{ Isolation: enginepb.SNAPSHOT, Key: Key("foo"), ID: func() *uuid.UUID { u := uuid.MakeV4() return &u }(), Epoch: 2, Timestamp: makeTS(20, 21), Priority: 957356782, Sequence: 123, BatchIndex: 1, }, Name: "name", Status: COMMITTED, LastHeartbeat: &hlc.Timestamp{WallTime: 1, Logical: 2}, OrigTimestamp: makeTS(30, 31), MaxTimestamp: makeTS(40, 41), ObservedTimestamps: map[NodeID]hlc.Timestamp{1: makeTS(1, 2)}, Writing: true,
// TestTimestampCacheLayeredIntervals verifies the maximum timestamp // is chosen if previous entries have ranges which are layered over // each other. // // The test uses the layeredIntervalTestCase struct to allow reordering // of interval insertions while keeping each interval's timestamp fixed. // This can be used to verify that only the provided timestamp is used to // determine layering, and that the interval insertion order is irrelevant. func TestTimestampCacheLayeredIntervals(t *testing.T) { defer leaktest.AfterTest(t)() manual := hlc.NewManualClock(123) clock := hlc.NewClock(manual.UnixNano, time.Nanosecond) tc := newTimestampCache(clock) // Run each test case in several configurations. for testCaseIdx, testCase := range []layeredIntervalTestCase{ layeredIntervalTestCase1, layeredIntervalTestCase2, layeredIntervalTestCase3, layeredIntervalTestCase4, layeredIntervalTestCase5, } { t.Logf("test case %d", testCaseIdx+1) // In simultaneous runs, each span in the test case is given the // same time. Otherwise each gets a distinct timestamp (in the // order of definition). for _, simultaneous := range []bool{false, true} { t.Logf("simultaneous: %v", simultaneous) // In reverse runs, spans are inserted into the timestamp cache // out of order (so spans with higher timestamps are inserted // before those with lower timestamps). In simultaneous+reverse // runs, timestamps are all the same, but running in both // directions is still necessary to exercise all branches in the // code. for _, reverse := range []bool{false, true} { t.Logf("reverse: %v", reverse) // In sameTxn runs, all spans are inserted as a part of the // same transaction; otherwise each is a separate transaction. for _, sameTxn := range []bool{false, true} { t.Logf("sameTxn: %v", sameTxn) txns := make([]txnState, len(testCase.spans)) if sameTxn { id := uuid.MakeV4() for i := range testCase.spans { txns[i].id = &id } } else { for i := range testCase.spans { u := uuid.MakeV4() txns[i].id = &u } } tc.Clear(clock.Now()) if simultaneous { now := clock.Now() for i := range txns { txns[i].ts = now } } else { for i := range txns { txns[i].ts = clock.Now() } } if reverse { for i := len(testCase.spans) - 1; i >= 0; i-- { tc.add(testCase.spans[i].Key, testCase.spans[i].EndKey, txns[i].ts, txns[i].id, true) } } else { for i := range testCase.spans { tc.add(testCase.spans[i].Key, testCase.spans[i].EndKey, txns[i].ts, txns[i].id, true) } } testCase.validator(t, tc, txns) } } } } }
// TestSender mocks out some of the txn coordinator sender's // functionality. It responds to PutRequests using testPutResp. func newTestSender( pre, post func(roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error), ) SenderFunc { txnID := uuid.MakeV4() return func(_ context.Context, ba roachpb.BatchRequest) (*roachpb.BatchResponse, *roachpb.Error) { if ba.UserPriority == 0 { ba.UserPriority = 1 } if ba.Txn != nil && ba.Txn.ID == nil { ba.Txn.Key = txnKey ba.Txn.ID = &txnID } var br *roachpb.BatchResponse var pErr *roachpb.Error if pre != nil { br, pErr = pre(ba) } else { br = ba.CreateReply() } if pErr != nil { return nil, pErr } var writing bool status := roachpb.PENDING for i, req := range ba.Requests { args := req.GetInner() if _, ok := args.(*roachpb.PutRequest); ok { testPutRespCopy := testPutResp union := &br.Responses[i] // avoid operating on copy union.MustSetInner(&testPutRespCopy) } if roachpb.IsTransactionWrite(args) { writing = true } } if args, ok := ba.GetArg(roachpb.EndTransaction); ok { et := args.(*roachpb.EndTransactionRequest) writing = true if et.Commit { status = roachpb.COMMITTED } else { status = roachpb.ABORTED } } if ba.Txn != nil { txnClone := ba.Txn.Clone() br.Txn = &txnClone if pErr == nil { br.Txn.Writing = writing br.Txn.Status = status } } if post != nil { br, pErr = post(ba) } return br, pErr } }
func TestPrettyPrint(t *testing.T) { tm, _ := time.Parse(time.RFC3339Nano, "2016-03-30T13:40:35.053725008Z") duration := duration.Duration{Months: 1, Days: 1, Nanos: 1 * time.Second.Nanoseconds()} durationAsc, _ := encoding.EncodeDurationAscending(nil, duration) durationDesc, _ := encoding.EncodeDurationDescending(nil, duration) txnID := uuid.MakeV4() // The following test cases encode keys with a mixture of ascending and descending direction, // but always decode keys in the ascending direction. This is why some of the decoded values // seem bizarre. testCases := []struct { key roachpb.Key exp string }{ // local {StoreIdentKey(), "/Local/Store/storeIdent"}, {StoreGossipKey(), "/Local/Store/gossipBootstrap"}, {AbortCacheKey(roachpb.RangeID(1000001), txnID), fmt.Sprintf(`/Local/RangeID/1000001/r/AbortCache/%q`, txnID)}, {RaftTombstoneKey(roachpb.RangeID(1000001)), "/Local/RangeID/1000001/r/RaftTombstone"}, {RaftAppliedIndexKey(roachpb.RangeID(1000001)), "/Local/RangeID/1000001/r/RaftAppliedIndex"}, {LeaseAppliedIndexKey(roachpb.RangeID(1000001)), "/Local/RangeID/1000001/r/LeaseAppliedIndex"}, {RaftTruncatedStateKey(roachpb.RangeID(1000001)), "/Local/RangeID/1000001/r/RaftTruncatedState"}, {RangeLeaseKey(roachpb.RangeID(1000001)), "/Local/RangeID/1000001/r/RangeLease"}, {RangeStatsKey(roachpb.RangeID(1000001)), "/Local/RangeID/1000001/r/RangeStats"}, {RangeTxnSpanGCThresholdKey(roachpb.RangeID(1000001)), `/Local/RangeID/1000001/r/RangeTxnSpanGCThreshold`}, {RangeFrozenStatusKey(roachpb.RangeID(1000001)), "/Local/RangeID/1000001/r/RangeFrozenStatus"}, {RangeLastGCKey(roachpb.RangeID(1000001)), "/Local/RangeID/1000001/r/RangeLastGC"}, {RaftHardStateKey(roachpb.RangeID(1000001)), "/Local/RangeID/1000001/u/RaftHardState"}, {RaftLastIndexKey(roachpb.RangeID(1000001)), "/Local/RangeID/1000001/u/RaftLastIndex"}, {RaftLogKey(roachpb.RangeID(1000001), uint64(200001)), "/Local/RangeID/1000001/u/RaftLog/logIndex:200001"}, {RangeLastReplicaGCTimestampKey(roachpb.RangeID(1000001)), "/Local/RangeID/1000001/u/RangeLastReplicaGCTimestamp"}, {RangeLastVerificationTimestampKeyDeprecated(roachpb.RangeID(1000001)), "/Local/RangeID/1000001/u/RangeLastVerificationTimestamp"}, {MakeRangeKeyPrefix(roachpb.RKey("ok")), `/Local/Range/"ok"`}, {RangeDescriptorKey(roachpb.RKey("111")), `/Local/Range/"111"/RangeDescriptor`}, {TransactionKey(roachpb.Key("111"), txnID), fmt.Sprintf(`/Local/Range/"111"/Transaction/addrKey:/id:%q`, txnID)}, {LocalMax, `/Meta1/""`}, // LocalMax == Meta1Prefix // system {makeKey(Meta2Prefix, roachpb.Key("foo")), `/Meta2/"foo"`}, {makeKey(Meta1Prefix, roachpb.Key("foo")), `/Meta1/"foo"`}, {RangeMetaKey(roachpb.RKey("f")), `/Meta2/"f"`}, {NodeLivenessKey(10033), "/System/NodeLiveness/10033"}, {NodeStatusKey(1111), "/System/StatusNode/1111"}, {SystemMax, "/System/Max"}, // key of key {RangeMetaKey(roachpb.RKey(MakeRangeKeyPrefix(roachpb.RKey("ok")))), `/Meta2/Local/Range/"ok"`}, {RangeMetaKey(roachpb.RKey(makeKey(MakeTablePrefix(42), roachpb.RKey("foo")))), `/Meta2/Table/42/"foo"`}, {RangeMetaKey(roachpb.RKey(makeKey(Meta2Prefix, roachpb.Key("foo")))), `/Meta1/"foo"`}, // table {UserTableDataMin, "/Table/50"}, {MakeTablePrefix(111), "/Table/111"}, {makeKey(MakeTablePrefix(42), roachpb.RKey("foo")), `/Table/42/"foo"`}, {makeKey(MakeTablePrefix(42), roachpb.RKey(encoding.EncodeFloatAscending(nil, float64(233.221112)))), "/Table/42/233.221112"}, {makeKey(MakeTablePrefix(42), roachpb.RKey(encoding.EncodeFloatDescending(nil, float64(-233.221112)))), "/Table/42/233.221112"}, {makeKey(MakeTablePrefix(42), roachpb.RKey(encoding.EncodeFloatAscending(nil, math.Inf(1)))), "/Table/42/+Inf"}, {makeKey(MakeTablePrefix(42), roachpb.RKey(encoding.EncodeFloatAscending(nil, math.NaN()))), "/Table/42/NaN"}, {makeKey(MakeTablePrefix(42), roachpb.RKey(encoding.EncodeVarintAscending(nil, 1222)), roachpb.RKey(encoding.EncodeStringAscending(nil, "handsome man"))), `/Table/42/1222/"handsome man"`}, {makeKey(MakeTablePrefix(42), roachpb.RKey(encoding.EncodeVarintAscending(nil, 1222))), `/Table/42/1222`}, {makeKey(MakeTablePrefix(42), roachpb.RKey(encoding.EncodeVarintDescending(nil, 1222))), `/Table/42/-1223`}, {makeKey(MakeTablePrefix(42), roachpb.RKey(encoding.EncodeBytesAscending(nil, []byte{1, 2, 8, 255}))), `/Table/42/"\x01\x02\b\xff"`}, {makeKey(MakeTablePrefix(42), roachpb.RKey(encoding.EncodeBytesAscending(nil, []byte{1, 2, 8, 255})), roachpb.RKey("bar")), `/Table/42/"\x01\x02\b\xff"/"bar"`}, {makeKey(MakeTablePrefix(42), roachpb.RKey(encoding.EncodeBytesDescending(nil, []byte{1, 2, 8, 255})), roachpb.RKey("bar")), `/Table/42/"\x01\x02\b\xff"/"bar"`}, {makeKey(MakeTablePrefix(42), roachpb.RKey(encoding.EncodeNullAscending(nil))), "/Table/42/NULL"}, {makeKey(MakeTablePrefix(42), roachpb.RKey(encoding.EncodeNotNullAscending(nil))), "/Table/42/#"}, {makeKey(MakeTablePrefix(42), roachpb.RKey(encoding.EncodeTimeAscending(nil, tm))), "/Table/42/2016-03-30T13:40:35.053725008Z"}, {makeKey(MakeTablePrefix(42), roachpb.RKey(encoding.EncodeTimeDescending(nil, tm))), "/Table/42/1923-10-04T10:19:23.946274991Z"}, {makeKey(MakeTablePrefix(42), roachpb.RKey(encoding.EncodeDecimalAscending(nil, inf.NewDec(1234, 2)))), "/Table/42/12.34"}, {makeKey(MakeTablePrefix(42), roachpb.RKey(encoding.EncodeDecimalDescending(nil, inf.NewDec(1234, 2)))), "/Table/42/-12.34"}, {makeKey(MakeTablePrefix(42), roachpb.RKey(durationAsc)), "/Table/42/1m1d1s"}, {makeKey(MakeTablePrefix(42), roachpb.RKey(durationDesc)), "/Table/42/-2m-2d743h59m58.999999999s"}, // others {makeKey([]byte("")), "/Min"}, {Meta1KeyMax, "/Meta1/Max"}, {Meta2KeyMax, "/Meta2/Max"}, {makeKey(MakeTablePrefix(42), roachpb.RKey([]byte{0x12, 'a', 0x00, 0x02})), "/Table/42/<unknown escape sequence: 0x0 0x2>"}, } for i, test := range testCases { keyInfo := MassagePrettyPrintedSpanForTest(PrettyPrint(test.key), nil) exp := MassagePrettyPrintedSpanForTest(test.exp, nil) if exp != keyInfo { t.Errorf("%d: expected %s, got %s", i, exp, keyInfo) } if exp != MassagePrettyPrintedSpanForTest(test.key.String(), nil) { t.Errorf("%d: expected %s, got %s", i, exp, test.key.String()) } parsed, err := UglyPrint(keyInfo) if err != nil { if _, ok := err.(*errUglifyUnsupported); !ok { t.Errorf("%d: %s: %s", i, keyInfo, err) } else { t.Logf("%d: skipping parsing of %s; key is unsupported: %v", i, keyInfo, err) } } else if exp, act := test.key, parsed; !bytes.Equal(exp, act) { t.Errorf("%d: expected %q, got %q", i, exp, act) } if t.Failed() { return } } }
fn: func(ctx *EvalContext, args DTuple) (Datum, error) { s := string(*args[0].(*DString)) pattern := string(*args[1].(*DString)) escape := string(*args[2].(*DString)) return regexpExtract(ctx, s, pattern, escape) }, }, } var uuidV4Impl = Builtin{ Types: ArgTypes{}, ReturnType: TypeBytes, category: categoryIDGeneration, impure: true, fn: func(_ *EvalContext, args DTuple) (Datum, error) { return NewDBytes(DBytes(uuid.MakeV4().GetBytes())), nil }, } var ceilImpl = []Builtin{ floatBuiltin1(func(x float64) (Datum, error) { return NewDFloat(DFloat(math.Ceil(x))), nil }), decimalBuiltin1(func(x *inf.Dec) (Datum, error) { dd := &DDecimal{} dd.Round(x, 0, inf.RoundCeil) return dd, nil }), } var txnTSImpl = []Builtin{