Skip to content

Commit

Permalink
common: Adding Table class
Browse files Browse the repository at this point in the history
The Table class wraps Redis transaction access. The Table class define
conventions of "Tables" on flat key-value Redis DB.
Every key belongs to a table. The key encoding is <TABLE_NAME>:<KEY_NAME>

Every change in Redis DB will be recorded in three lists: KEY, VALUE and
OP. The idea behind those lists is to allow consumer to get notification
even when elements are rapidly added and removed from the DB.

Signed-off-by: Elad Raz <[email protected]>
  • Loading branch information
eladraz authored and Shuotian Cheng committed Mar 7, 2016
1 parent ba40150 commit cf7eb02
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 1 deletion.
3 changes: 2 additions & 1 deletion common/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ endif
libswsscommon_la_SOURCES = \
logger.cpp \
redisreply.cpp \
dbconnector.cpp
dbconnector.cpp \
table.cpp

libswsscommon_la_CFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON)
libswsscommon_la_CPPFLAGS = $(DBGFLAGS) $(AM_CFLAGS) $(CFLAGS_COMMON)
Expand Down
113 changes: 113 additions & 0 deletions common/table.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
#include <hiredis/hiredis.h>
#include <system_error>

#include "common/table.h"
#include "common/logger.h"
#include "common/redisreply.h"

using namespace std;

namespace swss {

Table::Table(DBConnector *db, string tableName) :
m_db(db),
m_tableName(tableName)
{
}

string Table::getKeyName(string key)
{
return m_tableName + ':' + key;
}

string Table::getKeyQueueTableName()
{
return m_tableName + "_KEY_QUEUE";
}

string Table::getValueQueueTableName()
{
return m_tableName + "_VALUE_QUEUE";
}

string Table::getOpQueueTableName()
{
return m_tableName + "_OP_QUEUE";
}

string Table::getChannelTableName()
{
return m_tableName + "_CHANNEL";
}

void Table::multi()
{
while (!m_expectedResults.empty())
m_expectedResults.pop();
RedisReply r(m_db, "MULTI", REDIS_REPLY_STATUS);
r.checkStatusOK();
}

redisReply *Table::queueResultsFront()
{
return m_results.front()->getContext();
}

void Table::queueResultsPop()
{
delete m_results.front();
m_results.pop();
}

void Table::exec()
{
redisReply *reply = (redisReply *)redisCommand(m_db->getContext(), "EXEC");
unsigned int size = reply->elements;

try
{
if (reply->type != REDIS_REPLY_ARRAY)
throw system_error(make_error_code(errc::io_error),
"Error in transaction");

if (size != m_expectedResults.size())
throw system_error(make_error_code(errc::io_error),
"Got to different nuber of answers!");

while (!m_results.empty())
queueResultsPop();

for (unsigned int i = 0; i < size; i++)
{
int expectedType = m_expectedResults.front();
m_expectedResults.pop();
if (expectedType != reply->element[i]->type)
{
SWSS_LOG_INFO("Except to get redis type %d got type %d\n",
expectedType, reply->element[i]->type);
throw system_error(make_error_code(errc::io_error),
"Got unexpected result");
}
}
}
catch (...) {
freeReplyObject(reply);
throw;
}

for (unsigned int i = 0; i < size; i++)
m_results.push(new RedisReply(reply->element[i]));

/* Free only the array memory */
free(reply->element);
free(reply);
}

void Table::enqueue(std::string command, int exepectedResult, bool isFormatted)
{
RedisReply r(m_db, command, REDIS_REPLY_STATUS, isFormatted);
r.checkStatusQueued();
m_expectedResults.push(exepectedResult);
}

}
54 changes: 54 additions & 0 deletions common/table.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#ifndef __TABLE__
#define __TABLE__

#include <string>
#include <queue>
#include <tuple>
#include "hiredis/hiredis.h"
#include "common/dbconnector.h"
#include "common/redisreply.h"
#include "common/scheme.h"

namespace swss {

typedef std::tuple<std::string, std::string> FieldValueTuple;
#define fvField std::get<0>
#define fvValue std::get<1>
typedef std::tuple<std::string, std::string, std::vector<FieldValueTuple> > KeyOpFieldsValuesTuple;
#define kfvKey std::get<0>
#define kfvOp std::get<1>
#define kfvFieldsValues std::get<2>

class Table {
protected:
Table(DBConnector *db, std::string tableName);

/* Return the actual key name as a comibation of tableName:key */
std::string getKeyName(std::string key);

std::string getKeyQueueTableName();
std::string getValueQueueTableName();
std::string getOpQueueTableName();
std::string getChannelTableName();

/* Start a transaction */
void multi();
/* Execute a transaction and get results */
void exec();

/* Send a command within a transaction */
void enqueue(std::string command, int exepectedResult, bool isFormatted = false);
redisReply* queueResultsFront();
void queueResultsPop();

DBConnector *m_db;
std::string m_tableName;

/* Remember the expected results for the transaction */
std::queue<int> m_expectedResults;
std::queue<RedisReply * > m_results;
};

}

#endif

0 comments on commit cf7eb02

Please sign in to comment.