-
Notifications
You must be signed in to change notification settings - Fork 0
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
feat: add brpop cmd #48
base: unstable
Are you sure you want to change the base?
Changes from 1 commit
d525c40
80a5fec
c5d9c3f
ea7da1b
60c7bf8
561f034
0ae4aa2
fec46cd
3ad6f20
30830a4
9bb4f81
5b0332f
94b9a33
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,8 +15,8 @@ | |
|
||
#include "common.h" | ||
#include "config.h" | ||
#include "log.h" | ||
#include "kiwi.h" | ||
#include "log.h" | ||
#include "praft/praft.h" | ||
|
||
namespace kiwi { | ||
|
@@ -106,6 +106,91 @@ BaseCmd* BaseCmdGroup::GetSubCmd(const std::string& cmdName) { | |
return subCmd->second.get(); | ||
} | ||
|
||
void BaseCmd::BlockThisClientToWaitLRPush(std::vector<std::string>& keys, int64_t expire_time, PClient* client, | ||
BlockedConnNode::Type type) { | ||
std::unique_lock<std::shared_mutex> latch(g_kiwi->GetBlockMtx()); | ||
auto& key_to_conns = g_kiwi->GetMapFromKeyToConns(); | ||
std::shared_ptr<std::atomic<bool>> is_done = std::make_shared<std::atomic<bool>>(false); | ||
for (auto key : keys) { | ||
kiwi::BlockKey blpop_key{client->GetCurrentDB(), key}; | ||
|
||
auto it = key_to_conns.find(blpop_key); | ||
if (it == key_to_conns.end()) { | ||
key_to_conns.emplace(blpop_key, std::make_unique<std::list<BlockedConnNode>>()); | ||
it = key_to_conns.find(blpop_key); | ||
} | ||
auto& wait_list_of_this_key = it->second; | ||
yeyeye2333 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
wait_list_of_this_key->emplace_back(expire_time, client, type, is_done); | ||
AlexStocks marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} | ||
|
||
void BaseCmd::ServeAndUnblockConns(PClient* client) { | ||
kiwi::BlockKey key{client->GetCurrentDB(), client->Key()}; | ||
|
||
std::shared_lock<std::shared_mutex> read_latch(g_kiwi->GetBlockMtx()); | ||
auto& key_to_conns = g_kiwi->GetMapFromKeyToConns(); | ||
auto it = key_to_conns.find(key); | ||
if (it == key_to_conns.end()) { | ||
// no client is waitting for this key | ||
return; | ||
} | ||
read_latch.unlock(); | ||
yeyeye2333 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
std::unique_lock<std::shared_mutex> write_lock(g_kiwi->GetBlockMtx()); | ||
auto& waitting_list = it->second; | ||
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.
|
||
std::vector<std::string> elements; | ||
storage::Status s; | ||
|
||
// traverse this list from head to tail(in the order of adding sequence) ,means "first blocked, first get served“ | ||
for (auto conn_blocked = waitting_list->begin(); conn_blocked != waitting_list->end();) { | ||
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. 这里的迭代器 在哪里 ++ 的, 不在 for()的最后 |
||
if (conn_blocked->is_done_->exchange(true)) { | ||
conn_blocked = waitting_list->erase(conn_blocked); | ||
continue; | ||
} | ||
|
||
PClient* BlockedClient = (*conn_blocked).GetBlockedClient(); | ||
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. 指针强转用 |
||
|
||
if (BlockedClient->State() == ClientState::kClosed) { | ||
conn_blocked = waitting_list->erase(conn_blocked); | ||
continue; | ||
} | ||
|
||
switch (conn_blocked->GetCmdType()) { | ||
case BlockedConnNode::Type::BLPop: | ||
s = PSTORE.GetBackend(client->GetCurrentDB())->GetStorage()->LPop(client->Key(), 1, &elements); | ||
break; | ||
case BlockedConnNode::Type::BRPop: | ||
s = PSTORE.GetBackend(client->GetCurrentDB())->GetStorage()->RPop(client->Key(), 1, &elements); | ||
break; | ||
} | ||
|
||
if (s.ok()) { | ||
BlockedClient->AppendArrayLen(2); | ||
BlockedClient->AppendString(client->Key()); | ||
BlockedClient->AppendString(elements[0]); | ||
} else if (s.IsNotFound()) { | ||
// this key has no more elements to serve more blocked conn. | ||
break; | ||
} else { | ||
BlockedClient->SetRes(CmdRes::kErrOther, s.ToString()); | ||
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. 这里的错误码能兼容redis吗 |
||
} | ||
BlockedClient->SendPacket(); | ||
conn_blocked = waitting_list->erase(conn_blocked); // remove this conn from current waiting list | ||
} | ||
} | ||
|
||
bool BlockedConnNode::IsExpired() { | ||
if (expire_time_ == 0) { | ||
return false; | ||
} | ||
auto now = std::chrono::system_clock::now(); | ||
int64_t now_in_ms = std::chrono::time_point_cast<std::chrono::milliseconds>(now).time_since_epoch().count(); | ||
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. 判断是否过期, 从调用 |
||
if (expire_time_ <= now_in_ms) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
bool BaseCmdGroup::DoInitial(PClient* client) { | ||
client->SetSubCmdName(client->argv_[1]); | ||
if (!subCmds_.contains(client->SubCmdName())) { | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -135,6 +135,8 @@ const std::string kCmdNameRPush = "rpush"; | |
const std::string kCmdNameRPushx = "rpushx"; | ||
const std::string kCmdNameLPop = "lpop"; | ||
const std::string kCmdNameRPop = "rpop"; | ||
const std::string kCmdNameBLPop = "blpop"; | ||
const std::string kCmdNameBRPop = "brpop"; | ||
const std::string kCmdNameLRem = "lrem"; | ||
const std::string kCmdNameLRange = "lrange"; | ||
const std::string kCmdNameLTrim = "ltrim"; | ||
|
@@ -210,6 +212,23 @@ enum AclCategory { | |
kAclCategoryRaft = (1 << 21), | ||
}; | ||
|
||
class BlockedConnNode { | ||
public: | ||
enum Type { BLPop = 0, BRPop }; | ||
virtual ~BlockedConnNode() {} | ||
yeyeye2333 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
BlockedConnNode(int64_t expire_time, PClient* client, Type type, std::shared_ptr<std::atomic<bool>> is_done) | ||
: expire_time_(expire_time), client_(client), type_(type), is_done_(is_done) {} | ||
bool IsExpired(); | ||
PClient* GetBlockedClient() { return client_; } | ||
std::shared_ptr<std::atomic<bool>> is_done_; | ||
Type GetCmdType() { return type_; } | ||
|
||
private: | ||
Type type_; | ||
yeyeye2333 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
int64_t expire_time_; | ||
PClient* client_; | ||
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. 尽量使用 |
||
}; | ||
|
||
/** | ||
* @brief Base class for all commands | ||
* BaseCmd, as the base class for all commands, mainly implements some common functions | ||
|
@@ -273,6 +292,11 @@ class BaseCmd : public std::enable_shared_from_this<BaseCmd> { | |
|
||
uint32_t GetCmdID() const; | ||
|
||
void ServeAndUnblockConns(PClient* client); | ||
|
||
void BlockThisClientToWaitLRPush(std::vector<std::string>& keys, int64_t expire_time, PClient* client, | ||
BlockedConnNode::Type type); | ||
|
||
protected: | ||
// Execute a specific command | ||
virtual void DoCmd(PClient* client) = 0; | ||
|
@@ -312,4 +336,16 @@ class BaseCmdGroup : public BaseCmd { | |
private: | ||
std::map<std::string, std::unique_ptr<BaseCmd>> subCmds_; | ||
}; | ||
|
||
struct BlockKey { // this data struct is made for the scenario of multi dbs in pika. | ||
yeyeye2333 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
int db_id; | ||
yeyeye2333 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
std::string key; | ||
bool operator==(const BlockKey& p) const { return p.db_id == db_id && p.key == key; } | ||
}; | ||
struct BlockKeyHash { | ||
std::size_t operator()(const BlockKey& k) const { | ||
return std::hash<int>{}(k.db_id) ^ std::hash<std::string>{}(k.key); | ||
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. 这里依然会出现hash碰撞的情况吧,只是概率大小的问题 |
||
} | ||
}; | ||
|
||
} // namespace kiwi |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -165,6 +165,26 @@ void KiwiDB::OnNewConnection(uint64_t connId, std::shared_ptr<kiwi::PClient>& cl | |
client->OnConnect(); | ||
} | ||
|
||
void KiwiDB::ScanEvictedBlockedConnsOfBlrpop() { | ||
std::unique_lock<std::shared_mutex> latch(block_mtx_); | ||
auto& key_to_blocked_conns = g_kiwi->GetMapFromKeyToConns(); | ||
for (auto& it : key_to_blocked_conns) { | ||
auto& conns_list = it.second; | ||
for (auto conn_node = conns_list->begin(); conn_node != conns_list->end();) { | ||
if (conn_node->is_done_->exchange(true) || conn_node->GetBlockedClient()->State() == ClientState::kClosed) { | ||
conn_node = conns_list->erase(conn_node); | ||
} else if (conn_node->IsExpired()) { | ||
PClient* conn_ptr = conn_node->GetBlockedClient(); | ||
conn_ptr->AppendString(""); | ||
conn_ptr->SendPacket(); | ||
conn_node = conns_list->erase(conn_node); | ||
} else { | ||
conn_node++; | ||
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.
|
||
} | ||
} | ||
} | ||
} | ||
|
||
bool KiwiDB::Init() { | ||
char runid[kRunidSize + 1] = ""; | ||
getRandomHexChars(runid, kRunidSize); | ||
|
@@ -201,7 +221,7 @@ bool KiwiDB::Init() { | |
PREPL.SetMasterAddr(g_config.master_ip.ToString().c_str(), g_config.master_port.load()); | ||
} | ||
|
||
event_server_ =std::make_unique<net::EventServer<std::shared_ptr<PClient>>>(num); | ||
event_server_ = std::make_unique<net::EventServer<std::shared_ptr<PClient>>>(num); | ||
|
||
event_server_->SetRwSeparation(true); | ||
|
||
|
@@ -232,6 +252,10 @@ bool KiwiDB::Init() { | |
timerTask->SetCallback([]() { PREPL.Cron(); }); | ||
event_server_->AddTimerTask(timerTask); | ||
|
||
auto BLRPopTimerTask = std::make_shared<net::CommonTimerTask>(250); | ||
BLRPopTimerTask->SetCallback(std::bind(&KiwiDB::ScanEvictedBlockedConnsOfBlrpop, this)); | ||
event_server_->AddTimerTask(BLRPopTimerTask); | ||
|
||
time(&start_time_s_); | ||
|
||
return true; | ||
|
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.
BlockThisClientToWaitLRPush 这个方法没有看到哪里有调用
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.
看 pika 那边用到了这个方法, 可以补齐
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.
@浩林