feat(server): Introduce transaction clock.

Partially implements #6.
Before, each shard lazily updated its clock used for the expiry evaluation.
Now, the clock value is set during the transaction scheduling phase and is assigned
to each transaction. From now on DbSlice methods use this value when checking whether
the entry is expired via passed DbContext argument.

Also, implemented transactionally consistent TIME command and
verify that time is the same during the transaction. See
https://ably.com/blog/redis-keys-do-not-expire-atomically for motivation.

Still have not implemented any lamport style updates for background processes
(not sure if it's the right way to proceed).
This commit is contained in:
Roman Gershman 2022-09-25 16:18:59 +03:00 committed by Roman Gershman
parent 347a619b44
commit 0925829afb
28 changed files with 448 additions and 387 deletions

View file

@ -106,18 +106,22 @@ void BlockingController::RunStep(Transaction* completed_t) {
}
}
DbContext context;
context.time_now_ms = GetCurrentTimeMs();
for (DbIndex index : awakened_indices_) {
auto dbit = watched_dbs_.find(index);
if (dbit == watched_dbs_.end())
continue;
context.db_index = index;
DbWatchTable& wt = *dbit->second;
for (auto key : wt.awakened_keys) {
string_view sv_key = static_cast<string_view>(key);
DVLOG(1) << "Processing awakened key " << sv_key;
// Double verify we still got the item.
auto [it, exp_it] = owner_->db_slice().FindExt(index, sv_key);
auto [it, exp_it] = owner_->db_slice().FindExt(context, sv_key);
if (!IsValid(it) || it->second.ObjType() != OBJ_LIST) // Only LIST is allowed to block.
continue;