From a3b9f67fddad66a464e8141cc92180dd8bcbda31 Mon Sep 17 00:00:00 2001 From: Nathan VanBenschoten Date: Tue, 12 Sep 2023 11:29:23 -0400 Subject: [PATCH] storage: release replicated locks during intent resolution Fixes #109648. Informs #100193. This commit adds support for releasing replicated locks during point and ranged intent resolution, if any are found. Replicated locks are released if the resolving transaction is finalized or if resolving transaction is pending and the lock has been rolled back through an epoch bump or savepoint rollback. Release note: None --- pkg/storage/mvcc.go | 154 ++++++++--- pkg/storage/mvcc_history_test.go | 22 +- .../testdata/mvcc_histories/replicated_locks | 261 +++++++++++++++++- .../mvcc_histories/resolve_intent_pagination | 65 +++++ .../testdata/mvcc_histories/skip_locked | 2 - 5 files changed, 445 insertions(+), 59 deletions(-) diff --git a/pkg/storage/mvcc.go b/pkg/storage/mvcc.go index a35cfd9331df..77d0c6bcaca8 100644 --- a/pkg/storage/mvcc.go +++ b/pkg/storage/mvcc.go @@ -4574,6 +4574,7 @@ func MVCCResolveWriteIntent( if err := ltIter.ValueProto(&buf.meta); err != nil { return false, 0, nil, errors.Wrap(err, "unmarshaling lock table value") } + var strOk bool if str == lock.Intent { // Intent resolution requires an MVCC iterator to look up the MVCC // version associated with the intent. Create one. @@ -4588,21 +4589,21 @@ func MVCCResolveWriteIntent( } defer iter.Close() } - ok, err = mvccResolveWriteIntent(ctx, rw, iter, ms, intent, &buf.meta, buf) + strOk, err = mvccResolveWriteIntent(ctx, rw, iter, ms, intent, &buf.meta, buf) } else { - // TODO(nvanbenschoten): implement. - _ = str + strOk, err = mvccReleaseLockInternal(ctx, rw, ms, intent, str, &buf.meta, buf) } if err != nil { return false, 0, nil, err } + ok = ok || strOk } numBytes = int64(rw.BufferedSize() - beforeBytes) return ok, numBytes, nil, nil } // With the separated lock table, we are employing a performance optimization: -// when an intent metadata is removed, we preferably want to do so using a +// when a lock's metadata value is removed, we preferably want to do so using a // SingleDel (as opposed to a Del). This is only safe if the previous operations // on the metadata key allow it. Due to practical limitations, at the time of // writing the condition we need is that the pebble history of the key consists @@ -4613,7 +4614,7 @@ func MVCCResolveWriteIntent( // It is difficult to track the history of engine writes to a key precisely, in // particular when values are ever aborted. So we apply the optimization only to // the main case in which it is useful, namely that of a transaction committing -// its intent that it never re-wrote in the initial epoch (i.e. no chance of it +// its lock that it never re-wrote in the initial epoch (i.e. no chance of it // ever being removed before as part of being pushed). Note that when a txn // refreshes, it stays in the original epoch, and the intents are moved, which // does *not* cause a write to the MVCC metadata key (for which the history has @@ -4630,7 +4631,7 @@ func MVCCResolveWriteIntent( // INSERT INTO kv VALUES(1, 2); // COMMIT; // -// This would first remove the intent (1,1) during the ROLLBACK using a Del (the +// This would first remove the lock (1,1) during the ROLLBACK using a Del (the // anomaly below would occur the same if a SingleDel were used here), and thus // without an additional condition the INSERT (1,2) would be eligible for // committing via a SingleDel. This has to be avoided as well, since the @@ -4651,7 +4652,7 @@ func MVCCResolveWriteIntent( // ↓ // - Set // -// which means that a previously deleted intent metadata would erroneously +// which means that a previously deleted lock metadata would erroneously // become visible again. So on top of restricting SingleDel to the COMMIT case, // we also restrict it to the case of having no ignored sequence number ranges // (i.e. no nested txn was rolled back before the commit). @@ -4676,22 +4677,22 @@ func (h singleDelOptimizationHelper) v() bool { return *h._didNotUpdateMeta } -// onCommitIntent returns true if the SingleDel optimization is available -// for committing an intent. -func (h singleDelOptimizationHelper) onCommitIntent() bool { - // We're committing the intent at epoch zero, the meta tracking says we didn't - // rewrite the intent, and we also didn't previously remove the metadata for +// onCommitLock returns true if the SingleDel optimization is available +// for committing a lock/intent. +func (h singleDelOptimizationHelper) onCommitLock() bool { + // We're committing the lock at epoch zero, the meta tracking says we didn't + // rewrite the lock, and we also didn't previously remove the metadata for // this key as part of a voluntary rollback of a nested txn. So we are safe to // use a SingleDel here. return h.v() && !h._hasIgnoredSeqs && h._epoch == 0 } -// onAbortIntent returns true if the SingleDel optimization is available -// for removing an intent. It is always false. -// Note that "removing an intent" can occur if we know that the epoch +// onAbortLock returns true if the SingleDel optimization is available +// for removing a lock/intent. It is always false. +// Note that "removing a lock" can occur if we know that the epoch // changed, or when a savepoint is rolled back. It does not imply that // the transaction aborted. -func (h singleDelOptimizationHelper) onAbortIntent() bool { +func (h singleDelOptimizationHelper) onAbortLock() bool { return false } @@ -4708,7 +4709,7 @@ func mvccResolveWriteIntent( writer Writer, iter MVCCIterator, ms *enginepb.MVCCStats, - // TODO(nvanbenschoten): rename this field to "resolution". + // TODO(nvanbenschoten): rename this field to "update". intent roachpb.LockUpdate, meta *enginepb.MVCCMetadata, buf *putBuffer, @@ -4967,7 +4968,7 @@ func mvccResolveWriteIntent( writer, metaKey, lock.Intent, newMeta, true /* alreadyExists */) } else { metaKeySize, metaValSize, err = buf.clearLockMeta( - writer, metaKey, lock.Intent, canSingleDelHelper.onCommitIntent(), meta.Txn.ID, ClearOptions{ + writer, metaKey, lock.Intent, canSingleDelHelper.onCommitLock(), meta.Txn.ID, ClearOptions{ ValueSizeKnown: true, ValueSize: uint32(origMetaValSize), }) @@ -5079,7 +5080,7 @@ func mvccResolveWriteIntent( if !ok { // If there is no other version, we should just clean up the key entirely. _, _, err := buf.clearLockMeta( - writer, metaKey, lock.Intent, canSingleDelHelper.onAbortIntent(), meta.Txn.ID, ClearOptions{ + writer, metaKey, lock.Intent, canSingleDelHelper.onAbortLock(), meta.Txn.ID, ClearOptions{ ValueSizeKnown: true, ValueSize: uint32(origMetaValSize), }) @@ -5101,7 +5102,7 @@ func mvccResolveWriteIntent( ValBytes: int64(nextValueLen), } metaKeySize, metaValSize, err := buf.clearLockMeta( - writer, metaKey, lock.Intent, canSingleDelHelper.onAbortIntent(), meta.Txn.ID, ClearOptions{ + writer, metaKey, lock.Intent, canSingleDelHelper.onAbortLock(), meta.Txn.ID, ClearOptions{ ValueSizeKnown: true, ValueSize: uint32(origMetaValSize), }) @@ -5240,6 +5241,7 @@ func MVCCResolveWriteIntentRange( intent.EndKey = nil var lastResolvedKey roachpb.Key + var lastResolvedKeyOk bool for valid, err := ltIter.SeekEngineKeyGE(EngineKey{Key: ltStart}); ; valid, err = ltIter.NextEngineKey() { if err != nil { return 0, 0, nil, 0, errors.Wrap(err, "seeking lock table") @@ -5247,21 +5249,7 @@ func MVCCResolveWriteIntentRange( // No more intents in the given range. break } - keysExceeded = opts.MaxKeys > 0 && numKeys == opts.MaxKeys - bytesExceeded = opts.TargetBytes > 0 && numBytes >= opts.TargetBytes - if keysExceeded || bytesExceeded { - if keysExceeded { - resumeReason = kvpb.RESUME_KEY_LIMIT - } else if bytesExceeded { - resumeReason = kvpb.RESUME_BYTE_LIMIT - } - // We could also compute a tighter nextKey here if we wanted to. - // TODO(nvanbenschoten): this resumeSpan won't be correct if there - // are multiple locks on the same key. What if only of the locks for - // a key are removed? Fix this by resolving zero or all locks on a - // given key. - return numKeys, numBytes, &roachpb.Span{Key: lastResolvedKey.Next(), EndKey: intentEndKey}, resumeReason, nil - } + ltEngineKey, err := ltIter.EngineKey() if err != nil { return 0, 0, nil, 0, errors.Wrap(err, "retrieving lock table key") @@ -5270,29 +5258,58 @@ func MVCCResolveWriteIntentRange( if err != nil { return 0, 0, nil, 0, errors.Wrap(err, "decoding lock table key") } + sameLockedKey := lastResolvedKey.Equal(ltKey.Key) + if !sameLockedKey { + // If this is not the same locked key as the last iteration, check + // whether we've exceeded the max keys or bytes limit. We don't check in + // between locks with different strengths on the same key because we + // can't encode a resume span that would be correct in that case. A + // transaction can only hold up to 3 locks on any given key, so this + // will never lead to us significantly overshooting the TargetBytes + // limit. We also only count each unique locked key once towards the + // MaxKeys limit, so this will never lead to us overshooting the MaxKeys + // limit at all. + keysExceeded = opts.MaxKeys > 0 && numKeys == opts.MaxKeys + bytesExceeded = opts.TargetBytes > 0 && numBytes >= opts.TargetBytes + if keysExceeded || bytesExceeded { + if keysExceeded { + resumeReason = kvpb.RESUME_KEY_LIMIT + } else if bytesExceeded { + resumeReason = kvpb.RESUME_BYTE_LIMIT + } + // We could also compute a tighter nextKey here if we wanted to. + resumeSpan := &roachpb.Span{Key: lastResolvedKey.Next(), EndKey: intentEndKey} + return numKeys, numBytes, resumeSpan, resumeReason, nil + } + + // Copy the underlying bytes of the unsafe key. This is needed for + // stability of the key to check for sameLockedKey and to construct + // a resume span on subsequent iteration. + lastResolvedKey = append(lastResolvedKey[:0], ltKey.Key...) + lastResolvedKeyOk = false + } if ltKey.TxnUUID != intent.Txn.ID { return 0, 0, nil, 0, errors.AssertionFailedf( "unexpected txnID %v != %v while scanning lock table", ltKey.TxnUUID, intent.Txn.ID) } + intent.Key = ltKey.Key if err := ltIter.ValueProto(&buf.meta); err != nil { return 0, 0, nil, 0, errors.Wrap(err, "unmarshaling lock table value") } - // Copy the underlying bytes of the unsafe key. This is needed for - // stability of the key to construct a resume span on subsequent - // iteration. - intent.Key = ltKey.Key - lastResolvedKey = append(lastResolvedKey[:0], intent.Key...) beforeBytes := rw.BufferedSize() var ok bool if ltKey.Strength == lock.Intent { ok, err = mvccResolveWriteIntent(ctx, rw, mvccIter, ms, intent, &buf.meta, buf) } else { - // TODO(nvanbenschoten): implement. - _ = ltKey.Strength + ok, err = mvccReleaseLockInternal(ctx, rw, ms, intent, ltKey.Strength, &buf.meta, buf) } if err != nil { log.Warningf(ctx, "failed to resolve intent for key %q: %+v", lastResolvedKey, err) - } else if ok { + } + if ok && !lastResolvedKeyOk { + // We only count the first successfully resolved lock/intent on a + // given key towards the returned key count and key limit. + lastResolvedKeyOk = true numKeys++ } numBytes += int64(rw.BufferedSize() - beforeBytes) @@ -5458,6 +5475,57 @@ func validateLockAcquisition(txn *roachpb.Transaction, str lock.Strength) error return nil } +// mvccReleaseLockInternal releases a lock at the specified key and strength and +// by the specified transaction. The function accepts the instructions for how +// to release the lock (encoded in the LockUpdate), and the current value of the +// lock (meta). +func mvccReleaseLockInternal( + ctx context.Context, + writer Writer, + ms *enginepb.MVCCStats, + update roachpb.LockUpdate, + str lock.Strength, + meta *enginepb.MVCCMetadata, + buf *putBuffer, +) (bool, error) { + finalized := update.Status.IsFinalized() + rolledBack := meta.Txn.Epoch < update.Txn.Epoch || + (meta.Txn.Epoch == update.Txn.Epoch && enginepb.TxnSeqIsIgnored(meta.Txn.Sequence, update.IgnoredSeqNums)) + release := finalized || rolledBack + if !release { + return false, nil + } + + canSingleDelHelper := singleDelOptimizationHelper{ + _didNotUpdateMeta: meta.TxnDidNotUpdateMeta, + _hasIgnoredSeqs: len(update.IgnoredSeqNums) > 0, + _epoch: update.Txn.Epoch, + } + var txnDidNotUpdateMeta bool + if update.Status == roachpb.COMMITTED && !rolledBack { + txnDidNotUpdateMeta = canSingleDelHelper.onCommitLock() + } else { + txnDidNotUpdateMeta = canSingleDelHelper.onAbortLock() + } + + metaKey := MakeMVCCMetadataKey(update.Key) + origMetaKeySize := int64(metaKey.EncodedSize()) + origMetaValSize := int64(meta.Size()) + keyBytes, valBytes, err := buf.clearLockMeta(writer, metaKey, str, txnDidNotUpdateMeta, meta.Txn.ID, ClearOptions{ + ValueSizeKnown: true, + ValueSize: uint32(meta.Size()), + }) + if err != nil { + return false, err + } + + // TODO(nvanbenschoten): handle MVCCStats update after addressing #109645. + _, _, _, _, _ = ms, origMetaKeySize, origMetaValSize, keyBytes, valBytes + + return true, nil + +} + // MVCCGarbageCollect creates an iterator on the ReadWriter. In parallel // it iterates through the keys listed for garbage collection by the // keys slice. The iterator is seeked in turn to each listed diff --git a/pkg/storage/mvcc_history_test.go b/pkg/storage/mvcc_history_test.go index a1ec2c66b902..b98435d536db 100644 --- a/pkg/storage/mvcc_history_test.go +++ b/pkg/storage/mvcc_history_test.go @@ -379,6 +379,10 @@ func TestMVCCHistories(t *testing.T) { if err != nil { return errors.Wrapf(err, "decoding LockTable key: %v", eKey) } + if ltKey.Strength == lock.Intent { + // Ignore intents, which are reported by reportDataEntries. + continue + } // Unmarshal. v, err := iter.UnsafeValue() if err != nil { @@ -601,11 +605,11 @@ func TestMVCCHistories(t *testing.T) { } cmd := e.getCmd() - txnChange = txnChange || cmd.typ == typTxnUpdate - dataChange = dataChange || cmd.typ == typDataUpdate - locksChange = locksChange || cmd.typ == typLocksUpdate + txnChange = txnChange || cmd.typ&typTxnUpdate != 0 + dataChange = dataChange || cmd.typ&typDataUpdate != 0 + locksChange = locksChange || cmd.typ&typLocksUpdate != 0 - if trace || (stats && cmd.typ == typDataUpdate) { + if trace || (stats && cmd.typ&typDataUpdate != 0) { // If tracing is also requested by the datadriven input, // we'll trace the statement in the actual results too. buf.Printf(">> %s", d.Cmd) @@ -638,10 +642,10 @@ func TestMVCCHistories(t *testing.T) { // If tracing is enabled, we report the intermediate results // after each individual step in the script. // This may modify foundErr too. - reportResults(cmd.typ == typTxnUpdate, cmd.typ == typDataUpdate, cmd.typ == typLocksUpdate) + reportResults(cmd.typ&typTxnUpdate != 0, cmd.typ&typDataUpdate != 0, cmd.typ&typLocksUpdate != 0) } - if stats && cmd.typ == typDataUpdate { + if stats && cmd.typ&typDataUpdate != 0 { // If stats are enabled, emit evaluated stats returned by the // command, and compare them with the real computed stats diff. var msEngineDiff enginepb.MVCCStats @@ -753,7 +757,7 @@ type cmd struct { type cmdType int const ( - typReadOnly cmdType = iota + typReadOnly cmdType = 1 << iota typTxnUpdate typDataUpdate typLocksUpdate @@ -770,8 +774,8 @@ var commands = map[string]cmd{ "txn_step": {typTxnUpdate, cmdTxnStep}, "txn_update": {typTxnUpdate, cmdTxnUpdate}, - "resolve_intent": {typDataUpdate, cmdResolveIntent}, - "resolve_intent_range": {typDataUpdate, cmdResolveIntentRange}, + "resolve_intent": {typDataUpdate | typLocksUpdate, cmdResolveIntent}, + "resolve_intent_range": {typDataUpdate | typLocksUpdate, cmdResolveIntentRange}, "check_intent": {typReadOnly, cmdCheckIntent}, "add_unreplicated_lock": {typLocksUpdate, cmdAddUnreplicatedLock}, "check_for_acquire_lock": {typReadOnly, cmdCheckForAcquireLock}, diff --git a/pkg/storage/testdata/mvcc_histories/replicated_locks b/pkg/storage/testdata/mvcc_histories/replicated_locks index 3f7a3093f03a..090182341ffb 100644 --- a/pkg/storage/testdata/mvcc_histories/replicated_locks +++ b/pkg/storage/testdata/mvcc_histories/replicated_locks @@ -241,7 +241,6 @@ lock (Replicated): "k1"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri lock (Replicated): "k2"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false lock (Replicated): "k2"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false lock (Replicated): "k3"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false -lock (Replicated): "k4"/Intent -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=10.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true run error check_for_acquire_lock t=B k=k4 str=shared @@ -257,7 +256,6 @@ lock (Replicated): "k1"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri lock (Replicated): "k2"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false lock (Replicated): "k2"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false lock (Replicated): "k3"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false -lock (Replicated): "k4"/Intent -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=10.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true error: (*kvpb.LockConflictError:) conflicting locks on "k4" # The intent history is considered when determining whether a reacquisition is @@ -288,7 +286,6 @@ lock (Replicated): "k1"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri lock (Replicated): "k2"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false lock (Replicated): "k2"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false lock (Replicated): "k3"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false -lock (Replicated): "k4"/Intent -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=2} ts=10.000000000,0 del=false klen=12 vlen=13 ih={{1 /BYTES/v4}} mergeTs= txnDidNotUpdateMeta=false run with t=A @@ -302,7 +299,6 @@ lock (Replicated): "k1"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri lock (Replicated): "k2"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false lock (Replicated): "k2"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false lock (Replicated): "k3"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false -lock (Replicated): "k4"/Intent -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=2} ts=10.000000000,0 del=false klen=12 vlen=13 ih={{1 /BYTES/v4}} mergeTs= txnDidNotUpdateMeta=false run with t=A @@ -316,7 +312,6 @@ lock (Replicated): "k1"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri lock (Replicated): "k2"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false lock (Replicated): "k2"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false lock (Replicated): "k3"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false -lock (Replicated): "k4"/Intent -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=2} ts=10.000000000,0 del=false klen=12 vlen=13 ih={{1 /BYTES/v4}} mergeTs= txnDidNotUpdateMeta=false lock (Replicated): "k4"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=3} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true # Replicated locks are ignored by non-locking scans by any transaction. Note @@ -512,3 +507,259 @@ data: "k3"/10.000000000,0 -> / data: "k3"/5.000000000,0 -> /BYTES/v3 meta: "k4"/0,0 -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=2} ts=10.000000000,0 del=false klen=12 vlen=13 ih={{1 /BYTES/v4}} mergeTs= txnDidNotUpdateMeta=false data: "k4"/10.000000000,0 -> /BYTES/v4_prime + +# Release locks. + +# Reset ignored sequence numbers for now. +run ok +txn_ignore_seqs t=A seqs +---- +>> at end: +txn: "A" meta={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=4} lock=true stat=PENDING rts=10.000000000,0 wto=false gul=0,0 + +# Pending resolution without epoch bump or savepoint rollback is a no-op. +run ok +resolve_intent t=A k=k1 status=PENDING +---- +resolve_intent: "k1" -> resolved key = false +>> at end: +data: "k1"/5.000000000,0 -> /BYTES/v1 +meta: "k2"/0,0 -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=4} ts=10.000000000,0 del=true klen=12 vlen=0 ih={{3 /BYTES/v2}} mergeTs= txnDidNotUpdateMeta=false +data: "k2"/10.000000000,0 -> / +data: "k2"/5.000000000,0 -> /BYTES/v2 +meta: "k3"/0,0 -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=4} ts=10.000000000,0 del=true klen=12 vlen=0 ih={{3 /BYTES/v3}} mergeTs= txnDidNotUpdateMeta=false +data: "k3"/10.000000000,0 -> / +data: "k3"/5.000000000,0 -> /BYTES/v3 +meta: "k4"/0,0 -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=2} ts=10.000000000,0 del=false klen=12 vlen=13 ih={{1 /BYTES/v4}} mergeTs= txnDidNotUpdateMeta=false +data: "k4"/10.000000000,0 -> /BYTES/v4_prime +lock (Replicated): "k1"/Shared -> txn={id=00000002 key=/Min iso=Serializable pri=0.00000000 epo=0 ts=11.000000000,0 min=0,0 seq=0} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true +lock (Replicated): "k1"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k2"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k2"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k3"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k4"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=3} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true + +# Committed resolution releases the lock. +run ok +resolve_intent t=A k=k1 status=COMMITTED +---- +resolve_intent: "k1" -> resolved key = true +>> at end: +data: "k1"/5.000000000,0 -> /BYTES/v1 +meta: "k2"/0,0 -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=4} ts=10.000000000,0 del=true klen=12 vlen=0 ih={{3 /BYTES/v2}} mergeTs= txnDidNotUpdateMeta=false +data: "k2"/10.000000000,0 -> / +data: "k2"/5.000000000,0 -> /BYTES/v2 +meta: "k3"/0,0 -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=4} ts=10.000000000,0 del=true klen=12 vlen=0 ih={{3 /BYTES/v3}} mergeTs= txnDidNotUpdateMeta=false +data: "k3"/10.000000000,0 -> / +data: "k3"/5.000000000,0 -> /BYTES/v3 +meta: "k4"/0,0 -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=2} ts=10.000000000,0 del=false klen=12 vlen=13 ih={{1 /BYTES/v4}} mergeTs= txnDidNotUpdateMeta=false +data: "k4"/10.000000000,0 -> /BYTES/v4_prime +lock (Replicated): "k1"/Shared -> txn={id=00000002 key=/Min iso=Serializable pri=0.00000000 epo=0 ts=11.000000000,0 min=0,0 seq=0} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true +lock (Replicated): "k2"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k2"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k3"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k4"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=3} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true + +# Re-acquire the lock. +run ok +acquire_lock t=A k=k1 str=shared +---- +>> at end: +lock (Replicated): "k1"/Shared -> txn={id=00000002 key=/Min iso=Serializable pri=0.00000000 epo=0 ts=11.000000000,0 min=0,0 seq=0} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true +lock (Replicated): "k1"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=4} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true +lock (Replicated): "k2"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k2"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k3"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k4"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=3} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true + +# Aborted resolution releases the lock. +run ok +resolve_intent t=A k=k1 status=ABORTED +---- +resolve_intent: "k1" -> resolved key = true +>> at end: +data: "k1"/5.000000000,0 -> /BYTES/v1 +meta: "k2"/0,0 -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=4} ts=10.000000000,0 del=true klen=12 vlen=0 ih={{3 /BYTES/v2}} mergeTs= txnDidNotUpdateMeta=false +data: "k2"/10.000000000,0 -> / +data: "k2"/5.000000000,0 -> /BYTES/v2 +meta: "k3"/0,0 -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=4} ts=10.000000000,0 del=true klen=12 vlen=0 ih={{3 /BYTES/v3}} mergeTs= txnDidNotUpdateMeta=false +data: "k3"/10.000000000,0 -> / +data: "k3"/5.000000000,0 -> /BYTES/v3 +meta: "k4"/0,0 -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=2} ts=10.000000000,0 del=false klen=12 vlen=13 ih={{1 /BYTES/v4}} mergeTs= txnDidNotUpdateMeta=false +data: "k4"/10.000000000,0 -> /BYTES/v4_prime +lock (Replicated): "k1"/Shared -> txn={id=00000002 key=/Min iso=Serializable pri=0.00000000 epo=0 ts=11.000000000,0 min=0,0 seq=0} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true +lock (Replicated): "k2"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k2"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k3"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k4"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=3} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true + +# Re-acquire the lock. +run ok +acquire_lock t=A k=k1 str=shared +---- +>> at end: +lock (Replicated): "k1"/Shared -> txn={id=00000002 key=/Min iso=Serializable pri=0.00000000 epo=0 ts=11.000000000,0 min=0,0 seq=0} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true +lock (Replicated): "k1"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=4} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true +lock (Replicated): "k2"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k2"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k3"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k4"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=3} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true + +# Pending resolution with newer epoch releases the lock. +run ok +with t=A + txn_restart + resolve_intent k=k1 status=PENDING +---- +resolve_intent: "k1" -> resolved key = true +>> at end: +txn: "A" meta={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=2 ts=10.000000000,0 min=0,0 seq=0} lock=true stat=PENDING rts=10.000000000,0 wto=false gul=0,0 +data: "k1"/5.000000000,0 -> /BYTES/v1 +meta: "k2"/0,0 -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=4} ts=10.000000000,0 del=true klen=12 vlen=0 ih={{3 /BYTES/v2}} mergeTs= txnDidNotUpdateMeta=false +data: "k2"/10.000000000,0 -> / +data: "k2"/5.000000000,0 -> /BYTES/v2 +meta: "k3"/0,0 -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=4} ts=10.000000000,0 del=true klen=12 vlen=0 ih={{3 /BYTES/v3}} mergeTs= txnDidNotUpdateMeta=false +data: "k3"/10.000000000,0 -> / +data: "k3"/5.000000000,0 -> /BYTES/v3 +meta: "k4"/0,0 -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=2} ts=10.000000000,0 del=false klen=12 vlen=13 ih={{1 /BYTES/v4}} mergeTs= txnDidNotUpdateMeta=false +data: "k4"/10.000000000,0 -> /BYTES/v4_prime +lock (Replicated): "k1"/Shared -> txn={id=00000002 key=/Min iso=Serializable pri=0.00000000 epo=0 ts=11.000000000,0 min=0,0 seq=0} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true +lock (Replicated): "k2"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k2"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k3"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k4"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=3} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true + +# Re-acquire the lock at the new epoch. +run ok +acquire_lock t=A k=k1 str=shared +---- +>> at end: +lock (Replicated): "k1"/Shared -> txn={id=00000002 key=/Min iso=Serializable pri=0.00000000 epo=0 ts=11.000000000,0 min=0,0 seq=0} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true +lock (Replicated): "k1"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=2 ts=10.000000000,0 min=0,0 seq=0} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true +lock (Replicated): "k2"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k2"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k3"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k4"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=3} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true + +# Pending resolution with older epoch is a no-op, regardless of savepoint +# rollback. +run ok +with t=A + txn_restart epoch=1 + resolve_intent k=k1 status=PENDING +---- +resolve_intent: "k1" -> resolved key = false +>> at end: +txn: "A" meta={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=0} lock=true stat=PENDING rts=10.000000000,0 wto=false gul=0,0 +data: "k1"/5.000000000,0 -> /BYTES/v1 +meta: "k2"/0,0 -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=4} ts=10.000000000,0 del=true klen=12 vlen=0 ih={{3 /BYTES/v2}} mergeTs= txnDidNotUpdateMeta=false +data: "k2"/10.000000000,0 -> / +data: "k2"/5.000000000,0 -> /BYTES/v2 +meta: "k3"/0,0 -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=4} ts=10.000000000,0 del=true klen=12 vlen=0 ih={{3 /BYTES/v3}} mergeTs= txnDidNotUpdateMeta=false +data: "k3"/10.000000000,0 -> / +data: "k3"/5.000000000,0 -> /BYTES/v3 +meta: "k4"/0,0 -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=2} ts=10.000000000,0 del=false klen=12 vlen=13 ih={{1 /BYTES/v4}} mergeTs= txnDidNotUpdateMeta=false +data: "k4"/10.000000000,0 -> /BYTES/v4_prime +lock (Replicated): "k1"/Shared -> txn={id=00000002 key=/Min iso=Serializable pri=0.00000000 epo=0 ts=11.000000000,0 min=0,0 seq=0} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true +lock (Replicated): "k1"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=2 ts=10.000000000,0 min=0,0 seq=0} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true +lock (Replicated): "k2"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k2"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k3"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k4"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=3} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true + +run ok +with t=A + txn_ignore_seqs seqs=0-0 + resolve_intent k=k1 status=PENDING +---- +resolve_intent: "k1" -> resolved key = false +>> at end: +txn: "A" meta={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=0} lock=true stat=PENDING rts=10.000000000,0 wto=false gul=0,0 isn=1 +data: "k1"/5.000000000,0 -> /BYTES/v1 +meta: "k2"/0,0 -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=4} ts=10.000000000,0 del=true klen=12 vlen=0 ih={{3 /BYTES/v2}} mergeTs= txnDidNotUpdateMeta=false +data: "k2"/10.000000000,0 -> / +data: "k2"/5.000000000,0 -> /BYTES/v2 +meta: "k3"/0,0 -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=4} ts=10.000000000,0 del=true klen=12 vlen=0 ih={{3 /BYTES/v3}} mergeTs= txnDidNotUpdateMeta=false +data: "k3"/10.000000000,0 -> / +data: "k3"/5.000000000,0 -> /BYTES/v3 +meta: "k4"/0,0 -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=2} ts=10.000000000,0 del=false klen=12 vlen=13 ih={{1 /BYTES/v4}} mergeTs= txnDidNotUpdateMeta=false +data: "k4"/10.000000000,0 -> /BYTES/v4_prime +lock (Replicated): "k1"/Shared -> txn={id=00000002 key=/Min iso=Serializable pri=0.00000000 epo=0 ts=11.000000000,0 min=0,0 seq=0} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true +lock (Replicated): "k1"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=2 ts=10.000000000,0 min=0,0 seq=0} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true +lock (Replicated): "k2"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k2"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k3"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k4"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=3} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true + +# Pending resolution of the lock in the same epoch without a savepoint rollback +# is a no-op. +run ok +with t=A + txn_restart epoch=2 + resolve_intent k=k1 status=PENDING +---- +resolve_intent: "k1" -> resolved key = false +>> at end: +txn: "A" meta={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=2 ts=10.000000000,0 min=0,0 seq=0} lock=true stat=PENDING rts=10.000000000,0 wto=false gul=0,0 +data: "k1"/5.000000000,0 -> /BYTES/v1 +meta: "k2"/0,0 -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=4} ts=10.000000000,0 del=true klen=12 vlen=0 ih={{3 /BYTES/v2}} mergeTs= txnDidNotUpdateMeta=false +data: "k2"/10.000000000,0 -> / +data: "k2"/5.000000000,0 -> /BYTES/v2 +meta: "k3"/0,0 -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=4} ts=10.000000000,0 del=true klen=12 vlen=0 ih={{3 /BYTES/v3}} mergeTs= txnDidNotUpdateMeta=false +data: "k3"/10.000000000,0 -> / +data: "k3"/5.000000000,0 -> /BYTES/v3 +meta: "k4"/0,0 -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=2} ts=10.000000000,0 del=false klen=12 vlen=13 ih={{1 /BYTES/v4}} mergeTs= txnDidNotUpdateMeta=false +data: "k4"/10.000000000,0 -> /BYTES/v4_prime +lock (Replicated): "k1"/Shared -> txn={id=00000002 key=/Min iso=Serializable pri=0.00000000 epo=0 ts=11.000000000,0 min=0,0 seq=0} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true +lock (Replicated): "k1"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=2 ts=10.000000000,0 min=0,0 seq=0} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true +lock (Replicated): "k2"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k2"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k3"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k4"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=3} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true + +# Pending resolution of the lock in the same epoch with savepoint rollback +# releases the lock. +run ok +with t=A + txn_ignore_seqs seqs=0-0 + resolve_intent k=k1 status=PENDING +---- +resolve_intent: "k1" -> resolved key = true +>> at end: +txn: "A" meta={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=2 ts=10.000000000,0 min=0,0 seq=0} lock=true stat=PENDING rts=10.000000000,0 wto=false gul=0,0 isn=1 +data: "k1"/5.000000000,0 -> /BYTES/v1 +meta: "k2"/0,0 -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=4} ts=10.000000000,0 del=true klen=12 vlen=0 ih={{3 /BYTES/v2}} mergeTs= txnDidNotUpdateMeta=false +data: "k2"/10.000000000,0 -> / +data: "k2"/5.000000000,0 -> /BYTES/v2 +meta: "k3"/0,0 -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=4} ts=10.000000000,0 del=true klen=12 vlen=0 ih={{3 /BYTES/v3}} mergeTs= txnDidNotUpdateMeta=false +data: "k3"/10.000000000,0 -> / +data: "k3"/5.000000000,0 -> /BYTES/v3 +meta: "k4"/0,0 -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=2} ts=10.000000000,0 del=false klen=12 vlen=13 ih={{1 /BYTES/v4}} mergeTs= txnDidNotUpdateMeta=false +data: "k4"/10.000000000,0 -> /BYTES/v4_prime +lock (Replicated): "k1"/Shared -> txn={id=00000002 key=/Min iso=Serializable pri=0.00000000 epo=0 ts=11.000000000,0 min=0,0 seq=0} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true +lock (Replicated): "k2"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k2"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k3"/Exclusive -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=1} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=false +lock (Replicated): "k4"/Shared -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=1 ts=10.000000000,0 min=0,0 seq=3} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true + +# Ranged resolution behaves like single-key resolution. +# As always, it also resolves intents in the range. +run ok +resolve_intent_range t=A k=k2 end=k5 status=COMMITTED +---- +resolve_intent_range: "k2"-"k5" -> resolved 3 key(s) +>> at end: +data: "k1"/5.000000000,0 -> /BYTES/v1 +data: "k2"/5.000000000,0 -> /BYTES/v2 +data: "k3"/5.000000000,0 -> /BYTES/v3 +lock (Replicated): "k1"/Shared -> txn={id=00000002 key=/Min iso=Serializable pri=0.00000000 epo=0 ts=11.000000000,0 min=0,0 seq=0} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true + +# Aborted resolution for other transaction releases the lock. +run ok +resolve_intent t=B k=k1 status=ABORTED +---- +resolve_intent: "k1" -> resolved key = true +>> at end: +data: "k1"/5.000000000,0 -> /BYTES/v1 +data: "k2"/5.000000000,0 -> /BYTES/v2 +data: "k3"/5.000000000,0 -> /BYTES/v3 diff --git a/pkg/storage/testdata/mvcc_histories/resolve_intent_pagination b/pkg/storage/testdata/mvcc_histories/resolve_intent_pagination index b4a46ff05a44..93f65399321a 100644 --- a/pkg/storage/testdata/mvcc_histories/resolve_intent_pagination +++ b/pkg/storage/testdata/mvcc_histories/resolve_intent_pagination @@ -136,3 +136,68 @@ data: "dddddddddddddddddddddddddddddddddddddddddddddddddd"/1.000000000,0 -> /BYT data: "eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"/1.000000000,0 -> /BYTES/e meta: "f"/0,0 -> txn={id=00000001 key=/Min iso=Serializable pri=0.00000000 epo=0 ts=1.000000000,0 min=0,0 seq=0} ts=1.000000000,0 del=false klen=12 vlen=6 mergeTs= txnDidNotUpdateMeta=true data: "f"/1.000000000,0 -> /BYTES/f + + +# Test MaxKeys and TargetBytes for resolve intent range with replicated locks. + +run ok +clear_range k=a end=z +---- +>> at end: + + +# Put some test data with locks. +run ok +with t=B + txn_begin ts=1 + acquire_lock k=a str=shared + put k=a v=a + acquire_lock k=b str=shared + acquire_lock k=b str=exclusive + put k=b v=b + acquire_lock k=c str=shared +---- +>> at end: +txn: "B" meta={id=00000002 key=/Min iso=Serializable pri=0.00000000 epo=0 ts=1.000000000,0 min=0,0 seq=0} lock=true stat=PENDING rts=1.000000000,0 wto=false gul=0,0 +meta: "a"/0,0 -> txn={id=00000002 key=/Min iso=Serializable pri=0.00000000 epo=0 ts=1.000000000,0 min=0,0 seq=0} ts=1.000000000,0 del=false klen=12 vlen=6 mergeTs= txnDidNotUpdateMeta=true +data: "a"/1.000000000,0 -> /BYTES/a +meta: "b"/0,0 -> txn={id=00000002 key=/Min iso=Serializable pri=0.00000000 epo=0 ts=1.000000000,0 min=0,0 seq=0} ts=1.000000000,0 del=false klen=12 vlen=6 mergeTs= txnDidNotUpdateMeta=true +data: "b"/1.000000000,0 -> /BYTES/b +lock (Replicated): "a"/Shared -> txn={id=00000002 key=/Min iso=Serializable pri=0.00000000 epo=0 ts=1.000000000,0 min=0,0 seq=0} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true +lock (Replicated): "b"/Exclusive -> txn={id=00000002 key=/Min iso=Serializable pri=0.00000000 epo=0 ts=1.000000000,0 min=0,0 seq=0} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true +lock (Replicated): "b"/Shared -> txn={id=00000002 key=/Min iso=Serializable pri=0.00000000 epo=0 ts=1.000000000,0 min=0,0 seq=0} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true +lock (Replicated): "c"/Shared -> txn={id=00000002 key=/Min iso=Serializable pri=0.00000000 epo=0 ts=1.000000000,0 min=0,0 seq=0} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true + +run ok +resolve_intent_range t=B k=a end=z status=COMMITTED maxKeys=1 batched +---- +resolve_intent_range: "a"-"z" -> resolved 1 key(s), 56 bytes +resolve_intent_range: resume span ["a\x00","z") RESUME_KEY_LIMIT +resolve_intent_range: batch after write is non-empty +>> at end: +data: "a"/1.000000000,0 -> /BYTES/a +meta: "b"/0,0 -> txn={id=00000002 key=/Min iso=Serializable pri=0.00000000 epo=0 ts=1.000000000,0 min=0,0 seq=0} ts=1.000000000,0 del=false klen=12 vlen=6 mergeTs= txnDidNotUpdateMeta=true +data: "b"/1.000000000,0 -> /BYTES/b +lock (Replicated): "b"/Exclusive -> txn={id=00000002 key=/Min iso=Serializable pri=0.00000000 epo=0 ts=1.000000000,0 min=0,0 seq=0} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true +lock (Replicated): "b"/Shared -> txn={id=00000002 key=/Min iso=Serializable pri=0.00000000 epo=0 ts=1.000000000,0 min=0,0 seq=0} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true +lock (Replicated): "c"/Shared -> txn={id=00000002 key=/Min iso=Serializable pri=0.00000000 epo=0 ts=1.000000000,0 min=0,0 seq=0} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true + +run ok +resolve_intent_range t=B k=a end=z status=COMMITTED targetBytes=1 batched +---- +resolve_intent_range: "a"-"z" -> resolved 1 key(s), 84 bytes +resolve_intent_range: resume span ["b\x00","z") RESUME_BYTE_LIMIT +resolve_intent_range: batch after write is non-empty +>> at end: +data: "a"/1.000000000,0 -> /BYTES/a +data: "b"/1.000000000,0 -> /BYTES/b +lock (Replicated): "c"/Shared -> txn={id=00000002 key=/Min iso=Serializable pri=0.00000000 epo=0 ts=1.000000000,0 min=0,0 seq=0} ts=0,0 del=false klen=0 vlen=0 mergeTs= txnDidNotUpdateMeta=true + +run ok +resolve_intent_range t=B k=a end=z status=COMMITTED maxKeys=1 batched +---- +resolve_intent_range: "a"-"z" -> resolved 1 key(s), 28 bytes +resolve_intent_range: batch after write is non-empty +>> at end: +data: "a"/1.000000000,0 -> /BYTES/a +data: "b"/1.000000000,0 -> /BYTES/b diff --git a/pkg/storage/testdata/mvcc_histories/skip_locked b/pkg/storage/testdata/mvcc_histories/skip_locked index 60ddf7b1af7a..7119e9782ed3 100644 --- a/pkg/storage/testdata/mvcc_histories/skip_locked +++ b/pkg/storage/testdata/mvcc_histories/skip_locked @@ -39,8 +39,6 @@ meta: "k3"/0,0 -> txn={id=00000003 key=/Min iso=Serializable pri=0.00000000 epo= data: "k3"/14.000000000,0 -> /BYTES/v4 data: "k4"/15.000000000,0 -> /BYTES/v5 data: "k5"/17.000000000,0 -> /BYTES/v6 -lock (Replicated): "k2"/Intent -> txn={id=00000002 key=/Min iso=Serializable pri=0.00000000 epo=0 ts=13.000000000,0 min=0,0 seq=0} ts=13.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true -lock (Replicated): "k3"/Intent -> txn={id=00000003 key=/Min iso=Serializable pri=0.00000000 epo=0 ts=14.000000000,0 min=0,0 seq=0} ts=14.000000000,0 del=false klen=12 vlen=7 mergeTs= txnDidNotUpdateMeta=true lock (Unreplicated): k4/Exclusive -> id=00000005 key=/Min iso=Serializable pri=0.00000000 epo=0 ts=16.000000000,0 min=0,0 seq=0 # Test cases: