Skip to content
This repository has been archived by the owner on Jun 23, 2022. It is now read-only.

feat: support to modify configs without restart #682

Merged
merged 33 commits into from
Dec 22, 2020
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
fee3882
fix
levy5307 Dec 3, 2020
d3a07c0
fix
levy5307 Dec 4, 2020
8df75be
fix
levy5307 Dec 7, 2020
4a3a2c3
fix
levy5307 Dec 7, 2020
6b8f82b
fix
levy5307 Dec 7, 2020
1396ec8
fix
levy5307 Dec 7, 2020
cc93bce
fix
levy5307 Dec 7, 2020
f6e0c2f
Merge branch 'master' into config-update
levy5307 Dec 7, 2020
40c9c8b
fix
levy5307 Dec 7, 2020
84d9fda
Merge branch 'config-update' of github.com:levy5307/rdsn into config-…
levy5307 Dec 7, 2020
02bac72
fix
levy5307 Dec 7, 2020
bfaa2fd
fix
levy5307 Dec 7, 2020
24cec31
fix
levy5307 Dec 7, 2020
1c6cbb6
fix
levy5307 Dec 9, 2020
101b734
fix
levy5307 Dec 9, 2020
406c65a
fix
levy5307 Dec 10, 2020
af2319d
fix
levy5307 Dec 10, 2020
39c015a
fix
levy5307 Dec 15, 2020
e5ab823
fix
levy5307 Dec 15, 2020
d29dcbf
Merge branch 'master' into config-update
acelyc111 Dec 15, 2020
52bfa45
Merge branch 'master' into config-update
levy5307 Dec 16, 2020
f41964d
fix
levy5307 Dec 16, 2020
10b7e93
Merge branch 'master' into config-update
levy5307 Dec 16, 2020
61bb5dc
Merge branch 'master' into config-update
levy5307 Dec 16, 2020
4f3718e
Merge branch 'master' into config-update
levy5307 Dec 16, 2020
3e47c7b
Merge branch 'master' into config-update
levy5307 Dec 17, 2020
e4e3c4e
Merge branch 'master' into config-update
levy5307 Dec 17, 2020
a0af437
Merge branch 'master' into config-update
levy5307 Dec 18, 2020
41d44b4
fix
levy5307 Dec 18, 2020
c87db04
fix
levy5307 Dec 18, 2020
b308b0b
fix
levy5307 Dec 18, 2020
8840fa2
fix
levy5307 Dec 21, 2020
a5fb193
fix
levy5307 Dec 21, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions include/dsn/utility/error_code.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,5 @@ DEFINE_ERR_CODE(ERR_KRB5_INTERNAL)
DEFINE_ERR_CODE(ERR_SASL_INTERNAL)
DEFINE_ERR_CODE(ERR_SASL_INCOMPLETE)
DEFINE_ERR_CODE(ERR_ACL_DENY)
DEFINE_ERR_CODE(ERR_NO_PERMISSION)
} // namespace dsn
22 changes: 22 additions & 0 deletions include/dsn/utility/flags.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,13 @@
#include <string>
#include <cstdint>
#include <functional>
#include "errors.h"
#include "utils.h"

enum class flag_tag
{
FT_MUTABLE = 0, /** flag data is mutable */
};

// Example:
// DSN_DEFINE_string("core", filename, "my_file.txt", "The file to read");
Expand Down Expand Up @@ -52,6 +59,10 @@
dassert(FLAGS_VALIDATOR_FN_##name(FLAGS_##name), "validation failed: %s", #name); \
})

#define DSN_TAG_VARIABLE(name, tag) \
COMPILE_ASSERT(sizeof(decltype(FLAGS_##name)), exist_##name##_##tag); \
static dsn::flag_tagger FLAGS_TAGGER_##name##_##tag(#name, flag_tag::tag)

namespace dsn {

// An utility class that registers a flag upon initialization.
Expand All @@ -74,7 +85,18 @@ class flag_validator
flag_validator(const char *name, std::function<void()>);
};

class flag_tagger
{
public:
flag_tagger(const char *name, const flag_tag &tag);
};

// Loads all the flags from configuration.
extern void flags_initialize();

// update the specified flag to val
extern error_s update_flag(const std::string &name, const std::string &val);

// determine if the tag is exist for the specified flag
extern bool has_tag(const std::string &name, const flag_tag &tag);
} // namespace dsn
5 changes: 5 additions & 0 deletions include/dsn/utility/string_conv.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ inline bool buf2int64(string_view buf, int64_t &result)
return internal::buf2signed(buf, result);
}

inline bool buf2uint32(string_view buf, uint32_t &result)
{
return internal::buf2unsigned(buf, result);
}

inline bool buf2uint64(string_view buf, uint64_t &result)
{
return internal::buf2unsigned(buf, result);
Expand Down
24 changes: 22 additions & 2 deletions include/dsn/utility/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,26 @@

#define TIME_MS_MAX 0xffffffff

// The COMPILE_ASSERT macro can be used to verify that a compile time
// expression is true. For example, you could use it to verify the
// size of a static array:
//
// COMPILE_ASSERT(ARRAYSIZE(content_type_names) == CONTENT_NUM_TYPES,
// content_type_names_incorrect_size);
//
// or to make sure a struct is smaller than a certain size:
//
// COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large);
//
// The second argument to the macro is the name of the variable. If
// the expression is false, most compilers will issue a warning/error
// containing the name of the variable.
struct CompileAssert
{
};

#define COMPILE_ASSERT(expr, msg) static const CompileAssert msg[bool(expr) ? 1 : -1]

namespace dsn {
namespace utils {

Expand Down Expand Up @@ -88,5 +108,5 @@ bool hostname(const dsn::rpc_address &address, std::string *hostname_result);
// valid_ip_network_order -> return TRUE && hostname_result=hostname |
// invalid_ip_network_order -> return FALSE
bool hostname_from_ip(uint32_t ip, std::string *hostname_result);
}
}
} // namespace utils
} // namespace dsn
5 changes: 5 additions & 0 deletions src/http/builtin_http_calls.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,11 @@ namespace dsn {
get_perf_counter_handler(req, resp);
})
.with_help("Gets the value of a perf counter");

