mirror of
https://github.com/dragonflydb/dragonfly.git
synced 2025-05-11 10:25:47 +02:00
fix: call NotifyPending only from tx queue invocations (#1439)
* fix: call NotifyPending only from tx queue invocations --------- Signed-off-by: Vladislav Oleshko <vlad@dragonflydb.io>
This commit is contained in:
parent
f25098bb98
commit
6f78ae5073
5 changed files with 61 additions and 11 deletions
|
@ -196,6 +196,9 @@ void BlockingController::FinalizeWatched(ArgSlice args, Transaction* tx) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlockingController::NotifyPending() {
|
void BlockingController::NotifyPending() {
|
||||||
|
const Transaction* tx = owner_->GetContTx();
|
||||||
|
CHECK(tx == nullptr) << tx->DebugId();
|
||||||
|
|
||||||
DbContext context;
|
DbContext context;
|
||||||
context.time_now_ms = GetCurrentTimeMs();
|
context.time_now_ms = GetCurrentTimeMs();
|
||||||
|
|
||||||
|
|
|
@ -402,8 +402,8 @@ void EngineShard::PollExecution(const char* context, Transaction* trans) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EngineShard::ShutdownMulti(Transaction* multi) {
|
void EngineShard::RemoveContTx(Transaction* tx) {
|
||||||
if (continuation_trans_ == multi) {
|
if (continuation_trans_ == tx) {
|
||||||
continuation_trans_ = nullptr;
|
continuation_trans_ = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,8 +98,8 @@ class EngineShard {
|
||||||
return &shard_lock_;
|
return &shard_lock_;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Awkward interface. I should solve it somehow.
|
// Remove current continuation trans if its equal to tx.
|
||||||
void ShutdownMulti(Transaction* multi);
|
void RemoveContTx(Transaction* tx);
|
||||||
|
|
||||||
void IncQuickRun() {
|
void IncQuickRun() {
|
||||||
stats_.quick_runs++;
|
stats_.quick_runs++;
|
||||||
|
@ -153,6 +153,10 @@ class EngineShard {
|
||||||
return is_replica_;
|
return is_replica_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Transaction* GetContTx() const {
|
||||||
|
return continuation_trans_;
|
||||||
|
}
|
||||||
|
|
||||||
void TEST_EnableHeartbeat();
|
void TEST_EnableHeartbeat();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -827,4 +827,39 @@ TEST_F(ListFamilyTest, BLPopUnwakesInScript) {
|
||||||
f2.Join();
|
f2.Join();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ListFamilyTest, OtherMultiWakesBLpop) {
|
||||||
|
const string_view SCRIPT = R"(
|
||||||
|
redis.call('LPUSH', 'l', 'bad')
|
||||||
|
for i = 1, 1000 do
|
||||||
|
redis.call('MGET', 'a', 'b', 'c', 'd')
|
||||||
|
end
|
||||||
|
redis.call('LPUSH', 'l', 'good')
|
||||||
|
)";
|
||||||
|
|
||||||
|
const string_view SCRIPT_SHORT = R"(
|
||||||
|
redis.call('GET', KEYS[1])
|
||||||
|
)";
|
||||||
|
|
||||||
|
// Start BLPOP with infinite timeout
|
||||||
|
auto f1 = pp_->at(1)->LaunchFiber(Launch::dispatch, [&]() {
|
||||||
|
auto resp = Run("blpop", {"BLPOP", "l", "0"});
|
||||||
|
// blpop should only be awakened after the script has completed, so the
|
||||||
|
// last element added in the script should be returned.
|
||||||
|
EXPECT_THAT(resp, ArgType(RespExpr::ARRAY));
|
||||||
|
EXPECT_THAT(resp.GetVec(), ElementsAre("l", "good"));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Start long running script that accesses the list, but should wake up blpop only after it
|
||||||
|
// finished
|
||||||
|
auto f2 = pp_->at(2)->LaunchFiber(Launch::dispatch, [&]() {
|
||||||
|
Run("script", {"EVAL", SCRIPT, "5", "a", "b", "c", "d", "l"});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Run quick multi transaction that concludes after one hop
|
||||||
|
Run({"EVAL", SCRIPT_SHORT, "1", "y"});
|
||||||
|
|
||||||
|
f1.Join();
|
||||||
|
f2.Join();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace dfly
|
} // namespace dfly
|
||||||
|
|
|
@ -522,16 +522,24 @@ bool Transaction::RunInShard(EngineShard* shard, bool txq_ooo) {
|
||||||
sd.local_mask &= ~OUT_OF_ORDER;
|
sd.local_mask &= ~OUT_OF_ORDER;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is the last hop, so clear cont_trans if its held by the current tx
|
||||||
|
shard->RemoveContTx(this);
|
||||||
|
|
||||||
// It has 2 responsibilities.
|
// It has 2 responsibilities.
|
||||||
// 1: to go over potential wakened keys, verify them and activate watch queues.
|
// 1: to go over potential wakened keys, verify them and activate watch queues.
|
||||||
// 2: if this transaction was notified and finished running - to remove it from the head
|
// 2: if this transaction was notified and finished running - to remove it from the head
|
||||||
// of the queue and notify the next one.
|
// of the queue and notify the next one.
|
||||||
// RunStep is also called for global transactions because of commands like MOVE.
|
// RunStep is also called for global transactions because of commands like MOVE.
|
||||||
if (shard->blocking_controller()) {
|
if (auto* bcontroller = shard->blocking_controller(); bcontroller) {
|
||||||
if (awaked_prerun || was_suspended) {
|
if (awaked_prerun || was_suspended) {
|
||||||
shard->blocking_controller()->FinalizeWatched(largs, this);
|
bcontroller->FinalizeWatched(largs, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wake only if no tx queue head is currently running
|
||||||
|
// Note: RemoveContTx might have no effect above if this tx had no continuations
|
||||||
|
if (shard->GetContTx() == nullptr) {
|
||||||
|
bcontroller->NotifyPending();
|
||||||
}
|
}
|
||||||
shard->blocking_controller()->NotifyPending();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1232,12 +1240,12 @@ void Transaction::UnlockMultiShardCb(const std::vector<KeyList>& sharded_keys, E
|
||||||
sd.pq_pos = TxQueue::kEnd;
|
sd.pq_pos = TxQueue::kEnd;
|
||||||
}
|
}
|
||||||
|
|
||||||
shard->ShutdownMulti(this);
|
shard->RemoveContTx(this);
|
||||||
|
|
||||||
// notify awakened transactions, not sure we need it here because it's done after
|
// Wake only if no tx queue head is currently running
|
||||||
// each operation
|
if (shard->blocking_controller() && shard->GetContTx() == nullptr)
|
||||||
if (shard->blocking_controller())
|
|
||||||
shard->blocking_controller()->NotifyPending();
|
shard->blocking_controller()->NotifyPending();
|
||||||
|
|
||||||
shard->PollExecution("unlockmulti", nullptr);
|
shard->PollExecution("unlockmulti", nullptr);
|
||||||
|
|
||||||
this->DecreaseRunCnt();
|
this->DecreaseRunCnt();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue