New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
txn: Add txn_heart_beat API #5407
Changes from all commits
eec7448
d9ca6d6
aaadd77
b2d87a3
c56afcd
2f0bb22
3d4b7ef
974e07e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -114,6 +114,7 @@ pub enum StorageCb { | |
MvccInfoByKey(Callback<MvccInfo>), | ||
MvccInfoByStartTs(Callback<Option<(Key, MvccInfo)>>), | ||
Locks(Callback<Vec<LockInfo>>), | ||
TxnStatus(Callback<(u64, u64)>), | ||
} | ||
|
||
/// Store Transaction scheduler commands. | ||
|
@@ -195,6 +196,12 @@ pub enum Command { | |
start_ts: u64, | ||
for_update_ts: u64, | ||
}, | ||
TxnHeartBeat { | ||
ctx: Context, | ||
primary_key: Key, | ||
start_ts: u64, | ||
advise_ttl: u64, | ||
}, | ||
/// Scan locks from `start_key`, and find all locks whose timestamp is before `max_ts`. | ||
ScanLock { | ||
ctx: Context, | ||
|
@@ -344,6 +351,16 @@ impl Display for Command { | |
for_update_ts, | ||
ctx | ||
), | ||
Command::TxnHeartBeat { | ||
ref ctx, | ||
ref primary_key, | ||
start_ts, | ||
advise_ttl, | ||
} => write!( | ||
f, | ||
"kv::command::txn_heart_beat {} @ {} ttl {} | {:?}", | ||
primary_key, start_ts, advise_ttl, ctx | ||
), | ||
Command::ScanLock { | ||
ref ctx, | ||
max_ts, | ||
|
@@ -449,6 +466,7 @@ impl Command { | |
Command::Cleanup { .. } => CommandKind::cleanup, | ||
Command::Rollback { .. } => CommandKind::rollback, | ||
Command::PessimisticRollback { .. } => CommandKind::pessimistic_rollback, | ||
Command::TxnHeartBeat { .. } => CommandKind::txn_heart_beat, | ||
Command::ScanLock { .. } => CommandKind::scan_lock, | ||
Command::ResolveLock { .. } => CommandKind::resolve_lock, | ||
Command::ResolveLockLite { .. } => CommandKind::resolve_lock_lite, | ||
|
@@ -466,7 +484,8 @@ impl Command { | |
| Command::Cleanup { start_ts, .. } | ||
| Command::Rollback { start_ts, .. } | ||
| Command::PessimisticRollback { start_ts, .. } | ||
| Command::MvccByStartTs { start_ts, .. } => start_ts, | ||
| Command::MvccByStartTs { start_ts, .. } | ||
| Command::TxnHeartBeat { start_ts, .. } => start_ts, | ||
Command::Commit { lock_ts, .. } => lock_ts, | ||
Command::ScanLock { max_ts, .. } => max_ts, | ||
Command::ResolveLockLite { start_ts, .. } => start_ts, | ||
|
@@ -485,6 +504,7 @@ impl Command { | |
| Command::Cleanup { ref ctx, .. } | ||
| Command::Rollback { ref ctx, .. } | ||
| Command::PessimisticRollback { ref ctx, .. } | ||
| Command::TxnHeartBeat { ref ctx, .. } | ||
| Command::ScanLock { ref ctx, .. } | ||
| Command::ResolveLock { ref ctx, .. } | ||
| Command::ResolveLockLite { ref ctx, .. } | ||
|
@@ -503,6 +523,7 @@ impl Command { | |
| Command::Cleanup { ref mut ctx, .. } | ||
| Command::Rollback { ref mut ctx, .. } | ||
| Command::PessimisticRollback { ref mut ctx, .. } | ||
| Command::TxnHeartBeat { ref mut ctx, .. } | ||
| Command::ScanLock { ref mut ctx, .. } | ||
| Command::ResolveLock { ref mut ctx, .. } | ||
| Command::ResolveLockLite { ref mut ctx, .. } | ||
|
@@ -558,6 +579,11 @@ impl Command { | |
Command::Cleanup { ref key, .. } => { | ||
bytes += key.as_encoded().len(); | ||
} | ||
Command::TxnHeartBeat { | ||
ref primary_key, .. | ||
} => { | ||
bytes += primary_key.as_encoded().len(); | ||
} | ||
_ => {} | ||
} | ||
bytes | ||
|
@@ -1246,6 +1272,28 @@ impl<E: Engine, L: LockMgr> Storage<E, L> { | |
Ok(()) | ||
} | ||
|
||
/// Check the specified primary key and enlarge it's TTL if necessary. Returns the new TTL. | ||
/// | ||
/// Schedules a [`Command::TxnHeartBeat`] | ||
pub fn async_txn_heart_beat( | ||
&self, | ||
ctx: Context, | ||
primary_key: Key, | ||
start_ts: u64, | ||
advise_ttl: u64, | ||
callback: Callback<(u64, u64)>, | ||
) -> Result<()> { | ||
let cmd = Command::TxnHeartBeat { | ||
ctx, | ||
primary_key, | ||
start_ts, | ||
advise_ttl, | ||
}; | ||
self.schedule(cmd, StorageCb::TxnStatus(callback))?; | ||
KV_COMMAND_COUNTER_VEC_STATIC.txn_heart_beat.inc(); | ||
Ok(()) | ||
} | ||
|
||
/// Scan locks from `start_key`, and find all locks whose timestamp is before `max_ts`. | ||
/// | ||
/// Schedules a [`Command::ScanLock`]. | ||
|
@@ -4202,4 +4250,82 @@ mod tests { | |
.unwrap(); | ||
rx.recv().unwrap(); | ||
} | ||
|
||
#[test] | ||
fn test_txn_heart_beat() { | ||
let storage = TestStorageBuilder::new().build().unwrap(); | ||
let (tx, rx) = channel(); | ||
|
||
let k = Key::from_raw(b"k"); | ||
let v = b"v".to_vec(); | ||
|
||
// No lock. | ||
storage | ||
.async_txn_heart_beat( | ||
Context::default(), | ||
k.clone(), | ||
10, | ||
100, | ||
expect_fail_callback(tx.clone(), 0, |e| match e { | ||
Error::Txn(txn::Error::Mvcc(mvcc::Error::TxnLockNotFound { .. })) => (), | ||
e => panic!("unexpected error chain: {:?}", e), | ||
}), | ||
) | ||
.unwrap(); | ||
rx.recv().unwrap(); | ||
|
||
let mut options = Options::default(); | ||
options.lock_ttl = 100; | ||
storage | ||
.async_prewrite( | ||
Context::default(), | ||
vec![Mutation::Put((k.clone(), v))], | ||
k.as_encoded().to_vec(), | ||
10, | ||
options, | ||
expect_ok_callback(tx.clone(), 0), | ||
) | ||
.unwrap(); | ||
rx.recv().unwrap(); | ||
|
||
// `advise_ttl` = 90, which is less than current ttl 100. The lock's ttl will remains 100. | ||
storage | ||
.async_txn_heart_beat( | ||
Context::default(), | ||
k.clone(), | ||
10, | ||
90, | ||
expect_value_callback(tx.clone(), 0, (100, 0)), | ||
) | ||
.unwrap(); | ||
rx.recv().unwrap(); | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we also get the lock directly and check its TTL? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The function to get lock which is scan_lock, doesn't get the TTL. So I didn't check it directly. |
||
// `advise_ttl` = 110, which is greater than current ttl. The lock's ttl will be updated to | ||
// 110. | ||
storage | ||
.async_txn_heart_beat( | ||
Context::default(), | ||
k.clone(), | ||
10, | ||
110, | ||
expect_value_callback(tx.clone(), 0, (110, 0)), | ||
) | ||
.unwrap(); | ||
rx.recv().unwrap(); | ||
|
||
// Lock not match. Nothing happens except throwing an error. | ||
storage | ||
.async_txn_heart_beat( | ||
Context::default(), | ||
k.clone(), | ||
11, | ||
150, | ||
expect_fail_callback(tx.clone(), 0, |e| match e { | ||
Error::Txn(txn::Error::Mvcc(mvcc::Error::TxnLockNotFound { .. })) => (), | ||
e => panic!("unexpected error chain: {:?}", e), | ||
}), | ||
) | ||
.unwrap(); | ||
rx.recv().unwrap(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
how about define
advice_ttl=90; expect_ttl=100
to make the test case more easily to understand?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it may be still difficult to read since you need to find the variable's definition or assignment to know what value it is. I think I'll add comments near these arguments.