register_http_call("updateConfig")
.with_callback(
[](const http_request &req, http_response &resp) { update_config(req, resp); })
.with_help("Updates the value of a config");
}

} // namespace dsn
2 changes: 2 additions & 0 deletions src/http/builtin_http_calls.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,6 @@ extern void get_help_handler(const http_request &req, http_response &resp);

extern void get_recent_start_time_handler(const http_request &req, http_response &resp);

extern void update_config(const http_request &req, http_response &resp);

} // namespace dsn
40 changes: 40 additions & 0 deletions src/http/config_http_service.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

#include <dsn/http/http_server.h>
#include <dsn/utility/flags.h>
#include <dsn/utility/output_utils.h>

namespace dsn {
void update_config(const http_request &req, http_response &resp)
{
if (req.query_args.size() != 1) {
resp.status_code = http_status_code::bad_request;
return;
}

auto iter = req.query_args.begin();
auto res = update_flag(iter->first, iter->second);

utils::table_printer tp;
tp.add_row_name_and_data("update_status", res.description());
std::ostringstream out;
tp.output(out, dsn::utils::table_printer::output_format::kJsonCompact);
resp.body = out.str();
resp.status_code = http_status_code::ok;
}
} // namespace dsn
78 changes: 78 additions & 0 deletions src/utils/flags.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
#include <dsn/utility/flags.h>
#include <dsn/utility/config_api.h>
#include <dsn/utility/singleton.h>
#include <dsn/utility/errors.h>
#include <dsn/utility/string_conv.h>
#include <dsn/c/api_utilities.h>
#include <boost/optional/optional.hpp>
#include <fmt/format.h>

#include <map>

Expand Down Expand Up @@ -37,6 +40,19 @@ class flag_data
} \
break

#define FLAG_DATA_UPDATE_CASE(type, type_enum, suffix) \
case type_enum: \
type tmpval_##type_enum; \
if (!dsn::buf2##suffix(val, tmpval_##type_enum)) { \
return error_s::make(ERR_INVALID_PARAMETERS, fmt::format("{} in invalid", val)); \
} \
value<type>() = tmpval_##type_enum; \
break

#define FLAG_DATA_UPDATE_STRING() \
case FV_STRING: \
return error_s::make(ERR_NO_PERMISSION, "string modifications are not supported")
levy5307 marked this conversation as resolved.
Show resolved Hide resolved

void load()
{
switch (_type) {
Expand All @@ -55,9 +71,30 @@ class flag_data
{
}

error_s update(const std::string &val)
{
if (!has_tag(flag_tag::FT_MUTABLE)) {
return error_s::make(ERR_NO_PERMISSION, fmt::format("{} is not mutable", _name));
}

switch (_type) {
FLAG_DATA_UPDATE_CASE(int32_t, FV_INT32, int32);
FLAG_DATA_UPDATE_CASE(int64_t, FV_INT64, int64);
FLAG_DATA_UPDATE_CASE(uint32_t, FV_UINT32, uint32);
FLAG_DATA_UPDATE_CASE(uint64_t, FV_UINT64, uint64);
FLAG_DATA_UPDATE_CASE(bool, FV_BOOL, bool);
FLAG_DATA_UPDATE_CASE(double, FV_DOUBLE, double);
FLAG_DATA_UPDATE_STRING();
}
return error_s::make(ERR_OK);
}

void set_validator(validator_fn &validator) { _validator = std::move(validator); }
const validator_fn &validator() const { return _validator; }

void add_tag(const flag_tag &tag) { _tags.insert(tag); }
bool has_tag(const flag_tag &tag) const { return _tags.find(tag) != _tags.end(); }

private:
template <typename T>
T &value()
Expand All @@ -72,13 +109,23 @@ class flag_data
const char *_name;
const char *_desc;
validator_fn _validator;
std::unordered_set<flag_tag> _tags;
};

class flag_registry : public utils::singleton<flag_registry>
{
public:
void add_flag(const char *name, flag_data flag) { _flags.emplace(name, flag); }

error_s update_flag(const std::string &name, const std::string &val)
{
auto it = _flags.find(name);
if (it == _flags.end()) {
return error_s::make(ERR_OBJECT_NOT_FOUND, fmt::format("{} is not found", name));
}
return it->second.update(val);
}

void add_validator(const char *name, validator_fn &validator)
{
auto it = _flags.find(name);
Expand All @@ -97,6 +144,22 @@ class flag_registry : public utils::singleton<flag_registry>
}
}

void add_tag(const char *name, const flag_tag &tag)
{
auto it = _flags.find(name);
dassert(it != _flags.end(), "flag \"%s\" does not exist", name);
it->second.add_tag(tag);
}

bool has_tag(const std::string &name, const flag_tag &tag) const
{
auto it = _flags.find(name);
if (it == _flags.end()) {
return false;
}
return it->second.has_tag(tag);
}

private:
friend class utils::singleton<flag_registry>;
flag_registry() = default;
Expand Down Expand Up @@ -125,6 +188,21 @@ flag_validator::flag_validator(const char *name, validator_fn validator)
flag_registry::instance().add_validator(name, validator);
}

