Skip to content

Commit a6b4f24

Browse files
ti-chi-botMyonKemintati-chi-bot[bot]
authored
txn: Fix the issue that CheckTxnStatus didn't make rollback on optimistic transaction's primary protected, which may break transaction atomicity (tikv#16621) (tikv#16954)
close tikv#16620 Fix the issue that CheckTxnStatus didn't make rollback on optimistic transaction's primary protected, which may break transaction atomicity Signed-off-by: ti-chi-bot <[email protected]> Signed-off-by: MyonKeminta <[email protected]> Co-authored-by: MyonKeminta <[email protected]> Co-authored-by: MyonKeminta <[email protected]> Co-authored-by: ti-chi-bot[bot] <108142056+ti-chi-bot[bot]@users.noreply.github.com>
1 parent 31c050d commit a6b4f24

File tree

5 files changed

+114
-15
lines changed

5 files changed

+114
-15
lines changed

components/resolved_ts/src/cmd.rs

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,8 @@ pub(crate) fn decode_write(key: &[u8], value: &[u8], is_apply: bool) -> Option<W
143143
// gc_fence exists.
144144
if is_apply && write.gc_fence.is_some() {
145145
// `gc_fence` is set means the write record has been rewritten.
146-
// Currently the only case is writing overlapped_rollback. And in this case
146+
// Currently the only case is writing overlapped_rollback. And in this case we
147+
// can safely ignore the writing operation.
147148
assert!(write.has_overlapped_rollback);
148149
assert_ne!(write.write_type, WriteType::Rollback);
149150
return None;
@@ -334,9 +335,15 @@ mod tests {
334335
must_prewrite_put(&mut engine, b"k1", &[b'v'; 512], b"k1", 4);
335336
must_commit(&mut engine, b"k1", 4, 5);
336337

337-
must_prewrite_put(&mut engine, b"k1", b"v3", b"k1", 5);
338+
must_prewrite_put(&mut engine, b"k1", b"v3", b"pk", 5);
338339
must_rollback(&mut engine, b"k1", 5, false);
339340

341+
must_prewrite_put(&mut engine, b"k1", b"v4", b"k1", 6);
342+
must_commit(&mut engine, b"k1", 6, 7);
343+
344+
must_prewrite_put(&mut engine, b"k1", b"v5", b"k1", 7);
345+
must_rollback(&mut engine, b"k1", 7, true);
346+
340347
let k1 = Key::from_raw(b"k1");
341348
let rows: Vec<_> = engine
342349
.take_last_modifies()
@@ -398,6 +405,26 @@ mod tests {
398405
commit_ts: None,
399406
write_type: WriteType::Rollback,
400407
},
408+
ChangeRow::Prewrite {
409+
key: k1.clone(),
410+
start_ts: 6.into(),
411+
value: Some(b"v4".to_vec()),
412+
lock_type: LockType::Put,
413+
},
414+
ChangeRow::Commit {
415+
key: k1.clone(),
416+
start_ts: Some(6.into()),
417+
commit_ts: Some(7.into()),
418+
write_type: WriteType::Put,
419+
},
420+
ChangeRow::Prewrite {
421+
key: k1.clone(),
422+
start_ts: 7.into(),
423+
value: Some(b"v5".to_vec()),
424+
lock_type: LockType::Put,
425+
},
426+
// Rollback of the txn@start_ts=7 will be missing as overlapped rollback is not
427+
// hanlded.
401428
];
402429
assert_eq!(rows, expected);
403430

src/storage/mvcc/txn.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -543,8 +543,10 @@ pub(crate) mod tests {
543543

544544
// Rollback lock
545545
must_rollback(&mut engine, k, 15, false);
546-
// Rollbacks of optimistic transactions needn't be protected
547-
must_get_rollback_protected(&mut engine, k, 15, false);
546+
// Rollbacks of optimistic transactions need to be protected
547+
// TODO: Re-check how the test can be better written after refinement of
548+
// `must_rollback`'s semantics.
549+
must_get_rollback_protected(&mut engine, k, 15, true);
548550
}
549551

550552
#[test]
@@ -896,16 +898,20 @@ pub(crate) mod tests {
896898
#[test]
897899
fn test_collapse_prev_rollback() {
898900
let mut engine = TestEngineBuilder::new().build().unwrap();
899-
let (key, value) = (b"key", b"value");
901+
let (key, pk, value) = (b"key", b"pk", b"value");
902+
903+
// Worked around the problem that `must_rollback` always protects primary lock
904+
// by setting different PK.
905+
// TODO: Cover primary when working on https://github.com/tikv/tikv/issues/16625
900906

901907
// Add a Rollback whose start ts is 1.
902-
must_prewrite_put(&mut engine, key, value, key, 1);
908+
must_prewrite_put(&mut engine, key, value, pk, 1);
903909
must_rollback(&mut engine, key, 1, false);
904910
must_get_rollback_ts(&mut engine, key, 1);
905911

906912
// Add a Rollback whose start ts is 2, the previous Rollback whose
907913
// start ts is 1 will be collapsed.
908-
must_prewrite_put(&mut engine, key, value, key, 2);
914+
must_prewrite_put(&mut engine, key, value, pk, 2);
909915
must_rollback(&mut engine, key, 2, false);
910916
must_get_none(&mut engine, key, 2);
911917
must_get_rollback_ts(&mut engine, key, 2);

src/storage/txn/actions/check_txn_status.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -322,8 +322,8 @@ pub fn rollback_lock(
322322
txn.delete_value(key.clone(), lock.ts);
323323
}
324324

325-
// Only the primary key of a pessimistic transaction needs to be protected.
326-
let protected: bool = is_pessimistic_txn && key.is_encoded_from(&lock.primary);
325+
// The primary key of a transaction needs to be protected.
326+
let protected: bool = key.is_encoded_from(&lock.primary);
327327
if let Some(write) = make_rollback(reader.start_ts, protected, overlapped_write) {
328328
txn.put_write(key.clone(), reader.start_ts, write.as_ref().to_bytes());
329329
}

src/storage/txn/actions/cleanup.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,9 @@ pub mod tests {
223223
// TTL expired. The lock should be removed.
224224
must_succeed(&mut engine, k, ts(10, 0), ts(120, 0));
225225
must_unlocked(&mut engine, k);
226-
// Rollbacks of optimistic transactions needn't be protected
227-
must_get_rollback_protected(&mut engine, k, ts(10, 0), false);
226+
// Rollbacks of optimistic transactions need to be protected
227+
// See: https://github.com/tikv/tikv/issues/16620
228+
must_get_rollback_protected(&mut engine, k, ts(10, 0), true);
228229
must_get_rollback_ts(&mut engine, k, ts(10, 0));
229230

230231
// Rollbacks of primary keys in pessimistic transactions should be protected

src/storage/txn/commands/check_txn_status.rs

Lines changed: 69 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,9 @@ impl<S: Snapshot, L: LockManager> WriteCommand<S, L> for CheckTxnStatus {
159159
#[cfg(test)]
160160
pub mod tests {
161161
use concurrency_manager::ConcurrencyManager;
162-
use kvproto::kvrpcpb::{self, Context, LockInfo, PrewriteRequestPessimisticAction::*};
162+
use kvproto::kvrpcpb::{
163+
self, Context, LockInfo, PrewriteRequestPessimisticAction::*, WriteConflictReason,
164+
};
163165
use tikv_util::deadline::Deadline;
164166
use txn_types::{Key, LastChange, WriteType};
165167

@@ -168,7 +170,7 @@ pub mod tests {
168170
kv::Engine,
169171
lock_manager::MockLockManager,
170172
mvcc,
171-
mvcc::tests::*,
173+
mvcc::{tests::*, ErrorInner},
172174
txn::{
173175
self,
174176
actions::acquire_pessimistic_lock::tests::acquire_pessimistic_lock_allow_lock_with_conflict,
@@ -224,7 +226,12 @@ pub mod tests {
224226
)
225227
.unwrap();
226228
if let ProcessResult::TxnStatus { txn_status } = result.pr {
227-
assert!(status_pred(txn_status));
229+
let formatted_txn_status = format!("{:?}", txn_status);
230+
assert!(
231+
status_pred(txn_status),
232+
"txn_status returned by check_txn_status ({}) doesn't pass the check",
233+
formatted_txn_status
234+
);
228235
} else {
229236
unreachable!();
230237
}
@@ -414,7 +421,7 @@ pub mod tests {
414421
|s| s == TtlExpire,
415422
);
416423
must_unlocked(&mut engine, b"k1");
417-
must_get_rollback_protected(&mut engine, b"k1", 1, false);
424+
must_get_rollback_protected(&mut engine, b"k1", 1, true);
418425

419426
// case 2: primary is prewritten (pessimistic)
420427
must_acquire_pessimistic_lock(&mut engine, b"k2", b"k2", 15, 15);
@@ -829,6 +836,7 @@ pub mod tests {
829836
ts(20, 0),
830837
WriteType::Rollback,
831838
);
839+
must_get_rollback_protected(&mut engine, k, ts(20, 0), true);
832840

833841
// Push the min_commit_ts of pessimistic locks.
834842
must_acquire_pessimistic_lock_for_large_txn(&mut engine, k, k, ts(4, 0), ts(130, 0), 200);
@@ -1437,4 +1445,61 @@ pub mod tests {
14371445
)
14381446
.unwrap_err();
14391447
}
1448+
1449+
#[test]
1450+
fn test_check_txn_status_rollback_optimistic() {
1451+
let mut engine = TestEngineBuilder::new().build().unwrap();
1452+
let k = b"k1";
1453+
let (v1, v2) = (b"v1", b"v2");
1454+
1455+
let ts = TimeStamp::compose;
1456+
1457+
must_prewrite_put_async_commit(&mut engine, k, v1, k, &Some(vec![]), ts(1, 0), ts(1, 1));
1458+
must_commit(&mut engine, k, ts(1, 0), ts(2, 0));
1459+
1460+
must_prewrite_put(&mut engine, k, v2, k, ts(2, 0));
1461+
assert!(!must_have_write(&mut engine, k, ts(2, 0)).has_overlapped_rollback);
1462+
1463+
must_success(
1464+
&mut engine,
1465+
k,
1466+
ts(2, 0),
1467+
ts(3, 0),
1468+
ts(3, 0),
1469+
true,
1470+
false,
1471+
false,
1472+
|s| s == TtlExpire,
1473+
);
1474+
must_get_overlapped_rollback(
1475+
&mut engine,
1476+
k,
1477+
ts(2, 0),
1478+
ts(1, 0),
1479+
WriteType::Put,
1480+
Some(0.into()),
1481+
);
1482+
1483+
let e = must_prewrite_put_err(&mut engine, k, v2, k, ts(2, 0));
1484+
match &*e.0 {
1485+
ErrorInner::WriteConflict {
1486+
start_ts,
1487+
conflict_start_ts,
1488+
conflict_commit_ts,
1489+
key,
1490+
primary,
1491+
reason,
1492+
} => {
1493+
assert_eq!(*start_ts, ts(2, 0));
1494+
assert_eq!(*conflict_start_ts, ts(1, 0));
1495+
assert_eq!(*conflict_commit_ts, ts(2, 0));
1496+
assert_eq!(key.as_slice(), k);
1497+
assert_eq!(primary.as_slice(), k);
1498+
assert_eq!(*reason, WriteConflictReason::SelfRolledBack);
1499+
}
1500+
e => {
1501+
panic!("unexpected error: {:?}", e);
1502+
}
1503+
}
1504+
}
14401505
}

0 commit comments

Comments
 (0)