-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Implement create space, drop space, add hosts, show hosts , remove hosts via MetaClient #223
Conversation
Can one of the admins verify this patch? |
src/graph/AdminExecutor.cpp
Outdated
|
||
switch (show_kind) { | ||
case ShowKind::kShowHosts: | ||
InitMetaClient(); |
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.
About using the MetaClient
, here are my thoughts:
- You just should not create a meta client per action, not even per request. As for the current infra, the
ExecutionEngine
ought to create and own it, and eachExecutionContext
shall have a reference to it via a raw pointer. - We disallow any synchronous action in the service, e.g. RPC. Here if the meta client doesn't support the async calls, we have to adjust it to do so.
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.
Excellent! I will do it.
src/parser/parser.yy
Outdated
; | ||
|
||
create_space_sentence | ||
: KW_CREATE KW_SPACE L_PAREN STRING COMMA INTEGER COMMA INTEGER R_PAREN { |
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.
I opt to define the syntax like this:
KW_CREATE KW_SPACE LABEL L_PAREN space_opt_list R_PAREN
space_opt_list: space_opt_item
| space_opt_list COMMA space_opt_item
| space_opt_list COMMA
space_opt_item : KW_ENGINE_TYPE ASSIGN LABEL
| KW_PARTITION_NUM ASSIGN INTEGER
| ...
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.
Right on!
@steppenwolfyuetong For your information, @dangleptr is working on the async interfaces of MetaClient |
Fair enough! |
reconstruct create space and ExecutionEngine could create and own metaClient. |
src/graph/AdminExecutor.cpp
Outdated
void CreateSpaceExecutor::execute() { | ||
CHECK_GT(partNum_, 0) << "partition_num value illegal"; | ||
CHECK_GT(replicaFactor_, 0) << "replica_factor value illegal"; | ||
auto ret = ectx()->getMetaClient()->createSpace(*spaceName_, partNum_, replicaFactor_).get(); |
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.
You should use the callback here, don't block the worker.
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.
👍 I will do it.
while (iter->valid()) { | ||
auto key = iter->key(); | ||
resp_.set_code(cpp2::ErrorCode::SUCCEEDED); | ||
kvstore_->asyncRemove(kDefaultSpaceId_, kDefaultPartId_, key.toString(), |
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.
Use removeRange here maybe more simple.
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.
Excellent! I will do it.
|
||
for (auto& hostKey : hostsKey) { | ||
resp_.set_code(cpp2::ErrorCode::SUCCEEDED); | ||
kvstore_->asyncRemove(kDefaultSpaceId_, kDefaultPartId_, hostKey, |
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.
ditto
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.
Here I think not use removeRange.
Because the hosts to be remove are not a range, are a list.
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.
ditto
} | ||
|
||
resp_.set_code(cpp2::ErrorCode::SUCCEEDED); | ||
onFinished(); |
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.
When you call onFinish, the instance of current processor will be destroyed. So if your process procedure is async, onFinish should be called in callback.
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.
Excellent! I will do it.
@@ -15,6 +15,10 @@ namespace graph { | |||
ExecutionEngine::ExecutionEngine() { | |||
schemaManager_ = std::make_unique<SchemaManager>(); | |||
storage_ = std::make_unique<StorageService>(schemaManager_.get()); | |||
|
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.
Add a TODO comment here , schemaManager/StorageClient should shared one meta client instance in the future
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.
👍
@@ -84,6 +84,29 @@ StatusOr<GraphSpaceID> BaseProcessor<RESP>::spaceExist(const std::string& name) | |||
return Status::SpaceNotFound(); | |||
} | |||
|
|||
// TODO(YT) Maybe we could use index to improve the efficient | |||
template<typename RESP> | |||
Status BaseProcessor<RESP>::hostsExist(std::vector<std::string> &hostsKey) { |
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.
If you don't modify "hostKey", pass const reference is a better choice.
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.
Good catch!👍
// TODO(YT) Maybe we could use index to improve the efficient | ||
template<typename RESP> | ||
Status BaseProcessor<RESP>::hostsExist(std::vector<std::string> &hostsKey) { | ||
for (auto& hostKey : hostsKey) { |
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.
Use const auto&
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.
Good catch!
@@ -84,6 +84,29 @@ StatusOr<GraphSpaceID> BaseProcessor<RESP>::spaceExist(const std::string& name) | |||
return Status::SpaceNotFound(); | |||
} | |||
|
|||
// TODO(YT) Maybe we could use index to improve the efficient |
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.
The hostKey is rowKey now, i think we don't need another index here.
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.
Yeah, it Is my negligence.
namespace meta { | ||
|
||
void DropSpaceProcessor::process(const cpp2::DropSpaceReq& req) { | ||
guard_ = std::make_unique<std::lock_guard<std::mutex>>( |
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.
for the lock, i have optimized it in #225
I will merge it recently, you'd better rebase on it.
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.
👍
[this] (kvstore::ResultCode code, HostAddr leader) { | ||
UNUSED(leader); | ||
this->resp_.set_code(to(code)); | ||
this->onFinished(); |
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.
It's dangerous here. Because we can't ensure the callback order for the two asyncRemove
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.
Excellent! I will fix it.
[this] (kvstore::ResultCode code, HostAddr leader) { | ||
UNUSED(leader); | ||
this->resp_.set_code(to(code)); | ||
this->onFinished(); |
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.
It's dangerous here. Because we can't ensure the callback order for the two asyncRemove
jenkins go |
need to deal with some problems caused by the merger, and request review later |
Unit testing failed. |
The update has been completed, please review。 |
Ready to review now? |
@dangleptr, yeah, please focus on the merge in yesterday. |
src/graph/AdminExecutor.cpp
Outdated
Status AddHostsExecutor::prepare() { | ||
host_ = sentence_->hosts(); | ||
if (host_.size() == 0) { | ||
LOG(FATAL) << "Add hosts Sentence host address illegal"; |
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.
FATAL means core dump. We'd better return ErrorCode here.
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.
Good point.
src/graph/AdminExecutor.cpp
Outdated
Status RemoveHostsExecutor::prepare() { | ||
host_ = sentence_->hosts(); | ||
if (host_.size() == 0) { | ||
LOG(FATAL) << "Remove hosts Sentence host address illegal"; |
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.
ditto
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.
Good catch.
src/graph/AdminExecutor.cpp
Outdated
|
||
|
||
void CreateSpaceExecutor::execute() { | ||
CHECK_GT(partNum_, 0) << "partition_num value illegal"; |
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.
You'd better check the numbers in prepare and return errorCode if failed.
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.
👍,good catch!
src/graph/AdminExecutor.cpp
Outdated
|
||
auto cb = [this] (auto &&resp) { | ||
auto spaceId = resp.value(); | ||
if (spaceId == 0) { |
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.
Why we need the if scope?
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.
Here I will use if (spaceId <= 0)
to ensure that return a valid spaceId.
} catch (std::exception& e) { | ||
LOG(ERROR) << "Convert failed for " << val << ", msg " << e.what(); | ||
} | ||
return *reinterpret_cast<const TagID*>(val.c_str()); |
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.
Generally, we prefer folly::to than cast by ourself. folly::to has error checks inside, it is important.
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.
Yeah,I see what you mean.
But storing to kvstore when creating space and getting from kvstore when droping space should be consistent,Otherwise an error occurs when getting. Also to Tag.
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.
If here using folly::to, storing to kvstore also uses folly::to, not cast by ourself.
std::string end = MetaUtils::partKey(spaceId, 0x7FFFFFFF); | ||
resp_.set_code(cpp2::ErrorCode::SUCCEEDED); | ||
|
||
kvstore_->asyncRemoveRange(kDefaultSpaceId_, kDefaultPartId_, start, end, |
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.
I reconsider the multi delete here. I think we should implement a batchDelete interface in kvstore. You could refer rocksdb::WriteBatch to implement it (Open another issue for it). wdyt?
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.
make sense.
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.
We can't use asyncXXX in this way. Currently, to keep safe, you should wait for all asyncXXX finished before invoke onFinished method.
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.
Yeah, here use multi asyncXXX and collectAll.
batchDelete need to implement for remove hosts;
|
||
for (auto& hostKey : hostsKey) { | ||
resp_.set_code(cpp2::ErrorCode::SUCCEEDED); | ||
kvstore_->asyncRemove(kDefaultSpaceId_, kDefaultPartId_, hostKey, |
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.
ditto
src/parser/AdminSentences.cpp
Outdated
|
||
std::string ShowSentence::toString() const { | ||
std::string buf; | ||
switch (showKind_) { |
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.
buf.reserve
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.
👍
src/parser/AdminSentences.cpp
Outdated
char buf[256]; | ||
switch (optType_) { | ||
case PARTITION_NUM: | ||
snprintf(buf, sizeof(buf), "partition_num = %ld", boost::get<int64_t>(optValue_)); |
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.
You could use folly::stringPrintf directly.
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.
Good point!
fixed, please review again |
jenkins go |
Unit testing passed. |
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.
Well done! Thanks for taking care of this
I have some inline comments
src/graph/AdminExecutor.cpp
Outdated
|
||
using nebula::network::NetworkUtils; | ||
|
||
ShowExecutor::ShowExecutor(Sentence *sentence, |
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.
I would highly recommend to break this file into multiple files, with each file contains one Executor implementation
Generally speaking, we want to keep every class in its own header file and implementation file, unless for those accessory classes
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.
Good, this makes sense.
src/graph/AdminExecutor.cpp
Outdated
|
||
|
||
void ShowExecutor::execute() { | ||
auto show_kind = sentence_->showKind(); |
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.
We don't use C style variable name, please rename it to showKind
In addition, I discussed with @dutor this afternoon, it seems we use kind pretty much across the entire QE. But in American English, "type" is more preferable over "kind"
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.
Right on!👍,I will fix it.
cpp2::ExecutionResponse resp; | ||
std::string query = "add hosts(\"127.0.0.1:1000\", \"127.0.0.1:1100\")"; | ||
auto code = client->execute(query, resp); | ||
ASSERT_EQ(cpp2::ErrorCode::SUCCEEDED, code); |
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.
I don't think you need ASSERT_EQ()
here. EXPECT_EQ()
could be more suitable
ASSERT_EQ()
will stop running all following code, while EXPECT_EQ()
will only print an error message and continue the testcase execusion
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.
Right on!
But I check the reference manual about *_EQ, the rule has changed. As follow:
Historical note: Before February 2016 *_EQ had a convention of calling it as ASSERT_EQ(expected, actual), so lots of existing code uses this order. Now *_EQ treats both parameters in the same way.
refer to https://github.com/google/googletest/blob/master/googletest/docs/primer.md
IMO,we maintain the original specification as *_EQ(expected, actual)
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.
@sherman-the-tank Here are my opinions:
From my experiences, in most cases, if an assertion(ASSERT
s or EXPECT
s) fails, the following testings usually make no sense. Even worse, the following testings are error-prone so that we need much more mental overheads to take care of these. So I recommend to use ASSERT
s by default, unless we intentionally want the testing to continue. If you concern about that to fail earlier will print out less infos, I think we ought to take more advantages of the stream operator provided by the testing macros, e.g. ASSERT_TRUE(status.ok()) << status
.
Take following codes as example:
{
cpp2::ListSpacesReq req;
auto* processor = ListSpacesProcessor::instance(kv.get());
auto f = processor->getFuture();
processor->process(req);
auto resp = std::move(f).get();
EXPECT_EQ(cpp2::ErrorCode::SUCCEEDED, resp.code);
EXPECT_EQ(1, resp.spaces.size());
EXPECT_EQ(1, resp.spaces[0].id.get_space_id());
EXPECT_EQ("default_space", resp.spaces[0].name);
}
These code will definitely crash on failure, and nobody noticed it. Besides, reviewers often pay less attention to the testing codes, at least it is true so far.
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.
@dutor, @sherman-the-tank
I think we should use a same rule to unify all testcasts in new pr.
so future operations need to follow the same rules.
wdyt?
fixed, please review again. |
rebase master |
rebase master |
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.
The patch looks more clear now. There are some inline comments, please recheck it.
auto spaceRet = getSpaceId(req.get_space_name()); | ||
|
||
if (!spaceRet.ok()) { | ||
if (spaceRet.status() == Status::SpaceNotFound()) { |
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.
Implement a function that convert Status to cpp2::ErrorCode
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.
Good point
|
||
auto hostsRet = hostsExist(hostsKey); | ||
if (!hostsRet.ok()) { | ||
if (hostsRet == Status::HostNotFound()) { |
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.
ditto
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.
ditto
src/parser/AdminSentences.cpp
Outdated
buf.reserve(256); | ||
switch (optType_) { | ||
case PARTITION_NUM: | ||
buf = folly::stringPrintf("partition_num = %ld", boost::get<int64_t>(optValue_)); |
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.
"buf" seems useless. Return directly
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.
Good catch!
src/parser/AdminSentences.h
Outdated
if (isInt()) { | ||
return asInt(); | ||
} else { | ||
LOG(FATAL) << "partition_num value illegal."; |
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.
Be carefully about FATAL. Do we really want to crash here?
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.
Good catch! 👍
src/parser/AdminSentences.h
Outdated
if (isInt()) { | ||
return asInt(); | ||
} else { | ||
LOG(FATAL) << "replica_factor value illegal."; |
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.
ditto
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.
👍
} | ||
|
||
deleteKeys.emplace_back(MetaUtils::indexKey(EntryType::SPACE, req.get_space_name())); | ||
deleteKeys.emplace_back(MetaUtils::spaceKey(spaceId)); |
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.
Add TODO comments, should we delete Tag/Edge under the space here?
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.
make sense!
@@ -28,7 +28,7 @@ void AddTagProcessor::process(const cpp2::AddTagReq& req) { | |||
auto version = time::TimeUtils::nowInMSeconds(); | |||
TagID tagId = autoIncrementId(); | |||
data.emplace_back(MetaUtils::indexKey(EntryType::TAG, req.get_tag_name()), | |||
std::string(reinterpret_cast<const char*>(&tagId), sizeof(tagId))); | |||
folly::to<std::string>(tagId)); |
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.
Generally, if cast from int to char*, you could cast directly. Because no errors should happen.
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.
AddTagProcessor.cpp is same to CreatesSpaceProcessor.cpp.
`
dangleptr 3 days ago
Generally, we prefer folly::to than cast by ourself. folly::to has error checks inside, it is important.
@steppenwolfyuetong
steppenwolfyuetong 3 days ago •
Author
Yeah,I see what you mean.
But storing to kvstore when creating space and getting from kvstore when droping space should be consistent,Otherwise an error occurs when getting. Also to Tag.
@steppenwolfyuetong
steppenwolfyuetong 3 days ago Author
If here using folly::to, storing to kvstore also uses folly::to, not cast by ourself.
`
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.
Reference to reply in CreatesSpaceProcessor.cpp
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.
fixed according to the offline discussion
fixed and rebase master, please review |
Jenkins go |
jenkins go |
Unit testing passed. |
@dutor, @dangleptr, @sherman-the-tank, Thank you for your meticulous review. 👍 |
…sts via MetaClient
@dutor, fix and rebase master, please review again, thanks. |
src/parser/parser.yy
Outdated
create_space_sentence | ||
: KW_CREATE KW_SPACE LABEL L_PAREN space_opt_list R_PAREN { | ||
auto sentence = new CreateSpaceSentence($3); | ||
sentence->setOpts($5); |
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.
indentation.
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.
Good catch!
src/parser/parser.yy
Outdated
$$->addOpt($1); | ||
} | ||
| space_opt_list COMMA space_opt_item { | ||
$$ = $1; |
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.
indent four spaces.
src/parser/parser.yy
Outdated
$$->addOpt($3); | ||
} | ||
| space_opt_list COMMA { | ||
$$ = $1; |
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.
ditto.
sentence->setOpts($5); | ||
$$ = sentence; | ||
} | ||
; |
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.
Better to add a blank line below.
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.
Yeah, good catch!
@dutor, fix and rebase. thanks. |
Jenkins, go! |
Unit testing passed. |
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.
…sts via MetaClient (vesoft-inc#223) close vesoft-inc#200
…sts via MetaClient (vesoft-inc#223) close vesoft-inc#200
#200 Support some admin sentences in Query Engine.
Implement the following SQL via MetaClient.:
create space
drop space
add hosts
show hosts
remove hosts