flag_tagger::flag_tagger(const char *name, const flag_tag &tag)
{
flag_registry::instance().add_tag(name, tag);
}

/*extern*/ void flags_initialize() { flag_registry::instance().load_from_config(); }

/*extern*/ error_s update_flag(const std::string &name, const std::string &val)
{
return flag_registry::instance().update_flag(name, val);
}

/*extern*/ bool has_tag(const std::string &name, const flag_tag &tag)
{
return flag_registry::instance().has_tag(name, tag);
}

} // namespace dsn
105 changes: 105 additions & 0 deletions src/utils/test/flag_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

#include <gtest/gtest.h>
#include <dsn/utility/flags.h>

namespace dsn {
namespace utils {

DSN_DEFINE_int32("flag_test", test_int32, 5, "");
DSN_TAG_VARIABLE(test_int32, FT_MUTABLE);

DSN_DEFINE_uint32("flag_test", test_uint32, 5, "");
DSN_TAG_VARIABLE(test_uint32, FT_MUTABLE);

DSN_DEFINE_int64("flag_test", test_int64, 5, "");
DSN_TAG_VARIABLE(test_int64, FT_MUTABLE);

DSN_DEFINE_uint64("flag_test", test_uint64, 5, "");
DSN_TAG_VARIABLE(test_uint64, FT_MUTABLE);

DSN_DEFINE_double("flag_test", test_double, 5.0, "");
DSN_TAG_VARIABLE(test_double, FT_MUTABLE);

DSN_DEFINE_bool("flag_test", test_bool, true, "");
DSN_TAG_VARIABLE(test_bool, FT_MUTABLE);

DSN_DEFINE_string("flag_test", test_string_immutable, "immutable_string", "");

TEST(flag_test, update_config)
{
auto res = update_flag("test_int32", "3");
ASSERT_TRUE(res.is_ok());
ASSERT_EQ(FLAGS_test_int32, 3);

res = update_flag("test_uint32", "3");
ASSERT_TRUE(res.is_ok());
ASSERT_EQ(FLAGS_test_uint32, 3);

res = update_flag("test_int64", "3");
ASSERT_TRUE(res.is_ok());
ASSERT_EQ(FLAGS_test_int64, 3);

res = update_flag("test_uint64", "3");
ASSERT_TRUE(res.is_ok());
ASSERT_EQ(FLAGS_test_uint64, 3);

res = update_flag("test_double", "3.0");
ASSERT_TRUE(res.is_ok());
ASSERT_EQ(FLAGS_test_double, 3.0);

res = update_flag("test_bool", "false");
ASSERT_TRUE(res.is_ok());
ASSERT_FALSE(FLAGS_test_bool);

// string modifications are not supported
res = update_flag("test_string_immutable", "update_string");
ASSERT_EQ(res.code(), ERR_NO_PERMISSION);
ASSERT_EQ(strcmp(FLAGS_test_string_immutable, "immutable_string"), 0);

// test flag is not exist
res = update_flag("test_not_exist", "test_string");
ASSERT_EQ(res.code(), ERR_OBJECT_NOT_FOUND);

// test to update invalid value
res = update_flag("test_int32", "3ab");
ASSERT_EQ(res.code(), ERR_INVALID_PARAMETERS);
ASSERT_EQ(FLAGS_test_int32, 3);
}

DSN_DEFINE_int32("flag_test", has_tag, 5, "");
DSN_TAG_VARIABLE(has_tag, FT_MUTABLE);

DSN_DEFINE_int32("flag_test", no_tag, 5, "");

TEST(flag_test, tag_flag)
{
// has tag
auto res = has_tag("has_tag", flag_tag::FT_MUTABLE);
ASSERT_TRUE(res);

// doesn't has tag
res = has_tag("no_tag", flag_tag::FT_MUTABLE);
ASSERT_FALSE(res);

// flag is not exist
res = has_tag("no_flag", flag_tag::FT_MUTABLE);
ASSERT_FALSE(res);
}
} // namespace utils
} // namespace dsn
Loading