Skip to content
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 DBInterface/SonicV2Connector in C++ #387

Merged
merged 37 commits into from
Oct 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
eddb06e
[pyext] Add more OUTPUT type
qiluo-msft Aug 29, 2020
639599d
Refactor: add new class RedisConnector
qiluo-msft Aug 29, 2020
27dc476
dbconnector: remove emtpy line
qiluo-msft Aug 29, 2020
9341111
Refine script functions parameter
qiluo-msft Aug 29, 2020
ee3a609
Add copy constructor to RedisConnector
qiluo-msft Sep 2, 2020
c358b24
Optimize DBConnector ctor
qiluo-msft Sep 2, 2020
eab9a9b
Revert back m_namespace
qiluo-msft Sep 2, 2020
89bd455
Refactor: change name
qiluo-msft Sep 2, 2020
f11bca4
Add new ctor for DBConnector from RedisContext and dbId
qiluo-msft Sep 11, 2020
5f4e3da
(ongoing) Add DBInterface class
qiluo-msft Sep 11, 2020
f731fcf
(temp, not build)
qiluo-msft Sep 12, 2020
c7b7023
Fix build
qiluo-msft Sep 22, 2020
9b4d414
Extract psubscribe and subscribe function into DBConnector class
qiluo-msft Sep 23, 2020
d901fe5
Implement _subscribe_keyspace_notification, _unsubscribe_keyspace_not…
qiluo-msft Sep 24, 2020
03b99c5
Implement blockable
qiluo-msft Sep 24, 2020
1aa56b4
Implement connect with retry
qiluo-msft Sep 24, 2020
a8457a9
Implement DBConnector::publish(),
qiluo-msft Sep 25, 2020
87bcf7f
Use c++11 syntax instead of c++14
qiluo-msft Sep 26, 2020
b0fbe03
Implement blocking for get and del
qiluo-msft Sep 26, 2020
bf779bd
Add to pyext
qiluo-msft Sep 26, 2020
c193a6a
Add set_redis_kwargs(), fix _onetime_connect()
qiluo-msft Sep 28, 2020
9f0eb3e
Fix LGTM: delete implicitly-declared copy assignment operator
qiluo-msft Sep 29, 2020
e126eb1
update DBInterface redis_client index from db_id to db_name
qiluo-msft Oct 1, 2020
c754f15
Add DBInterface::delete_all_by_pattern()
qiluo-msft Oct 1, 2020
d282fbc
Add SonicV2Connector class
qiluo-msft Oct 2, 2020
c530fea
Add unit test for SonicV2Connector
qiluo-msft Oct 3, 2020
fdc256f
Make const strings public because they are used as public method default
qiluo-msft Oct 5, 2020
d4e442c
SWIG supports keyword arguments in generated python module
qiluo-msft Oct 5, 2020
1d628ec
Add python namespace property to DBConnector class, solve the paramter
qiluo-msft Oct 5, 2020
8c93402
Move SonicV2Connector to standalone .h/.cpp files
qiluo-msft Oct 5, 2020
f84f07b
Add missing include statements into SWIG inteface file
qiluo-msft Oct 7, 2020
3a761e6
Add pytest unit test for DBInterface and SonicV2Connector
qiluo-msft Oct 7, 2020
04545d7
Fix swig customization on SonicV2Connector ctor
qiluo-msft Oct 7, 2020
2c8b889
Add attrib SonicV2Connector.namespace
qiluo-msft Oct 7, 2020
11658b9
Remove debug code
qiluo-msft Oct 8, 2020
832ace7
Use EXPECT_NE to simplify test
qiluo-msft Oct 8, 2020
b789a21
Remove unused code
qiluo-msft Oct 12, 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
2 changes: 2 additions & 0 deletions common/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ libswsscommon_la_SOURCES = \
logger.cpp \
redisreply.cpp \
dbconnector.cpp \
dbinterface.cpp \
sonicv2connector.cpp \
table.cpp \
json.cpp \
producertable.cpp \
Expand Down
237 changes: 162 additions & 75 deletions common/dbconnector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@

using json = nlohmann::json;
using namespace std;

