From cf7eb02f3ab9a75e9620432fb59829aa8f4f5204 Mon Sep 17 00:00:00 2001 From: Elad Raz Date: Sun, 6 Mar 2016 15:55:03 +0200 Subject: [PATCH] common: Adding Table class 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 : 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 --- common/Makefile.am | 3 +- common/table.cpp | 113 +++++++++++++++++++++++++++++++++++++++++++++ common/table.h | 54 ++++++++++++++++++++++ 3 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 common/table.cpp create mode 100644 common/table.h diff --git a/common/Makefile.am b/common/Makefile.am index 1c0020bb2f..5566022afd 100644 --- a/common/Makefile.am +++ b/common/Makefile.am @@ -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) diff --git a/common/table.cpp b/common/table.cpp new file mode 100644 index 0000000000..08f48121a1 --- /dev/null +++ b/common/table.cpp @@ -0,0 +1,113 @@ +#include +#include + +#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); +} + +} diff --git a/common/table.h b/common/table.h new file mode 100644 index 0000000000..01b7d40fa9 --- /dev/null +++ b/common/table.h @@ -0,0 +1,54 @@ +#ifndef __TABLE__ +#define __TABLE__ + +#include +#include +#include +#include "hiredis/hiredis.h" +#include "common/dbconnector.h" +#include "common/redisreply.h" +#include "common/scheme.h" + +namespace swss { + +typedef std::tuple FieldValueTuple; +#define fvField std::get<0> +#define fvValue std::get<1> +typedef std::tuple > 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 m_expectedResults; + std::queue m_results; +}; + +} + +#endif