2022-01-29 13:12:55 +08:00
|
|
|
// Copyright 2022, Roman Gershman. All rights reserved.
|
2021-12-07 14:27:11 +08:00
|
|
|
// See LICENSE for licensing terms.
|
|
|
|
//
|
|
|
|
#include "server/debugcmd.h"
|
|
|
|
|
|
|
|
#include <absl/strings/str_cat.h>
|
|
|
|
|
2022-01-24 02:53:45 +08:00
|
|
|
#include <boost/fiber/operations.hpp>
|
2022-01-29 13:12:55 +08:00
|
|
|
#include <filesystem>
|
2022-01-24 02:53:45 +08:00
|
|
|
|
2021-12-07 14:27:11 +08:00
|
|
|
#include "base/logging.h"
|
|
|
|
#include "server/engine_shard_set.h"
|
2021-12-20 17:42:55 +08:00
|
|
|
#include "server/error.h"
|
2022-04-12 13:34:48 +08:00
|
|
|
#include "server/main_service.h"
|
2022-01-29 13:12:55 +08:00
|
|
|
#include "server/rdb_load.h"
|
2021-12-20 17:42:55 +08:00
|
|
|
#include "server/string_family.h"
|
2022-04-12 13:34:48 +08:00
|
|
|
#include "server/transaction.h"
|
2022-01-24 02:53:45 +08:00
|
|
|
#include "util/uring/uring_fiber_algo.h"
|
2022-01-29 13:12:55 +08:00
|
|
|
#include "util/uring/uring_file.h"
|
|
|
|
|
|
|
|
DECLARE_string(dir);
|
|
|
|
DECLARE_string(dbfilename);
|
2021-12-07 14:27:11 +08:00
|
|
|
|
|
|
|
namespace dfly {
|
|
|
|
|
|
|
|
using namespace std;
|
2022-01-24 02:53:45 +08:00
|
|
|
using namespace util;
|
|
|
|
namespace this_fiber = ::boost::this_fiber;
|
2022-04-12 13:34:48 +08:00
|
|
|
using boost::intrusive_ptr;
|
2022-01-24 02:53:45 +08:00
|
|
|
using boost::fibers::fiber;
|
2022-04-05 03:48:45 +08:00
|
|
|
using namespace facade;
|
2022-01-29 13:12:55 +08:00
|
|
|
namespace fs = std::filesystem;
|
2021-12-07 14:27:11 +08:00
|
|
|
|
|
|
|
struct PopulateBatch {
|
2021-12-20 17:42:55 +08:00
|
|
|
DbIndex dbid;
|
2021-12-07 14:27:11 +08:00
|
|
|
uint64_t index[32];
|
|
|
|
uint64_t sz = 0;
|
2021-12-20 17:42:55 +08:00
|
|
|
|
|
|
|
PopulateBatch(DbIndex id) : dbid(id) {
|
|
|
|
}
|
2021-12-07 14:27:11 +08:00
|
|
|
};
|
|
|
|
|
2022-01-24 02:53:45 +08:00
|
|
|
void DoPopulateBatch(std::string_view prefix, size_t val_size, const SetCmd::SetParams& params,
|
|
|
|
const PopulateBatch& ps) {
|
2021-12-20 17:42:55 +08:00
|
|
|
SetCmd sg(&EngineShard::tlocal()->db_slice());
|
2021-12-07 14:27:11 +08:00
|
|
|
|
|
|
|
for (unsigned i = 0; i < ps.sz; ++i) {
|
|
|
|
string key = absl::StrCat(prefix, ":", ps.index[i]);
|
|
|
|
string val = absl::StrCat("value:", ps.index[i]);
|
|
|
|
|
|
|
|
if (val.size() < val_size) {
|
|
|
|
val.resize(val_size, 'x');
|
|
|
|
}
|
2021-12-20 17:42:55 +08:00
|
|
|
sg.Set(params, key, val);
|
2021-12-07 14:27:11 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-12 13:34:48 +08:00
|
|
|
DebugCmd::DebugCmd(ServerFamily* owner, ConnectionContext* cntx) : sf_(*owner), cntx_(cntx) {
|
2021-12-07 14:27:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void DebugCmd::Run(CmdArgList args) {
|
|
|
|
std::string_view subcmd = ArgS(args, 1);
|
|
|
|
if (subcmd == "HELP") {
|
|
|
|
std::string_view help_arr[] = {
|
|
|
|
"DEBUG <subcommand> [<arg> [value] [opt] ...]. Subcommands are:",
|
2022-04-05 03:48:45 +08:00
|
|
|
"OBJECT <key>",
|
|
|
|
" Show low-level info about `key` and associated value.",
|
2022-01-29 13:12:55 +08:00
|
|
|
"RELOAD [option ...]",
|
|
|
|
" Save the RDB on disk (TBD) and reload it back to memory. Valid <option> values:",
|
|
|
|
" * NOSAVE: the database will be loaded from an existing RDB file.",
|
|
|
|
" Examples:",
|
|
|
|
" * DEBUG RELOAD NOSAVE: replace the current database with the contents of an",
|
|
|
|
" existing RDB file.",
|
2021-12-07 14:27:11 +08:00
|
|
|
"POPULATE <count> [<prefix>] [<size>]",
|
|
|
|
" Create <count> string keys named key:<num>. If <prefix> is specified then",
|
|
|
|
" it is used instead of the 'key' prefix.",
|
|
|
|
"HELP",
|
|
|
|
" Prints this help.",
|
|
|
|
};
|
2022-02-03 06:43:09 +08:00
|
|
|
return (*cntx_)->SendSimpleStrArr(help_arr, ABSL_ARRAYSIZE(help_arr));
|
2021-12-07 14:27:11 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
VLOG(1) << "subcmd " << subcmd;
|
|
|
|
|
|
|
|
if (subcmd == "POPULATE") {
|
|
|
|
return Populate(args);
|
|
|
|
}
|
|
|
|
|
2022-01-29 13:12:55 +08:00
|
|
|
if (subcmd == "RELOAD") {
|
|
|
|
return Reload(args);
|
|
|
|
}
|
|
|
|
|
2022-04-05 03:48:45 +08:00
|
|
|
if (subcmd == "OBJECT" && args.size() == 3) {
|
|
|
|
string_view key = ArgS(args, 2);
|
|
|
|
return Inspect(key);
|
|
|
|
}
|
|
|
|
|
2021-12-07 14:27:11 +08:00
|
|
|
string reply = absl::StrCat("Unknown subcommand or wrong number of arguments for '", subcmd,
|
|
|
|
"'. Try DEBUG HELP.");
|
2022-04-04 18:08:16 +08:00
|
|
|
return (*cntx_)->SendError(reply, kSyntaxErr);
|
2021-12-07 14:27:11 +08:00
|
|
|
}
|
|
|
|
|
2022-01-29 13:12:55 +08:00
|
|
|
void DebugCmd::Reload(CmdArgList args) {
|
|
|
|
bool save = true;
|
2022-04-12 13:34:48 +08:00
|
|
|
|
2022-01-29 13:12:55 +08:00
|
|
|
for (size_t i = 2; i < args.size(); ++i) {
|
|
|
|
ToUpper(&args[i]);
|
|
|
|
std::string_view opt = ArgS(args, i);
|
|
|
|
VLOG(1) << "opt " << opt;
|
|
|
|
|
|
|
|
if (opt == "NOSAVE") {
|
|
|
|
save = false;
|
|
|
|
} else {
|
2022-02-03 06:43:09 +08:00
|
|
|
return (*cntx_)->SendError("DEBUG RELOAD only supports the NOSAVE options.");
|
2022-01-29 13:12:55 +08:00
|
|
|
}
|
|
|
|
}
|
2022-04-12 13:34:48 +08:00
|
|
|
|
|
|
|
error_code ec;
|
2022-04-12 21:39:25 +08:00
|
|
|
EngineShardSet& ess = sf_.service().shard_set();
|
2022-04-12 13:34:48 +08:00
|
|
|
|
2022-01-29 13:12:55 +08:00
|
|
|
if (save) {
|
2022-04-12 13:34:48 +08:00
|
|
|
string err_details;
|
|
|
|
const CommandId* cid = sf_.service().FindCmd("SAVE");
|
|
|
|
CHECK_NOTNULL(cid);
|
2022-04-12 21:39:25 +08:00
|
|
|
intrusive_ptr<Transaction> trans(new Transaction{cid, &ess});
|
2022-04-12 13:34:48 +08:00
|
|
|
trans->InitByArgs(0, {});
|
|
|
|
ec = sf_.DoSave(trans.get(), &err_details);
|
|
|
|
if (ec) {
|
|
|
|
return (*cntx_)->SendError(absl::StrCat(err_details, ec.message()));
|
|
|
|
}
|
2022-01-29 13:12:55 +08:00
|
|
|
}
|
|
|
|
|
2022-04-12 13:34:48 +08:00
|
|
|
const CommandId* cid = sf_.service().FindCmd("FLUSHALL");
|
2022-04-12 21:39:25 +08:00
|
|
|
intrusive_ptr<Transaction> flush_trans(new Transaction{cid, &ess});
|
2022-04-12 13:34:48 +08:00
|
|
|
flush_trans->InitByArgs(0, {});
|
|
|
|
ec = sf_.DoFlush(flush_trans.get(), DbSlice::kDbAll);
|
|
|
|
if (ec) {
|
|
|
|
LOG(ERROR) << "Error flushing db " << ec.message();
|
2022-01-29 13:12:55 +08:00
|
|
|
}
|
|
|
|
|
2022-04-12 13:34:48 +08:00
|
|
|
string last_save_file = sf_.LastSaveFile();
|
|
|
|
fs::path path(last_save_file);
|
|
|
|
|
|
|
|
if (last_save_file.empty()) {
|
|
|
|
fs::path dir_path(FLAGS_dir);
|
|
|
|
string filename = FLAGS_dbfilename;
|
|
|
|
dir_path.append(filename);
|
|
|
|
path = dir_path;
|
|
|
|
}
|
2022-01-29 13:12:55 +08:00
|
|
|
auto res = uring::OpenRead(path.generic_string());
|
|
|
|
|
|
|
|
if (!res) {
|
2022-02-03 06:43:09 +08:00
|
|
|
(*cntx_)->SendError(res.error().message());
|
2022-01-29 13:12:55 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
io::FileSource fs(*res);
|
|
|
|
|
2022-04-12 21:39:25 +08:00
|
|
|
RdbLoader loader(&ess);
|
2022-04-12 13:34:48 +08:00
|
|
|
ec = loader.Load(&fs);
|
2022-01-29 13:12:55 +08:00
|
|
|
|
|
|
|
if (ec) {
|
2022-02-03 06:43:09 +08:00
|
|
|
(*cntx_)->SendError(ec.message());
|
2022-01-29 13:12:55 +08:00
|
|
|
} else {
|
2022-02-03 06:43:09 +08:00
|
|
|
(*cntx_)->SendOk();
|
2022-01-29 13:12:55 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-07 14:27:11 +08:00
|
|
|
void DebugCmd::Populate(CmdArgList args) {
|
|
|
|
if (args.size() < 3 || args.size() > 5) {
|
2022-02-03 06:43:09 +08:00
|
|
|
return (*cntx_)->SendError(
|
2021-12-07 14:27:11 +08:00
|
|
|
"Unknown subcommand or wrong number of arguments for 'populate'. Try DEBUG HELP.");
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t total_count = 0;
|
|
|
|
if (!absl::SimpleAtoi(ArgS(args, 2), &total_count))
|
2022-02-03 06:43:09 +08:00
|
|
|
return (*cntx_)->SendError(kUintErr);
|
2021-12-07 14:27:11 +08:00
|
|
|
std::string_view prefix{"key"};
|
|
|
|
|
|
|
|
if (args.size() > 3) {
|
|
|
|
prefix = ArgS(args, 3);
|
|
|
|
}
|
|
|
|
uint32_t val_size = 0;
|
|
|
|
if (args.size() > 4) {
|
|
|
|
std::string_view str = ArgS(args, 4);
|
|
|
|
if (!absl::SimpleAtoi(str, &val_size))
|
2022-02-03 06:43:09 +08:00
|
|
|
return (*cntx_)->SendError(kUintErr);
|
2021-12-07 14:27:11 +08:00
|
|
|
}
|
|
|
|
|
2022-04-12 13:34:48 +08:00
|
|
|
ProactorPool& pp = sf_.service().proactor_pool();
|
|
|
|
size_t runners_count = pp.size();
|
2021-12-07 14:27:11 +08:00
|
|
|
vector<pair<uint64_t, uint64_t>> ranges(runners_count - 1);
|
|
|
|
uint64_t batch_size = total_count / runners_count;
|
|
|
|
size_t from = 0;
|
|
|
|
for (size_t i = 0; i < ranges.size(); ++i) {
|
|
|
|
ranges[i].first = from;
|
|
|
|
ranges[i].second = batch_size;
|
|
|
|
from += batch_size;
|
|
|
|
}
|
|
|
|
ranges.emplace_back(from, total_count - from);
|
|
|
|
|
2022-01-24 02:53:45 +08:00
|
|
|
vector<fiber> fb_arr(ranges.size());
|
2021-12-07 14:27:11 +08:00
|
|
|
for (size_t i = 0; i < ranges.size(); ++i) {
|
2022-01-27 04:11:35 +08:00
|
|
|
auto range = ranges[i];
|
|
|
|
|
|
|
|
// whatever we do, we should not capture i by reference.
|
2022-04-12 13:34:48 +08:00
|
|
|
fb_arr[i] = pp.at(i)->LaunchFiber(
|
2022-04-12 21:39:25 +08:00
|
|
|
[range, prefix, val_size, this] { this->PopulateRangeFiber(range.first, range.second, prefix, val_size); });
|
2021-12-07 14:27:11 +08:00
|
|
|
}
|
|
|
|
for (auto& fb : fb_arr)
|
|
|
|
fb.join();
|
|
|
|
|
2022-02-03 06:43:09 +08:00
|
|
|
(*cntx_)->SendOk();
|
2021-12-07 14:27:11 +08:00
|
|
|
}
|
|
|
|
|
2022-01-24 02:53:45 +08:00
|
|
|
void DebugCmd::PopulateRangeFiber(uint64_t from, uint64_t len, std::string_view prefix,
|
|
|
|
unsigned value_len) {
|
|
|
|
this_fiber::properties<FiberProps>().set_name("populate_range");
|
2022-01-27 04:11:35 +08:00
|
|
|
VLOG(1) << "PopulateRange: " << from << "-" << (from + len - 1);
|
2022-01-24 02:53:45 +08:00
|
|
|
|
|
|
|
string key = absl::StrCat(prefix, ":");
|
|
|
|
size_t prefsize = key.size();
|
2022-01-27 04:11:35 +08:00
|
|
|
DbIndex db_indx = cntx_->db_index();
|
2022-04-12 13:34:48 +08:00
|
|
|
EngineShardSet& ess = sf_.service().shard_set();
|
|
|
|
std::vector<PopulateBatch> ps(ess.size(), PopulateBatch{db_indx});
|
2022-01-24 02:53:45 +08:00
|
|
|
SetCmd::SetParams params{db_indx};
|
|
|
|
|
|
|
|
for (uint64_t i = from; i < from + len; ++i) {
|
|
|
|
absl::StrAppend(&key, i);
|
2022-04-12 13:34:48 +08:00
|
|
|
ShardId sid = Shard(key, ess.size());
|
2022-01-24 02:53:45 +08:00
|
|
|
key.resize(prefsize);
|
|
|
|
|
|
|
|
auto& pops = ps[sid];
|
|
|
|
pops.index[pops.sz++] = i;
|
|
|
|
if (pops.sz == 32) {
|
2022-04-12 13:34:48 +08:00
|
|
|
ess.Add(sid, [=, p = pops] {
|
2022-01-24 02:53:45 +08:00
|
|
|
DoPopulateBatch(prefix, value_len, params, p);
|
|
|
|
if (i % 50 == 0) {
|
|
|
|
this_fiber::yield();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// we capture pops by value so we can override it here.
|
|
|
|
pops.sz = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-12 13:34:48 +08:00
|
|
|
ess.RunBriefInParallel([&](EngineShard* shard) {
|
2022-01-24 02:53:45 +08:00
|
|
|
DoPopulateBatch(prefix, value_len, params, ps[shard->shard_id()]);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-04-05 03:48:45 +08:00
|
|
|
void DebugCmd::Inspect(string_view key) {
|
2022-04-12 13:34:48 +08:00
|
|
|
EngineShardSet& ess = sf_.service().shard_set();
|
|
|
|
ShardId sid = Shard(key, ess.size());
|
2022-04-05 03:48:45 +08:00
|
|
|
using ObjInfo = pair<unsigned, unsigned>; // type, encoding.
|
|
|
|
|
|
|
|
auto cb = [&]() -> facade::OpResult<ObjInfo> {
|
|
|
|
auto& db_slice = EngineShard::tlocal()->db_slice();
|
|
|
|
PrimeIterator it = db_slice.FindExt(cntx_->db_index(), key).first;
|
|
|
|
if (IsValid(it)) {
|
|
|
|
return ObjInfo(it->second.ObjType(), it->second.Encoding());
|
|
|
|
}
|
|
|
|
return OpStatus::KEY_NOTFOUND;
|
|
|
|
};
|
|
|
|
|
2022-04-12 13:34:48 +08:00
|
|
|
OpResult<ObjInfo> res = ess.Await(sid, cb);
|
2022-04-05 03:48:45 +08:00
|
|
|
if (res) {
|
|
|
|
string resp = absl::StrCat("Value encoding:", strEncoding(res->second));
|
|
|
|
(*cntx_)->SendSimpleString(resp);
|
|
|
|
} else {
|
|
|
|
(*cntx_)->SendError(res.status());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-07 14:27:11 +08:00
|
|
|
} // namespace dfly
|