Skip to content

Commit

Permalink
roachpb: use LocalUncertaintyLimit from err in readWithinUncertaintyI…
Browse files Browse the repository at this point in the history
…ntervalRetryTimestamp

Fixes #57685.

This commit updates readWithinUncertaintyIntervalRetryTimestamp to use the the
LocalUncertaintyLimit field from ReadWithinUncertaintyIntervalError in place of
ObservedTimestamps directly in readWithinUncertaintyIntervalRetryTimestamp. This
addresses a bug where ReadWithinUncertaintyIntervalErrors thrown by follower
replicas during follower reads would use meaningless observed timestamps.

Release note: None
  • Loading branch information
nvanbenschoten committed Feb 11, 2021
1 parent ff2a625 commit 0632a64
Showing 1 changed file with 29 additions and 23 deletions.
52 changes: 29 additions & 23 deletions pkg/roachpb/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -1403,8 +1403,7 @@ func PrepareTransactionForRetry(
// Use the priority communicated back by the server.
txn.Priority = errTxnPri
case *ReadWithinUncertaintyIntervalError:
txn.WriteTimestamp.Forward(
readWithinUncertaintyIntervalRetryTimestamp(ctx, &txn, tErr, pErr.OriginNode))
txn.WriteTimestamp.Forward(readWithinUncertaintyIntervalRetryTimestamp(tErr))
case *TransactionPushError:
// Increase timestamp if applicable, ensuring that we're just ahead of
// the pushee.
Expand Down Expand Up @@ -1465,34 +1464,41 @@ func CanTransactionRefresh(ctx context.Context, pErr *Error) (bool, *Transaction
// to the key that generated the error.
timestamp.Forward(writeTooOldRetryTimestamp(err))
case *ReadWithinUncertaintyIntervalError:
timestamp.Forward(
readWithinUncertaintyIntervalRetryTimestamp(ctx, txn, err, pErr.OriginNode))
timestamp.Forward(readWithinUncertaintyIntervalRetryTimestamp(err))
default:
return false, nil
}
return PrepareTransactionForRefresh(txn, timestamp)
}

func readWithinUncertaintyIntervalRetryTimestamp(
ctx context.Context, txn *Transaction, err *ReadWithinUncertaintyIntervalError, origin NodeID,
) hlc.Timestamp {
// If the reader encountered a newer write within the uncertainty
// interval, we advance the txn's timestamp just past the last observed
// timestamp from the node.
func readWithinUncertaintyIntervalRetryTimestamp(err *ReadWithinUncertaintyIntervalError) hlc.Timestamp {
// If the reader encountered a newer write within the uncertainty interval,
// we advance the txn's timestamp just past the uncertain value's timestamp.
// This ensures that we read above the uncertain value on a retry.
ts := err.ExistingTimestamp.Next()
// In addition to advancing past the uncertainty value's timestamp, we also
// advance the txn's timestamp up to the local uncertainty limit on the node
// which hit the error. This ensures that no future read after the retry on
// this node (ignoring lease complications in ComputeLocalUncertaintyLimit
// and values with synthetic timestamps) will throw an uncertainty error,
// even when reading other keys.
//
// TODO(nvanbenschoten): how is this supposed to work for follower reads?
// This is tracked in #57685. We can now use the LocalUncertaintyLimit in
// place of clockTS, which handles follower reads correctly.
clockTS, ok := txn.GetObservedTimestamp(origin)
if !ok {
log.Fatalf(ctx,
"missing observed timestamp for node %d found on uncertainty restart. "+
"err: %s. txn: %s. Observed timestamps: %v",
origin, err, txn, txn.ObservedTimestamps)
}
// Also forward by the existing timestamp.
ts := clockTS.ToTimestamp()
ts.Forward(err.ExistingTimestamp.Next())
// Note that if the request was not able to establish a local uncertainty
// limit due to a missing observed timestamp (for instance, if the request
// was evaluated on a follower replica and the txn had never visited the
// leaseholder), then LocalUncertaintyLimit will be empty and the Forward
// will be a no-op. In this case, we could advance all the way past the
// global uncertainty limit, but this time would likely be in the future, so
// this would necessitate a commit-wait period after committing.
//
// In general, we expect the local uncertainty limit, if set, to be above
// the uncertainty value's timestamp. So we expect this Forward to advance
// ts. However, this is not always the case. The one exception is if the
// uncertain value had a synthetic timestamp, so it was compared against the
// global uncertainty limit to determine uncertainty (see IsUncertain). In
// such cases, we're ok advancing just past the value's timestamp. Either
// way, we won't see the same value in our uncertainty interval on a retry.
ts.Forward(err.LocalUncertaintyLimit)
return ts
}

Expand Down

0 comments on commit 0632a64

Please sign in to comment.