diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 0000000000..dd84ea7824 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,38 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000000..bbcbbe7d61 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. diff --git a/include/pika_command.h b/include/pika_command.h index f438abf804..3328304039 100644 --- a/include/pika_command.h +++ b/include/pika_command.h @@ -100,6 +100,7 @@ const std::string kCmdNameTtl = "ttl"; const std::string kCmdNamePttl = "pttl"; const std::string kCmdNamePersist = "persist"; const std::string kCmdNameType = "type"; +const std::string kCmdNamePType = "ptype"; const std::string kCmdNameScan = "scan"; const std::string kCmdNameScanx = "scanx"; const std::string kCmdNamePKSetexAt = "pksetexat"; diff --git a/include/pika_kv.h b/include/pika_kv.h index 7ad2da73f2..b4532943cc 100644 --- a/include/pika_kv.h +++ b/include/pika_kv.h @@ -588,6 +588,24 @@ class TypeCmd : public Cmd { void DoInitial() override; }; +class PTypeCmd : public Cmd { + public: + PTypeCmd(const std::string& name, int arity, uint16_t flag) : Cmd(name, arity, flag) {} + std::vector current_key() const override { + std::vector res; + res.push_back(key_); + return res; + } + void Do(std::shared_ptr slot = nullptr) override; + void Split(std::shared_ptr slot, const HintKeys& hint_keys) override {}; + void Merge() override {}; + Cmd* Clone() override { return new PTypeCmd(*this); } + + private: + std::string key_; + void DoInitial() override; +}; + class ScanCmd : public Cmd { public: ScanCmd(const std::string& name, int arity, uint16_t flag) : Cmd(name, arity, flag), pattern_("*") {} diff --git a/src/pika_command.cc b/src/pika_command.cc index acb0799281..352d31eb62 100644 --- a/src/pika_command.cc +++ b/src/pika_command.cc @@ -280,6 +280,9 @@ void InitCmdTable(CmdTable* cmd_table) { std::unique_ptr typeptr = std::make_unique(kCmdNameType, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsKv); cmd_table->insert(std::pair>(kCmdNameType, std::move(typeptr))); + ////PTypeCmd + std::unique_ptr pTypeptr = std::make_unique(kCmdNamePType, 2, kCmdFlagsRead | kCmdFlagsSingleSlot | kCmdFlagsKv); + cmd_table->insert(std::pair>(kCmdNamePType, std::move(pTypeptr))); ////ScanCmd std::unique_ptr scanptr = std::make_unique(kCmdNameScan, -2, kCmdFlagsRead | kCmdFlagsMultiSlot | kCmdFlagsKv); diff --git a/src/pika_kv.cc b/src/pika_kv.cc index 3d1f63c0c2..44517b1dd4 100644 --- a/src/pika_kv.cc +++ b/src/pika_kv.cc @@ -184,9 +184,7 @@ void DelCmd::Split(std::shared_ptr slot, const HintKeys& hint_keys) { } } -void DelCmd::Merge() { - res_.AppendInteger(split_res_); -} +void DelCmd::Merge() { res_.AppendInteger(split_res_); } void IncrCmd::DoInitial() { if (!CheckArg(argv_.size())) { @@ -804,9 +802,7 @@ void ExistsCmd::Split(std::shared_ptr slot, const HintKeys& hint_keys) { } } -void ExistsCmd::Merge() { - res_.AppendInteger(split_res_); -} +void ExistsCmd::Merge() { res_.AppendInteger(split_res_); } void ExpireCmd::DoInitial() { if (!CheckArg(argv_.size())) { @@ -1088,10 +1084,33 @@ void TypeCmd::DoInitial() { } void TypeCmd::Do(std::shared_ptr slot) { - std::string res; - rocksdb::Status s = slot->db()->Type(key_, &res); + std::vector types(1); + rocksdb::Status s = slot->db()->GetType(key_, true, types); if (s.ok()) { - res_.AppendContent("+" + res); + res_.AppendContent("+" + types[0]); + } else { + res_.SetRes(CmdRes::kErrOther, s.ToString()); + } +} + +void PTypeCmd::DoInitial() { + if (!CheckArg(argv_.size())) { + res_.SetRes(CmdRes::kWrongNum, kCmdNameType); + return; + } + key_ = argv_[1]; +} + +void PTypeCmd::Do(std::shared_ptr slot) { + std::vector types(5); + rocksdb::Status s = slot->db()->GetType(key_, false, types); + + if (s.ok()) { + res_.AppendArrayLen(types.size()); + for (const auto& vs : types) { + res_.AppendStringLen(vs.size()); + res_.AppendContent(vs); + } } else { res_.SetRes(CmdRes::kErrOther, s.ToString()); } @@ -1328,8 +1347,7 @@ void PKScanRangeCmd::Do(std::shared_ptr slot) { std::string next_key; std::vector keys; std::vector kvs; - rocksdb::Status s = - slot->db()->PKScanRange(type_, key_start_, key_end_, pattern_, limit_, &keys, &kvs, &next_key); + rocksdb::Status s = slot->db()->PKScanRange(type_, key_start_, key_end_, pattern_, limit_, &keys, &kvs, &next_key); if (s.ok()) { res_.AppendArrayLen(2); diff --git a/src/pika_migrate_thread.cc b/src/pika_migrate_thread.cc index 2c99dde7e8..1630df76cc 100644 --- a/src/pika_migrate_thread.cc +++ b/src/pika_migrate_thread.cc @@ -662,9 +662,9 @@ int PikaMigrateThread::ReqMigrateOne(const std::string &key, const std::shared_p std::unique_lock lm(migrator_mutex_); int slot_id = GetSlotID(key); - std::string type_str; + std::vector type_str(1); char key_type; - rocksdb::Status s = slot->db()->Type(key, &type_str); + rocksdb::Status s = slot->db()->GetType(key, true, type_str); if (!s.ok()) { if (s.IsNotFound()) { LOG(INFO) << "PikaMigrateThread::ReqMigrateOne key: " << key << " not found"; @@ -675,20 +675,20 @@ int PikaMigrateThread::ReqMigrateOne(const std::string &key, const std::shared_p } } - if (type_str == "string") { + if (type_str[0] == "string") { key_type = 'k'; - } else if (type_str == "hash") { + } else if (type_str[0] == "hash") { key_type = 'h'; - } else if (type_str == "list") { + } else if (type_str[0] == "list") { key_type = 'l'; - } else if (type_str == "set") { + } else if (type_str[0] == "set") { key_type = 's'; - } else if (type_str == "zset") { + } else if (type_str[0] == "zset") { key_type = 'z'; - } else if (type_str == "none") { + } else if (type_str[0] == "none") { return 0; } else { - LOG(WARNING) << "PikaMigrateThread::ReqMigrateOne key: " << key << " type: " << type_str << " is illegal"; + LOG(WARNING) << "PikaMigrateThread::ReqMigrateOne key: " << key << " type: " << type_str[0] << " is illegal"; return -1; } diff --git a/src/pika_slot_command.cc b/src/pika_slot_command.cc index 0e0bc3ce01..0770d12ca5 100644 --- a/src/pika_slot_command.cc +++ b/src/pika_slot_command.cc @@ -891,22 +891,22 @@ void RemSlotKey(const std::string key, const std::shared_ptr& slot) { } int GetKeyType(const std::string key, std::string &key_type, const std::shared_ptr& slot) { - std::string type_str; - rocksdb::Status s = slot->db()->Type(key, &type_str); + std::vector type_str(1); + rocksdb::Status s = slot->db()->GetType(key, true, type_str); if (!s.ok()) { LOG(WARNING) << "Get key type error: " << key << " " << s.ToString(); key_type = ""; return -1; } - if (type_str == "string") { + if (type_str[0] == "string") { key_type = "k"; - } else if (type_str == "hash") { + } else if (type_str[0] == "hash") { key_type = "h"; - } else if (type_str == "list") { + } else if (type_str[0] == "list") { key_type = "l"; - } else if (type_str == "set") { + } else if (type_str[0] == "set") { key_type = "s"; - } else if (type_str == "zset") { + } else if (type_str[0] == "zset") { key_type = "z"; } else { LOG(WARNING) << "Get key type error: " << key; @@ -1079,8 +1079,8 @@ void SlotsMgrtTagSlotCmd::Do(std::shared_ptr slot) { // check key type int SlotsMgrtTagOneCmd::KeyTypeCheck(const std::shared_ptr& slot) { - std::string type_str; - rocksdb::Status s = slot->db()->Type(key_, &type_str); + std::vector type_str(1); + rocksdb::Status s = slot->db()->GetType(key_, true, type_str); if (!s.ok()) { if (s.IsNotFound()) { LOG(INFO) << "Migrate slot key " << key_ << " not found"; @@ -1091,15 +1091,15 @@ int SlotsMgrtTagOneCmd::KeyTypeCheck(const std::shared_ptr& slot) { } return -1; } - if (type_str == "string") { + if (type_str[0] == "string") { key_type_ = 'k'; - } else if (type_str == "hash") { + } else if (type_str[0] == "hash") { key_type_ = 'h'; - } else if (type_str == "list") { + } else if (type_str[0] == "list") { key_type_ = 'l'; - } else if (type_str == "set") { + } else if (type_str[0] == "set") { key_type_ = 's'; - } else if (type_str == "zset") { + } else if (type_str[0] == "zset") { key_type_ = 'z'; } else { LOG(WARNING) << "Migrate slot key: " << key_ << " not found"; diff --git a/src/storage/include/storage/storage.h b/src/storage/include/storage/storage.h index 736f3ad3d0..36beab5a35 100644 --- a/src/storage/include/storage/storage.h +++ b/src/storage/include/storage/storage.h @@ -131,8 +131,7 @@ struct BGTask { Operation operation; std::string argv; - BGTask(const DataType& _type = DataType::kAll, const Operation& _opeation = Operation::kNone, - std::string _argv = "") + BGTask(const DataType& _type = DataType::kAll, const Operation& _opeation = Operation::kNone, std::string _argv = "") : type(_type), operation(_opeation), argv(std::move(_argv)) {} }; @@ -424,7 +423,7 @@ class Storage { // Removes and returns several random elements specified by count from the set value store at key. Status SPop(const Slice& key, std::vector* members, int64_t count); - + // When called with just the key argument, return a random element from the // set value stored at key. // when called with the additional count argument, return an array of count @@ -968,8 +967,12 @@ class Storage { // return > 0 TTL in seconds std::map TTL(const Slice& key, std::map* type_status); - // Reutrns the data type of the key - Status Type(const std::string& key, std::string* type); + // Reutrns the data all type of the key + // if single is true, the query will return the first one + Status GetType(const std::string& key, bool single, std::vector& types); + + // Reutrns the data all type of the key + Status Type(const std::string& key, std::vector& types); Status Keys(const DataType& data_type, const std::string& pattern, std::vector* keys); @@ -1019,7 +1022,7 @@ class Storage { Status SetOptions(const OptionType& option_type, const std::string& db_type, const std::unordered_map& options); - void GetRocksDBInfo(std::string &info); + void GetRocksDBInfo(std::string& info); private: std::unique_ptr strings_db_; @@ -1027,21 +1030,21 @@ class Storage { std::unique_ptr sets_db_; std::unique_ptr zsets_db_; std::unique_ptr lists_db_; - std::atomic is_opened_; + std::atomic is_opened_ = false; std::unique_ptr> cursors_store_; // Storage start the background thread for compaction task - pthread_t bg_tasks_thread_id_; + pthread_t bg_tasks_thread_id_ = 0; pstd::Mutex bg_tasks_mutex_; pstd::CondVar bg_tasks_cond_var_; std::queue bg_tasks_queue_; - std::atomic current_task_type_; - std::atomic bg_tasks_should_exit_; + std::atomic current_task_type_ = kNone; + std::atomic bg_tasks_should_exit_ = false; // For scan keys in data base - std::atomic scan_keynum_exit_; + std::atomic scan_keynum_exit_ = false; }; } // namespace storage diff --git a/src/storage/src/storage.cc b/src/storage/src/storage.cc index 0ecdc253a4..5e53c4a787 100644 --- a/src/storage/src/storage.cc +++ b/src/storage/src/storage.cc @@ -48,12 +48,7 @@ Status StorageOptions::ResetOptions(const OptionType& option_type, return Status::OK(); } -Storage::Storage() - : - is_opened_(false), - current_task_type_(kNone), - bg_tasks_should_exit_(false), - scan_keynum_exit_(false) { +Storage::Storage() { cursors_store_ = std::make_unique>(); cursors_store_->SetCapacity(5000); @@ -1237,57 +1232,64 @@ std::map Storage::TTL(const Slice& key, std::mapclear(); +Status Storage::GetType(const std::string& key, bool single, std::vector& types) { + types.clear(); Status s; std::string value; s = strings_db_->Get(key, &value); if (s.ok()) { - *type = "string"; - return s; + types.emplace_back("string"); } else if (!s.IsNotFound()) { return s; } + if (single && !types.empty()) { + return s; + } int32_t hashes_len = 0; s = hashes_db_->HLen(key, &hashes_len); if (s.ok() && hashes_len != 0) { - *type = "hash"; - return s; + types.emplace_back("hash"); } else if (!s.IsNotFound()) { return s; } + if (single && !types.empty()) { + return s; + } uint64_t lists_len = 0; s = lists_db_->LLen(key, &lists_len); if (s.ok() && lists_len != 0) { - *type = "list"; - return s; + types.emplace_back("list"); } else if (!s.IsNotFound()) { return s; } + if (single && !types.empty()) { + return s; + } int32_t zsets_size = 0; s = zsets_db_->ZCard(key, &zsets_size); if (s.ok() && zsets_size != 0) { - *type = "zset"; - return s; + types.emplace_back("zset"); } else if (!s.IsNotFound()) { return s; } + if (single && !types.empty()) { + return s; + } int32_t sets_size = 0; s = sets_db_->SCard(key, &sets_size); if (s.ok() && sets_size != 0) { - *type = "set"; - return s; + types.emplace_back("set"); } else if (!s.IsNotFound()) { return s; } - - *type = "none"; + if (single && types.empty()) { + types.emplace_back("none"); + } return Status::OK(); } @@ -1390,7 +1392,7 @@ Status Storage::PfAdd(const Slice& key, const std::vector& values, } HyperLogLog log(kPrecision, registers); auto previous = static_cast(log.Estimate()); - for (const auto & value : values) { + for (const auto& value : values) { result = log.Add(value.data(), value.size()); } HyperLogLog update_log(kPrecision, result); @@ -1749,12 +1751,12 @@ Status Storage::SetOptions(const OptionType& option_type, const std::string& db_ return s; } -void Storage::GetRocksDBInfo(std::string &info) { - strings_db_->GetRocksDBInfo(info, "strings_"); - hashes_db_->GetRocksDBInfo(info, "hashes_"); - lists_db_->GetRocksDBInfo(info, "lists_"); - sets_db_->GetRocksDBInfo(info, "sets_"); - zsets_db_->GetRocksDBInfo(info, "zsets_"); +void Storage::GetRocksDBInfo(std::string& info) { + strings_db_->GetRocksDBInfo(info, "strings_"); + hashes_db_->GetRocksDBInfo(info, "hashes_"); + lists_db_->GetRocksDBInfo(info, "lists_"); + sets_db_->GetRocksDBInfo(info, "sets_"); + zsets_db_->GetRocksDBInfo(info, "zsets_"); } } // namespace storage diff --git a/tests/test_helper.tcl b/tests/test_helper.tcl index 8106ea5a78..0f19b51d8b 100644 --- a/tests/test_helper.tcl +++ b/tests/test_helper.tcl @@ -48,7 +48,9 @@ set ::all_tests { # unit/bitops # unit/memefficiency # unit/hyperloglog + unit/type unit/command + unit/type } # because the comment not works in tcl list, use regsub to ignore the item starting with '#' diff --git a/tests/unit/type.tcl b/tests/unit/type.tcl new file mode 100644 index 0000000000..2b5b9045ab --- /dev/null +++ b/tests/unit/type.tcl @@ -0,0 +1,50 @@ +start_server {tags {"type"}} { + + test "type none" { + r flushdb + assert_equal none [r type key] + } + + test "type command" { + r flushdb + + r set key1 key1 + assert_equal string [r type key1] + + r hset key2 key key2 + assert_equal hash [r type key2] + + r lpush key3 key3 + assert_equal list [r type key3] + + r zadd key4 100 key4 + assert_equal zset [r type key4] + + r sadd key5 key5 + assert_equal set [r type key5] + } + + test "ptype none" { + r flushdb + assert_equal {} [r ptype key] + } + + test "ptype command" { + r flushdb + + r set key1 key1 + assert_equal string [r ptype key1] + + r hset key1 key key1 + assert_equal {string hash} [r ptype key1] + + r lpush key1 key1 + assert_equal {string hash list} [r ptype key1] + + r zadd key1 100 key1 + assert_equal {string hash list zset} [r ptype key1] + + r sadd key1 key1 + assert_equal {string hash list zset set} [r ptype key1] + } +} \ No newline at end of file