namespace swss {
using namespace swss;

void SonicDBConfig::parseDatabaseConfig(const string &file,
std::unordered_map<std::string, RedisInstInfo> &inst_entry,
Expand Down Expand Up @@ -362,6 +361,22 @@ vector<string> SonicDBConfig::getNamespaces()
return list;
}

std::vector<std::string> SonicDBConfig::getDbList(const std::string &netns)
{
if (!m_init)
{
initialize();
}
validateNamespace(netns);

std::vector<std::string> dbNames;
for (auto& imap: m_db_info.at(netns))
{
dbNames.push_back(imap.first);
}
return dbNames;
}

constexpr const char *SonicDBConfig::DEFAULT_SONIC_DB_CONFIG_FILE;
constexpr const char *SonicDBConfig::DEFAULT_SONIC_DB_GLOBAL_CONFIG_FILE;
unordered_map<string, unordered_map<string, RedisInstInfo>> SonicDBConfig::m_inst_info;
Expand All @@ -370,7 +385,100 @@ unordered_map<string, unordered_map<int, string>> SonicDBConfig::m_db_separator;
bool SonicDBConfig::m_init = false;
bool SonicDBConfig::m_global_init = false;

constexpr const char *DBConnector::DEFAULT_UNIXSOCKET;
constexpr const char *RedisContext::DEFAULT_UNIXSOCKET;

RedisContext::~RedisContext()
{
redisFree(m_conn);
}

RedisContext::RedisContext()
{
}

RedisContext::RedisContext(const RedisContext &other)
{
auto octx = other.getContext();
const char *unixPath = octx->unix_sock.path;
if (unixPath)
{
initContext(unixPath, *octx->timeout);
}
else
{
initContext(octx->tcp.host, octx->tcp.port, *octx->timeout);
}
}

RedisContext::RedisContext(const string& hostname, int port,
unsigned int timeout)
{
struct timeval tv = {0, (suseconds_t)timeout * 1000};
initContext(hostname.c_str(), port, tv);
}

RedisContext::RedisContext(const string& unixPath, unsigned int timeout)
{
struct timeval tv = {0, (suseconds_t)timeout * 1000};
initContext(unixPath.c_str(), tv);
}

void RedisContext::initContext(const char *host, int port, const timeval& tv)
{
m_conn = redisConnectWithTimeout(host, port, tv);

if (m_conn->err)
throw system_error(make_error_code(errc::address_not_available),
"Unable to connect to redis");
}

void RedisContext::initContext(const char *path, const timeval &tv)
{
m_conn = redisConnectUnixWithTimeout(path, tv);

if (m_conn->err)
throw system_error(make_error_code(errc::address_not_available),
"Unable to connect to redis (unix-socket)");
}

redisContext *RedisContext::getContext() const
{
return m_conn;
}

void RedisContext::setContext(redisContext *ctx)
{
m_conn = ctx;
}

void RedisContext::setClientName(const string& clientName)
{
string command("CLIENT SETNAME ");
command += clientName;

RedisReply r(this, command, REDIS_REPLY_STATUS);
r.checkStatusOK();
}

string RedisContext::getClientName()
{
string command("CLIENT GETNAME");

RedisReply r(this, command);

auto ctx = r.getContext();
if (ctx->type == REDIS_REPLY_STRING)
{
return r.getReply<std::string>();
}
else
{
if (ctx->type != REDIS_REPLY_NIL)
SWSS_LOG_ERROR("Unable to obtain Redis client name");

return "";
}
}

void DBConnector::select(DBConnector *db)
{
Expand All @@ -381,45 +489,36 @@ void DBConnector::select(DBConnector *db)
r.checkStatusOK();
}

DBConnector::~DBConnector()
DBConnector::DBConnector(const DBConnector &other)
: RedisContext(other)
, m_dbId(other.m_dbId)
, m_namespace(other.m_namespace)
{
redisFree(m_conn);
select(this);
}

DBConnector::DBConnector(int dbId, const RedisContext& ctx)
: RedisContext(ctx)
, m_dbId(dbId)
, m_namespace(EMPTY_NAMESPACE)
{
select(this);
}

DBConnector::DBConnector(int dbId, const string& hostname, int port,
unsigned int timeout) :
RedisContext(hostname, port, timeout),
m_dbId(dbId),
m_namespace(EMPTY_NAMESPACE)
{
struct timeval tv = {0, (suseconds_t)timeout * 1000};

if (timeout)
m_conn = redisConnectWithTimeout(hostname.c_str(), port, tv);
else
m_conn = redisConnect(hostname.c_str(), port);

if (m_conn->err)
throw system_error(make_error_code(errc::address_not_available),
"Unable to connect to redis");

select(this);
}

DBConnector::DBConnector(int dbId, const string& unixPath, unsigned int timeout) :
RedisContext(unixPath, timeout),
m_dbId(dbId),
m_namespace(EMPTY_NAMESPACE)
{
struct timeval tv = {0, (suseconds_t)timeout * 1000};

if (timeout)
m_conn = redisConnectUnixWithTimeout(unixPath.c_str(), tv);
else
m_conn = redisConnectUnix(unixPath.c_str());

if (m_conn->err)
throw system_error(make_error_code(errc::address_not_available),
"Unable to connect to redis (unix-socket)");

select(this);
}

Expand All @@ -430,25 +529,15 @@ DBConnector::DBConnector(const string& dbName, unsigned int timeout, bool isTcpC
{
struct timeval tv = {0, (suseconds_t)timeout * 1000};

if (timeout)
if (isTcpConn)
{
if (isTcpConn)
m_conn = redisConnectWithTimeout(SonicDBConfig::getDbHostname(dbName, netns).c_str(), SonicDBConfig::getDbPort(dbName, netns), tv);
else
m_conn = redisConnectUnixWithTimeout(SonicDBConfig::getDbSock(dbName, netns).c_str(), tv);
initContext(SonicDBConfig::getDbHostname(dbName, netns).c_str(), SonicDBConfig::getDbPort(dbName, netns), tv);
}
else
{
if (isTcpConn)
m_conn = redisConnect(SonicDBConfig::getDbHostname(dbName, netns).c_str(), SonicDBConfig::getDbPort(dbName, netns));
else
m_conn = redisConnectUnix(SonicDBConfig::getDbSock(dbName, netns).c_str());
initContext(SonicDBConfig::getDbSock(dbName, netns).c_str(), tv);
}

if (m_conn->err)
throw system_error(make_error_code(errc::address_not_available),
"Unable to connect to redis");

select(this);
}

Expand All @@ -458,11 +547,6 @@ DBConnector::DBConnector(const string& dbName, unsigned int timeout, bool isTcpC
// Empty contructor
}

redisContext *DBConnector::getContext() const
{
return m_conn;
}

int DBConnector::getDbId() const
{
return m_dbId;
Expand All @@ -473,6 +557,11 @@ string DBConnector::getDbName() const
return m_dbName;
}

void DBConnector::setNamespace(const string& netns)
{
m_namespace = netns;
}

string DBConnector::getNamespace() const
{
return m_namespace;
Expand All @@ -493,40 +582,11 @@ DBConnector *DBConnector::newConnector(unsigned int timeout) const
timeout);

ret->m_dbName = m_dbName;
ret->m_namespace = m_namespace;
ret->setNamespace(getNamespace());

return ret;
}

void DBConnector::setClientName(const string& clientName)
{
string command("CLIENT SETNAME ");
command += clientName;

RedisReply r(this, command, REDIS_REPLY_STATUS);
r.checkStatusOK();
}

string DBConnector::getClientName()
{
string command("CLIENT GETNAME");

RedisReply r(this, command);

auto ctx = r.getContext();
if (ctx->type == REDIS_REPLY_STRING)
{
return r.getReply<std::string>();
}
else
{
if (ctx->type != REDIS_REPLY_NIL)
SWSS_LOG_ERROR("Unable to obtain Redis client name");

return "";
}
}

int64_t DBConnector::del(const string &key)
{
RedisCommand sdel;
Expand Down Expand Up @@ -578,6 +638,13 @@ void DBConnector::set(const string &key, const string &value)
RedisReply r(this, sset, REDIS_REPLY_STATUS);
}

void DBConnector::config_set(const std::string &key, const std::string &value)
{
RedisCommand sset;
sset.format("CONFIG SET %s %s", key.c_str(), value.c_str());
RedisReply r(this, sset, REDIS_REPLY_STATUS);
}

unordered_map<string, string> DBConnector::hgetall(const string &key)
{
unordered_map<string, string> map;
Expand Down Expand Up @@ -688,4 +755,24 @@ shared_ptr<string> DBConnector::blpop(const string &list, int timeout)
throw runtime_error("GET failed, memory exception");
}

void DBConnector::subscribe(const std::string &pattern)
{
std::string s("SUBSCRIBE ");
s += pattern;
RedisReply r(this, s, REDIS_REPLY_ARRAY);
}

void DBConnector::psubscribe(const std::string &pattern)
{
std::string s("PSUBSCRIBE ");
s += pattern;
RedisReply r(this, s, REDIS_REPLY_ARRAY);
}

int64_t DBConnector::publish(const string &channel, const string &message)
{
RedisCommand publish;
publish.format("PUBLISH %s %s", channel.c_str(), message.c_str());
RedisReply r(this, publish, REDIS_REPLY_INTEGER);
return r.getReply<long long int>();
}
Loading