Skip to content

Commit c44b357

Browse files
committed
kv: move range lease checks and transfers below latching
Needed for #57688. This commit reworks interactions between range leases and requests, pulling the consultation of a replica's lease down below the level of latching while keeping heavy-weight operations like lease acquisitions above the level of latching. Doing so comes with several benefits, some related specifically to non-blocking transactions and some more general. Background Before discussing the change here, let's discuss how lease checks, lease acquisitions, lease redirection, and lease transfers currently work. Today, requests consult a replica's range lease before acquiring latches. If the lease is good to go, the request proceeds to acquire latches. If the lease is not currently held by any replica, the lease is acquired (again, above latches) through a coalesced `RequestLeaseRequest`. If the lease is currently held by a different replica, the request is redirected to that replica using a `NotLeaseHolderError`. Finally, if the lease check notices a lease transfer in progress, the request is optimistically redirected to the prospective new leaseholder. This all works, but only because it's been around for so long. Due to the lease check above latching, we're forced to go to great lengths to get the synchronization with in-flight requests right, which leads to very subtle logic. This is most apparent with lease transfers, which properly synchronize with ongoing requests through a delicate dance with the HLC clock and some serious "spooky action at a distance". Every request bumps the local HLC clock in `Store.Send`, then grabs the replica mutex, checks for an ongoing lease transfer, drops the replica mutex, then evaluates. Lease transfers grab the replica mutex, grab a clock reading from the local HLC clock, bump the minLeaseProposedTS to stop using the current lease, drops the replica mutex, then proposes a new lease using this clock reading as its start time. This works only because each request bumps the HLC clock _before_ checking the lease, so the HLC clock can serve as an upper bound on every request that has made it through the lease check by the time the lease transfer begins. This structure is inflexible, subtle, and falls over as soon as we try to extend it. Motivation The primary motivation for pulling lease checks and transfers below latching is that the interaction between requests and lease transfers is incompatible with future-time operations, a key part of the non-blocking transaction project. This is because the structure relies on the HLC clock providing an upper bound on the time of any request served by an outgoing leaseholder, which is attached to lease transfers to ensure that the new leaseholder does not violate any request served on the old leaseholder. But this is quickly violated once we start serving future-time operations, which don't bump the HLC clock. So we quickly need to look elsewhere for this information. The obvious place to look for this information is the timestamp cache, which records the upper bound read time of each key span in a range, even if this upper bound time is synthetic. If we could scan the timestamp cache and attach the maximum read time to a lease transfer (through a new field, not as the lease start time), we'd be good. But this runs into a problem, because if we just read the timestamp cache under the lease transfer's lock, we can't be sure we didn't miss any in-progress operations that had passed the lease check previously but had not yet bumped the timestamp cache. Maybe they are still reading? So the custom locking quickly runs into problems (I said it was inflexible!). Solution The solution here is to stop relying on custom locking for lease transfers by pulling the lease check below latching and by pulling the determination of the transfer's start time below latching. This ensures that during a lease transfer, we don't only block new requests, but we also flush out in-flight requests. This means that by the time we look at the timestamp cache during the evaluation of a lease transfer, we know it has already been updated by any request that will be served under the current lease. This commit doesn't make the switch from consulting the HLC clock to consulting the timestamp cache during TransferLease request evaluation, but a future commit will. Other benefits Besides this primary change, a number of other benefits fall out of this restructuring. 1. we avoid relying on custom synchronization around leases, instead relying on more the more general latching mechanism. 2. we more closely aligns `TransferLeaseRequest` and `SubsumeRequest`, which now both grab clock readings during evaluation and will both need to forward their clock reading by the upper-bound of a range's portion of the timestamp cache. It makes sense that these two requests would be very similar, as both are responsible for renouncing the current leaseholder's powers and passing them elsewhere. 3. we more closely aligns the lease acquisition handling with the handling of `MergeInProgressError` by classifying a new `InvalidLeaseError` as a "concurrencyRetryError" (see isConcurrencyRetryError). This fits the existing structure of: grab latches, check range state, drop latches and wait if necessary, retry. 4. in doing so, we fuse the critical section of lease checks and the rest of the checks in `checkExecutionCanProceed`. So we grab the replica read lock one fewer time in the request path. 5. we move one step closer to a world where we can "ship a portion of the timestamp cache" during lease transfers (and range merges) to avoid retry errors / transaction aborts on the new leaseholder. This commit will be followed up by one that ships a very basic summary of a leaseholder's timestamp cache during lease transfers. However, this would now be trivial to extend with higher resolution information, given some size limit. Perhaps we prioritize the local portion of the timestamp cache to avoid txn aborts? 6. now that leases are checked below latching, we no longer have the potential for an arbitrary delay due to latching and waiting on locks between when the lease is checked and when a request evaluates, so we no longer need checks like [this](https://github.com/cockroachdb/cockroach/blob/7bcb2cef794da56f6993f1b27d5b6a036016242b/pkg/kv/kvserver/replica_write.go#L119). 7. we pull observed timestamp handling a layer down, which will be useful to address plumbing comments on #57077. Other behavioral changes There are two auxiliary behavioral changes made by this commit that deserve attention. The first is that during a lease transfer, operations now block on the outgoing leaseholder instead of immediately redirecting to the expected next leaseholder. This has trade-offs. On one hand, this delays redirection, which may make lease transfers more disruptive to ongoing traffic. On the other, we've seen in the past that the optimistic redirection is not an absolute win. In many cases, it can lead to thrashing and lots of wasted work, as the outgoing leaseholder and the incoming leaseholder both point at each other and requests ping-pong between them. We've seen this cause serious issues like #22837 and #32367, which we addressed by adding exponential backoff in the client in 89d349a. So while this change may make average-case latency during lease transfers slightly worse, it will keep things much more orderly, avoid wasted work, and reduce worse case latency during lease transfers. The other behavioral changes made by this commit is that observed timestamps are no longer applied to a request to reduce its MaxOffset until after latching and locking, instead of before. This sounds concerning, but it's actually not for two reasons. First, as of #57136, a transactions uncertainty interval is no longer considered by the lock table because locks in a transaction's uncertainty interval are no longer considered write-read conflicts. Instead, those locks' provisional values are considered at evaluation time to be uncertain. Second, the fact that the observed timestamp-limited MaxOffset was being used for latching is no longer correct in a world with synthetic timestamps (see #57077), so we would have had to make this change anyway. So put together, this behavioral change isn't meaningful.
1 parent 3c846bb commit c44b357

