@@ -1075,8 +1075,7 @@ func (b *putBuffer) putIntentMeta(
10751075 ctx context.Context ,
10761076 writer Writer ,
10771077 key MVCCKey ,
1078- state PrecedingIntentState ,
1079- txnDidNotUpdateMeta bool ,
1078+ helper txnDidNotUpdateMetaHelper ,
10801079 meta * enginepb.MVCCMetadata ,
10811080) (keyBytes , valBytes int64 , separatedIntentCountDelta int , err error ) {
10821081 if meta .Txn != nil && meta .Timestamp .ToTimestamp () != meta .Txn .WriteTimestamp {
@@ -1085,32 +1084,62 @@ func (b *putBuffer) putIntentMeta(
10851084 return 0 , 0 , 0 , errors .AssertionFailedf (
10861085 "meta.Timestamp != meta.Txn.WriteTimestamp: %s != %s" , meta .Timestamp , meta .Txn .WriteTimestamp )
10871086 }
1088- // All nodes in this cluster understand separated intents, so can fiddle
1089- // with TxnDidNotUpdateMeta, which is not understood by older nodes (which
1090- // are no longer present, and will never again be present).
1091- //
1092- // NB: the parameter txnDidNotUpdateMeta is about what happened prior to
1093- // this Put, and is passed through to writer below. The field
1094- // TxnDidNotUpdateMeta, in the MVCCMetadata we are about to write,
1095- // includes what happened in this Put.
1096- if state == NoExistingIntent {
1097- meta .TxnDidNotUpdateMeta = & trueValue
1098- } else {
1099- // Absence represents false.
1100- meta .TxnDidNotUpdateMeta = nil
1101- }
1102-
1087+ helper .populateMeta (ctx , meta )
11031088 bytes , err := b .marshalMeta (meta )
11041089 if err != nil {
11051090 return 0 , 0 , 0 , err
11061091 }
11071092 if separatedIntentCountDelta , err = writer .PutIntent (
1108- ctx , key .Key , bytes , state , txnDidNotUpdateMeta , meta .Txn .ID ); err != nil {
1093+ ctx , key .Key , bytes , helper . state , helper . valueForPutIntent () , meta .Txn .ID ); err != nil {
11091094 return 0 , 0 , 0 , err
11101095 }
11111096 return int64 (key .EncodedSize ()), int64 (len (bytes )), separatedIntentCountDelta , nil
11121097}
11131098
1099+ // txnDidNotUpdateMetaHelper is used to decide what to put in the MVCCMetadata
1100+ // proto, and what value to pass in the txnDidNotUpdateMeta parameter of
1101+ // PutIntent.
1102+ //
1103+ // Note that all nodes in this cluster understand separated intents, so can
1104+ // fiddle with TxnDidNotUpdateMeta, which is not understood by older nodes
1105+ // (which are no longer present, and will never again be present). Our
1106+ // assumption is that 21.1 nodes will never be writing separated intents since
1107+ // it is disabled by default and the code is known to have a bug. However they
1108+ // can set MVCCMetadata.TxnDidNotUpdateMeta to true when writing interleaved
1109+ // intents (this is harmless since interleaved intents do not invoke the
1110+ // SingleDelete optimization).
1111+ // - The txnDidNotUpdateMeta field is about what happened prior to this Put,
1112+ // and is intended to be passed through to Writer.PutIntent. It is only used
1113+ // by intentDemuxWriter when there was an existing separated intent that was
1114+ // written once and the writer is writing interleaved intents, and the true
1115+ // value enables SingleDelete of the existing separated intent. This
1116+ // transition can happen when an intent written at a 21.2 node is rewritten
1117+ // on a 21.1 node. But since 21.2 nodes never set
1118+ // MVCCMetadata.TxnDidNotUpdateMeta to true in a mixed version cluster (see
1119+ // next bullet), this will always be false in such a situation.
1120+ // - The state field describes the preceding intent state. It is intended to
1121+ // be used for populating the MVCCMetadata.TxnDidNotUpdateMeta. This is
1122+ // where we use Writer.OverrideTxnDidNotUpdateMetaToFalse to override to
1123+ // false until there can never be 21.1 nodes.
1124+ type txnDidNotUpdateMetaHelper struct {
1125+ txnDidNotUpdateMeta bool
1126+ state PrecedingIntentState
1127+ w Writer
1128+ }
1129+
1130+ func (t txnDidNotUpdateMetaHelper ) valueForPutIntent () bool {
1131+ return t .txnDidNotUpdateMeta
1132+ }
1133+
1134+ func (t txnDidNotUpdateMetaHelper ) populateMeta (ctx context.Context , meta * enginepb.MVCCMetadata ) {
1135+ if t .state == NoExistingIntent && ! t .w .OverrideTxnDidNotUpdateMetaToFalse (ctx ) {
1136+ meta .TxnDidNotUpdateMeta = & trueValue
1137+ } else {
1138+ // Absence represents false.
1139+ meta .TxnDidNotUpdateMeta = nil
1140+ }
1141+ }
1142+
11141143// MVCCPut sets the value for a specified key. It will save the value
11151144// with different versions according to its timestamp and update the
11161145// key metadata. The timestamp must be passed as a parameter; using
@@ -1746,7 +1775,12 @@ func mvccPutInternal(
17461775 var separatedIntentCountDelta int
17471776 if newMeta .Txn != nil {
17481777 metaKeySize , metaValSize , separatedIntentCountDelta , err = buf .putIntentMeta (
1749- ctx , writer , metaKey , precedingIntentState , txnDidNotUpdateMeta , newMeta )
1778+ ctx , writer , metaKey ,
1779+ txnDidNotUpdateMetaHelper {
1780+ txnDidNotUpdateMeta : txnDidNotUpdateMeta ,
1781+ state : precedingIntentState ,
1782+ w : writer ,
1783+ }, newMeta )
17501784 if err != nil {
17511785 return err
17521786 }
@@ -3176,7 +3210,13 @@ func mvccResolveWriteIntent(
31763210 // to do anything to update the intent but to move the timestamp forward,
31773211 // even if it can.
31783212 metaKeySize , metaValSize , separatedIntentCountDelta , err = buf .putIntentMeta (
3179- ctx , rw , metaKey , precedingIntentState , canSingleDelHelper .v (), & buf .newMeta )
3213+ ctx , rw , metaKey ,
3214+ txnDidNotUpdateMetaHelper {
3215+ txnDidNotUpdateMeta : canSingleDelHelper .v (),
3216+ state : precedingIntentState ,
3217+ w : rw ,
3218+ },
3219+ & buf .newMeta )
31803220 } else {
31813221 metaKeySize = int64 (metaKey .EncodedSize ())
31823222 separatedIntentCountDelta , err =
0 commit comments