33 files changed

Lines changed: 1334 additions & 728 deletions

pkg/kv/kvserver/batcheval/cmd_lease_test.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ import (
1717

1818
"github.com/cockroachdb/cockroach/pkg/base"
1919
"github.com/cockroachdb/cockroach/pkg/roachpb"
20+
"github.com/cockroachdb/cockroach/pkg/storage"
21+
"github.com/cockroachdb/cockroach/pkg/testutils"
2022
"github.com/cockroachdb/cockroach/pkg/testutils/serverutils"
23+
"github.com/cockroachdb/cockroach/pkg/util/hlc"
2124
"github.com/cockroachdb/cockroach/pkg/util/leaktest"
2225
"github.com/cockroachdb/cockroach/pkg/util/log"
2326
"github.com/stretchr/testify/require"
@@ -120,10 +123,13 @@ func TestLeaseCommandLearnerReplica(t *testing.T) {
120123
}
121124
desc := roachpb.RangeDescriptor{}
122125
desc.SetReplicas(roachpb.MakeReplicaSet(replicas))
126+
manual := hlc.NewManualClock(123)
127+
clock := hlc.NewClock(manual.UnixNano, time.Nanosecond)
123128
cArgs := CommandArgs{
124129
EvalCtx: (&MockEvalCtx{
125130
StoreID: voterStoreID,
126131
Desc: &desc,
132+
Clock: clock,
127133
}).EvalContext(),
128134
Args: &roachpb.TransferLeaseRequest{
129135
Lease: roachpb.Lease{
@@ -157,6 +163,64 @@ func TestLeaseCommandLearnerReplica(t *testing.T) {
157163
require.EqualError(t, err, expForLearner)
158164
}
159165

166+
// TestLeaseTransferForwardsStartTime tests that during a lease transfer, the
167+
// start time of the new lease is determined during evaluation, after latches
168+
// have granted the lease transfer full mutual exclusion over the leaseholder.
169+
func TestLeaseTransferForwardsStartTime(t *testing.T) {
170+
defer leaktest.AfterTest(t)()
171+
defer log.Scope(t).Close(t)
172+
173+
testutils.RunTrueAndFalse(t, "epoch", func(t *testing.T, epoch bool) {
174+
ctx := context.Background()
175+
db := storage.NewDefaultInMem()
176+
defer db.Close()
177+
batch := db.NewBatch()
178+
defer batch.Close()
179+
180+
replicas := []roachpb.ReplicaDescriptor{
181+
{NodeID: 1, StoreID: 1, Type: roachpb.ReplicaTypeVoterFull(), ReplicaID: 1},
182+
{NodeID: 2, StoreID: 2, Type: roachpb.ReplicaTypeVoterFull(), ReplicaID: 2},
183+
}
184+
desc := roachpb.RangeDescriptor{}
185+
desc.SetReplicas(roachpb.MakeReplicaSet(replicas))
186+
manual := hlc.NewManualClock(123)
187+
clock := hlc.NewClock(manual.UnixNano, time.Nanosecond)
188+
189+
nextLease := roachpb.Lease{
190+
Replica: replicas[1],
191+
Start: clock.NowAsClockTimestamp(),
192+
}
193+
if epoch {
194+
nextLease.Epoch = 1
195+
} else {
196+
exp := nextLease.Start.ToTimestamp().Add(9*time.Second.Nanoseconds(), 0)
197+
nextLease.Expiration = &exp
198+
}
199+
cArgs := CommandArgs{
200+
EvalCtx: (&MockEvalCtx{
201+
StoreID: 1,
202+
Desc: &desc,
203+
Clock: clock,
204+
}).EvalContext(),
205+
Args: &roachpb.TransferLeaseRequest{
206+
Lease: nextLease,
207+
},
208+
}
209+
210+
manual.Increment(1000)
211+
beforeEval := clock.NowAsClockTimestamp()
212+
213+
res, err := TransferLease(ctx, batch, cArgs, nil)
214+
require.NoError(t, err)
215+
216+
// The proposed lease start time should be assigned at eval time.
217+
propLease := res.Replicated.State.Lease
218+
require.NotNil(t, propLease)
219+
require.True(t, nextLease.Start.Less(propLease.Start))
220+
require.True(t, beforeEval.Less(propLease.Start))
221+
})
222+
}
223+
160224
func TestCheckCanReceiveLease(t *testing.T) {
161225
defer leaktest.AfterTest(t)()
162226
defer log.Scope(t).Close(t)

pkg/kv/kvserver/batcheval/cmd_lease_transfer.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,22 @@ func TransferLease(
6767
// LeaseRejectedError before going through Raft.
6868
prevLease, _ := cArgs.EvalCtx.GetLease()
6969

70+
// Forward the lease's start time to a current clock reading. At this
71+
// point, we're holding latches across the entire range, we know that
72+
// this time is greater than the timestamps at which any request was
73+
// serviced by the leaseholder before it stopped serving requests (i.e.
74+
// before the TransferLease request acquired latches).
75+
newLease := args.Lease
76+
newLease.Start.Forward(cArgs.EvalCtx.Clock().NowAsClockTimestamp())
77+
args.Lease = roachpb.Lease{} // prevent accidental use below
78+
7079
// If this check is removed at some point, the filtering of learners on the
7180
// sending side would have to be removed as well.
72-
if err := roachpb.CheckCanReceiveLease(args.Lease.Replica, cArgs.EvalCtx.Desc()); err != nil {
81+
if err := roachpb.CheckCanReceiveLease(newLease.Replica, cArgs.EvalCtx.Desc()); err != nil {
7382
return newFailedLeaseTrigger(true /* isTransfer */), err
7483
}
7584

76-
log.VEventf(ctx, 2, "lease transfer: prev lease: %+v, new lease: %+v", prevLease, args.Lease)
85+
log.VEventf(ctx, 2, "lease transfer: prev lease: %+v, new lease: %+v", prevLease, newLease)
7786
return evalNewLease(ctx, cArgs.EvalCtx, readWriter, cArgs.Stats,
78-
args.Lease, prevLease, false /* isExtension */, true /* isTransfer */)
87+
newLease, prevLease, false /* isExtension */, true /* isTransfer */)
7988
}

pkg/kv/kvserver/batcheval/cmd_push_txn.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,8 @@ func PushTxn(
124124
return result.Result{}, errors.Errorf("request timestamp %s less than pushee txn timestamp %s", h.Timestamp, args.PusheeTxn.WriteTimestamp)
125125
}
126126
now := cArgs.EvalCtx.Clock().Now()
127+
// TODO(nvanbenschoten): remove this limitation. But when doing so,
128+
// keep the h.Timestamp.Less(args.PushTo) check above.
127129
if now.Less(h.Timestamp) {
128130
// The batch's timestamp should have been used to update the clock.
129131
return result.Result{}, errors.Errorf("request timestamp %s less than current clock time %s", h.Timestamp, now)

pkg/kv/kvserver/client_merge_test.go

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ import (
3434
"github.com/cockroachdb/cockroach/pkg/kv/kvserver"
3535
"github.com/cockroachdb/cockroach/pkg/kv/kvserver/concurrency/lock"
3636
"github.com/cockroachdb/cockroach/pkg/kv/kvserver/kvserverbase"
37-
"github.com/cockroachdb/cockroach/pkg/kv/kvserver/liveness"
3837
"github.com/cockroachdb/cockroach/pkg/kv/kvserver/rditer"
3938
"github.com/cockroachdb/cockroach/pkg/kv/kvserver/stateloader"
4039
"github.com/cockroachdb/cockroach/pkg/kv/kvserver/txnwait"
@@ -1373,21 +1372,22 @@ func TestStoreRangeMergeRHSLeaseExpiration(t *testing.T) {
13731372
}
13741373

13751374
// Install a hook to observe when a get or a put request for a special key,
1376-
// rhsSentinel, acquires latches and begins evaluating.
1375+
// rhsSentinel, hits a MergeInProgressError and begins waiting on the merge.
13771376
const reqConcurrency = 10
13781377
var rhsSentinel roachpb.Key
1379-
reqAcquiredLatch := make(chan struct{}, reqConcurrency)
1380-
testingLatchFilter := func(_ context.Context, ba roachpb.BatchRequest) *roachpb.Error {
1381-
for _, r := range ba.Requests {
1382-
req := r.GetInner()
1383-
switch req.Method() {
1384-
case roachpb.Get, roachpb.Put:
1385-
if req.Header().Key.Equal(rhsSentinel) {
1386-
reqAcquiredLatch <- struct{}{}
1378+
reqWaitingOnMerge := make(chan struct{}, reqConcurrency)
1379+
testingConcurrencyRetryFilter := func(_ context.Context, ba roachpb.BatchRequest, pErr *roachpb.Error) {
1380+
if _, ok := pErr.GetDetail().(*roachpb.MergeInProgressError); ok {
1381+
for _, r := range ba.Requests {
1382+
req := r.GetInner()
1383+
switch req.Method() {
1384+
case roachpb.Get, roachpb.Put:
1385+
if req.Header().Key.Equal(rhsSentinel) {
1386+
reqWaitingOnMerge <- struct{}{}
1387+
}
13871388
}
13881389
}
13891390
}
1390-
return nil
13911391
}
13921392

13931393
manualClock := hlc.NewHybridManualClock()
@@ -1401,8 +1401,9 @@ func TestStoreRangeMergeRHSLeaseExpiration(t *testing.T) {
14011401
ClockSource: manualClock.UnixNano,
14021402
},
14031403
Store: &kvserver.StoreTestingKnobs{
1404-
TestingRequestFilter: testingRequestFilter,
1405-
TestingLatchFilter: testingLatchFilter,
1404+
TestingRequestFilter: testingRequestFilter,
1405+
TestingConcurrencyRetryFilter: testingConcurrencyRetryFilter,
1406+
AllowLeaseRequestProposalsWhenNotLeader: true,
14061407
},
14071408
},
14081409
},
@@ -1416,6 +1417,7 @@ func TestStoreRangeMergeRHSLeaseExpiration(t *testing.T) {
14161417
// during the merge.
14171418
lhsDesc, rhsDesc, err := tc.Servers[0].ScratchRangeWithExpirationLeaseEx()
14181419
require.NoError(t, err)
1420+
rhsSentinel = rhsDesc.StartKey.AsRawKey()
14191421

14201422
tc.AddVotersOrFatal(t, lhsDesc.StartKey.AsRawKey(), tc.Target(1))
14211423
tc.AddVotersOrFatal(t, rhsDesc.StartKey.AsRawKey(), tc.Target(1))
@@ -1438,26 +1440,26 @@ func TestStoreRangeMergeRHSLeaseExpiration(t *testing.T) {
14381440
// is aware of the merge and is refusing all traffic, so we can't just send a
14391441
// TransferLease request. Instead, we need to expire the second store's lease,
14401442
// then acquire the lease on the first store.
1443+
toAdvance := store.GetStoreConfig().LeaseExpiration()
14411444

14421445
// Before doing so, however, ensure that the merge transaction has written
14431446
// its transaction record so that it doesn't run into trouble with the low
14441447
// water mark of the new leaseholder's timestamp cache. This could result in
14451448
// the transaction being inadvertently aborted during its first attempt,
14461449
// which this test is not designed to handle. If the merge transaction did
14471450
// abort then the get requests could complete on r2 before the merge retried.
1448-
hb, hbH := heartbeatArgs(mergeTxn, tc.Servers[0].Clock().Now())
1451+
//
1452+
// We heartbeat the merge's transaction record with a timestamp forwarded by
1453+
// the duration we plan to advance the clock by so that the transaction does
1454+
// not look expired even after the manual clock update.
1455+
afterAdvance := tc.Servers[0].Clock().Now().Add(toAdvance, 0)
1456+
hb, hbH := heartbeatArgs(mergeTxn, afterAdvance)
14491457
if _, pErr := kv.SendWrappedWith(ctx, store.TestSender(), hbH, hb); pErr != nil {
14501458
t.Fatal(pErr)
14511459
}
14521460

1453-
// Turn off liveness heartbeats on the second store, then advance the clock
1454-
// past the liveness expiration time. This expires all leases on all stores.
1455-
tc.Servers[1].NodeLiveness().(*liveness.NodeLiveness).PauseHeartbeatLoopForTest()
1456-
manualClock.Increment(store.GetStoreConfig().LeaseExpiration())
1457-
1458-
// Manually heartbeat the liveness on the first store to ensure it's
1459-
// considered live. The automatic heartbeat might not come for a while.
1460-
require.NoError(t, tc.HeartbeatNodeLiveness(0))
1461+
// Then increment the clock to expire all leases.
1462+
manualClock.Increment(toAdvance)
14611463

14621464
// Send several get and put requests to the RHS. The first of these to
14631465
// arrive will acquire the lease; the remaining requests will wait for that
@@ -1513,19 +1515,17 @@ func TestStoreRangeMergeRHSLeaseExpiration(t *testing.T) {
15131515
time.Sleep(time.Millisecond)
15141516
}
15151517

1516-
// Wait for the get and put requests to acquire latches, which is as far as
1517-
// they can get while the merge is in progress. Then wait a little bit
1518-
// longer. This tests that the requests really do get stuck waiting for the
1519-
// merge to complete without depending too heavily on implementation
1520-
// details.
1518+
// Wait for the get and put requests to begin waiting on the merge to
1519+
// complete. Then wait a little bit longer. This tests that the requests
1520+
// really do get stuck waiting for the merge to complete without depending
1521+
// too heavily on implementation details.
15211522
for i := 0; i < reqConcurrency; i++ {
15221523
select {
1523-
case <-reqAcquiredLatch:
1524-
// Latch acquired.
1524+
case <-reqWaitingOnMerge:
1525+
// Waiting on merge.
15251526
case pErr := <-reqErrs:
1526-
// Requests may never make it to the latch acquisition if s1 has not
1527-
// yet learned s2's lease is expired. Instead, we'll see a
1528-
// NotLeaseholderError.
1527+
// Requests may never wait on the merge if s1 has not yet learned
1528+
// s2's lease is expired. Instead, we'll see a NotLeaseholderError.
15291529
require.IsType(t, &roachpb.NotLeaseHolderError{}, pErr.GetDetail())
15301530
}
15311531
}

pkg/kv/kvserver/client_raft_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1089,7 +1089,7 @@ func TestRequestsOnLaggingReplica(t *testing.T) {
10891089
tc := testcluster.StartTestCluster(t, 3, clusterArgs)
10901090
defer tc.Stopper().Stop(ctx)
10911091

1092-
rngDesc, err := tc.Servers[0].ScratchRangeEx()
1092+
_, rngDesc, err := tc.Servers[0].ScratchRangeEx()
10931093
require.NoError(t, err)
10941094
key := rngDesc.StartKey.AsRawKey()
10951095
// Add replicas on all the stores.

pkg/kv/kvserver/client_replica_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3439,6 +3439,12 @@ func TestDiscoverIntentAcrossLeaseTransferAwayAndBack(t *testing.T) {
34393439
err = tc.MoveRangeLeaseNonCooperatively(rangeDesc, tc.Target(1), manual)
34403440
require.NoError(t, err)
34413441

3442+
// Send an arbitrary request to the range to update the range descriptor
3443+
// cache with the new lease. This prevents the rollback from getting stuck
3444+
// waiting on latches held by txn2's read on the old leaseholder.
3445+
_, err = kvDB.Get(ctx, "c")
3446+
require.NoError(t, err)
3447+
34423448
// Roll back txn1.
34433449
err = txn1.Rollback(ctx)
34443450
require.NoError(t, err)

pkg/kv/kvserver/concurrency/concurrency_manager.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ func NewManager(cfg Config) Manager {
8989
lt: lt,
9090
ltw: &lockTableWaiterImpl{
9191
st: cfg.Settings,
92+
clock: cfg.Clock,
9293
stopper: cfg.Stopper,
9394
ir: cfg.IntentResolver,
9495
lt: lt,
@@ -443,9 +444,10 @@ func (g *Guard) HoldingLatches() bool {
443444
return g != nil && g.lg != nil
444445
}
445446

446-
// AssertLatches asserts that the guard is non-nil and holding latches.
447+
// AssertLatches asserts that the guard is non-nil and holding latches, if the
448+
// request is supposed to hold latches while evaluating in the first place.
447449
func (g *Guard) AssertLatches() {
448-
if !g.HoldingLatches() {
450+
if shouldAcquireLatches(g.Req) && !g.HoldingLatches() {
449451
panic("expected latches held, found none")
450452
}
451453
}

pkg/kv/kvserver/concurrency/concurrency_manager_test.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ import (
7373
// debug-latch-manager
7474
// debug-lock-table
7575
// debug-disable-txn-pushes
76+
// debug-set-clock ts=<secs>
7677
// reset
7778
//
7879
func TestConcurrencyManagerBasic(t *testing.T) {
@@ -119,7 +120,6 @@ func TestConcurrencyManagerBasic(t *testing.T) {
119120
ReadTimestamp: ts,
120121
MaxTimestamp: maxTS,
121122
}
122-
txn.UpdateObservedTimestamp(c.nodeDesc.NodeID, ts.UnsafeToClockTimestamp())
123123
c.registerTxn(txnName, txn)
124124
return ""
125125

@@ -459,6 +459,17 @@ func TestConcurrencyManagerBasic(t *testing.T) {
459459
c.disableTxnPushes()
460460
return ""
461461

462+
case "debug-set-clock":
463+
var secs int
464+
d.ScanArgs(t, "ts", &secs)
465+
466+
nanos := int64(secs) * time.Second.Nanoseconds()
467+
if nanos < c.manual.UnixNano() {
468+
d.Fatalf(t, "manual clock must advance")
469+
}
470+
c.manual.Set(nanos)
471+
return ""
472+
462473
case "reset":
463474
if n := mon.numMonitored(); n > 0 {
464475
d.Fatalf(t, "%d requests still in flight", n)
@@ -494,6 +505,8 @@ type cluster struct {
494505
nodeDesc *roachpb.NodeDescriptor
495506
rangeDesc *roachpb.RangeDescriptor
496507
st *clustersettings.Settings
508+
manual *hlc.ManualClock
509+
clock *hlc.Clock
497510
m concurrency.Manager
498511

499512
// Definitions.
@@ -523,10 +536,13 @@ type txnPush struct {
523536
}
524537

525538
func newCluster() *cluster {
539+
manual := hlc.NewManualClock(123 * time.Second.Nanoseconds())
526540
return &cluster{
527-
st: clustersettings.MakeTestingClusterSettings(),
528541
nodeDesc: &roachpb.NodeDescriptor{NodeID: 1},
529542
rangeDesc: &roachpb.RangeDescriptor{RangeID: 1},
543+
st: clustersettings.MakeTestingClusterSettings(),
544+
manual: manual,
545+
clock: hlc.NewClock(manual.UnixNano, time.Nanosecond),
530546

531547
txnsByName: make(map[string]*roachpb.Transaction),
532548
requestsByName: make(map[string]testReq),
@@ -541,6 +557,7 @@ func (c *cluster) makeConfig() concurrency.Config {
541557
NodeDesc: c.nodeDesc,
542558
RangeDesc: c.rangeDesc,
543559
Settings: c.st,
560+
Clock: c.clock,
544561
IntentResolver: c,
545562
OnContentionEvent: func(ev *roachpb.ContentionEvent) {
546563
ev.Duration = 1234 * time.Millisecond // for determinism

0 commit comments

Comments
 (0)