common/table.h (208 lines of code) (raw):

#ifndef __TABLE__ #define __TABLE__ #include <assert.h> #include <string> #include <queue> #include <tuple> #include <utility> #include <map> #include <deque> #include "hiredis/hiredis.h" #include "dbconnector.h" #include "redisreply.h" #include "redisselect.h" #include "redispipeline.h" #include "schema.h" #include "redistran.h" namespace swss { // Mapping of DB ID to table name separator string typedef std::map<int, std::string> TableNameSeparatorMap; typedef std::pair<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> typedef std::map<std::string,std::string> TableMap; typedef std::map<std::string,TableMap> TableDump; class TableBase { public: #ifndef SWIG __attribute__((deprecated)) #endif TableBase(int dbId, const std::string &tableName) : m_tableName(tableName) { m_tableSeparator = getTableSeparator(dbId); } TableBase(const std::string &tableName, const std::string &tableSeparator) : m_tableName(tableName), m_tableSeparator(tableSeparator) { static const std::string legalSeparators = ":|"; if (legalSeparators.find(tableSeparator) == std::string::npos) throw std::invalid_argument("Invalid table name separator"); } static std::string getTableSeparator(int dbId) { /* Look up table separator for the provided DB */ auto it = tableNameSeparatorMap.find(dbId); if (it != tableNameSeparatorMap.end()) { return it->second; } else { SWSS_LOG_NOTICE("Unrecognized database ID. Using default table name separator ('%s')", TABLE_NAME_SEPARATOR_VBAR.c_str()); return TABLE_NAME_SEPARATOR_VBAR; } } std::string getTableName() const { return m_tableName; } /* Return the actual key name as a combination of tableName<table_separator>key */ std::string getKeyName(const std::string &key) { if (key == "") return m_tableName; else return m_tableName + m_tableSeparator + key; } /* Return the table name separator being used */ std::string getTableNameSeparator() const { return m_tableSeparator; } std::string getChannelName() { return m_tableName + "_CHANNEL"; } /* Return tagged channel name */ std::string getChannelName(const std::string &tag) { return m_tableName + "_CHANNEL" + "@" + tag; } /* Return tagged channel name, most likely tag number could be dbId */ std::string getChannelName(int tag) { return getChannelName(std::to_string(tag)); } private: static const std::string TABLE_NAME_SEPARATOR_COLON; static const std::string TABLE_NAME_SEPARATOR_VBAR; static const TableNameSeparatorMap tableNameSeparatorMap; std::string m_tableName; std::string m_tableSeparator; }; class TableEntryWritable { public: virtual ~TableEntryWritable() = default; /* Set an entry in the table */ virtual void set(const std::string &key, const std::vector<FieldValueTuple> &values, const std::string &op = "", const std::string &prefix = EMPTY_PREFIX) = 0; /* Delete an entry in the table */ virtual void del(const std::string &key, const std::string &op = "", const std::string &prefix = EMPTY_PREFIX) = 0; }; class TableEntryPoppable { public: virtual ~TableEntryPoppable() = default; /* Pop an action (set or del) on the table */ virtual void pop(KeyOpFieldsValuesTuple &kco, const std::string &prefix = EMPTY_PREFIX) = 0; /* Get multiple pop elements */ virtual void pops(std::deque<KeyOpFieldsValuesTuple> &vkco, const std::string &prefix = EMPTY_PREFIX) = 0; /* Get multiple pop elements (only for SWIG usage) */ /* TODO: current swig 3.0 does not support std::tuple, remove after future support */ void pops(std::vector<std::string> &keys, std::vector<std::string> &ops, std::vector<std::vector<FieldValueTuple>> &fvss, const std::string &prefix = EMPTY_PREFIX) { std::deque<KeyOpFieldsValuesTuple> vkco; pops(vkco); keys.clear(); ops.clear(); fvss.clear(); while(!vkco.empty()) { auto& kco = vkco.front(); keys.emplace_back(kfvKey(kco)); ops.emplace_back(kfvOp(kco)); fvss.emplace_back(kfvFieldsValues(kco)); vkco.pop_front(); } } }; #if defined(SWIG) && defined(SWIGPYTHON) %pythoncode %{ def transpose_pops(m): return [tuple(m[j][i] for j in range(len(m))) for i in range(len(m[0]))] %} #endif class TableConsumable : public TableBase, public TableEntryPoppable, public RedisSelect { public: /* The default value of pop batch size is 128 */ static constexpr int DEFAULT_POP_BATCH_SIZE = 128; TableConsumable(const std::string &tableName, const std::string &separator, int pri) : TableBase(tableName, separator), RedisSelect(pri) { } }; class TableEntryEnumerable { public: virtual ~TableEntryEnumerable() = default; /* Get all the field-value tuple of the table entry with the key */ virtual bool get(const std::string &key, std::vector<FieldValueTuple> &values) = 0; virtual bool hget(const std::string &key, const std::string &field, std::string &value) = 0; /* get all the keys in the table */ virtual void getKeys(std::vector<std::string> &keys) = 0; /* Read the whole table content from the DB directly */ /* NOTE: Not an atomic function */ void getContent(std::vector<KeyOpFieldsValuesTuple> &tuples); }; /* The default time to live for a DB entry is infinite */ static constexpr int64_t DEFAULT_DB_TTL = -1; class Table : public TableBase, public TableEntryEnumerable { public: Table(const DBConnector *db, const std::string &tableName); Table(RedisPipeline *pipeline, const std::string &tableName, bool buffered); ~Table() override; /* Set an entry in the DB directly (op not in use) */ virtual void set(const std::string &key, const std::vector<FieldValueTuple> &values, const std::string &op = "", const std::string &prefix = EMPTY_PREFIX); /* Set an entry in the DB directly and configure ttl for it (op not in use) */ virtual void set(const std::string &key, const std::vector<FieldValueTuple> &values, const std::string &op, const std::string &prefix, const int64_t &ttl); /* Delete an entry in the table */ virtual void del(const std::string &key, const std::string &op = "", const std::string &prefix = EMPTY_PREFIX); /* Get the configured ttl value for key */ bool ttl(const std::string &key, int64_t &reply_value); #if defined(SWIG) && defined(SWIGPYTHON) // SWIG interface file (.i) globally rename map C++ `del` to python `delete`, // but applications already followed the old behavior of auto renamed `_del`. // So we implemented old behavior for backward compatibility // TODO: remove this function after applications use the function name `delete` %pythoncode %{ def _del(self, *args, **kwargs): return self.delete(*args, **kwargs) %} #endif virtual void hdel(const std::string &key, const std::string &field, const std::string &op = "", const std::string &prefix = EMPTY_PREFIX); /* Read a value from the DB directly */ /* Returns false if the key doesn't exists */ virtual bool get(const std::string &key, std::vector<FieldValueTuple> &ovalues); virtual bool hget(const std::string &key, const std::string &field, std::string &value); virtual void hset(const std::string &key, const std::string &field, const std::string &value, const std::string &op = "", const std::string &prefix = EMPTY_PREFIX); void getKeys(std::vector<std::string> &keys); void setBuffered(bool buffered); void flush(); void dump(TableDump &tableDump); protected: bool m_buffered; bool m_pipeowned; RedisPipeline *m_pipe; /* Strip special symbols from keys used for type identification * Input example: * port@ * DB entry: * 1) "ports@" * 2) "Ethernet0,Ethernet4,... * */ std::string stripSpecialSym(const std::string &key); std::string m_shaDump; }; class TableName_KeyValueOpQueues { private: std::string m_keyvalueop; public: TableName_KeyValueOpQueues(const std::string &tableName) : m_keyvalueop(tableName + "_KEY_VALUE_OP_QUEUE") { } std::string getKeyValueOpQueueTableName() const { return m_keyvalueop; } }; class TableName_KeySet { private: std::string m_key; std::string m_delkey; public: TableName_KeySet(const std::string &tableName) : m_key(tableName + "_KEY_SET") , m_delkey(tableName + "_DEL_SET") { } std::string getKeySetName() const { return m_key; } std::string getDelKeySetName() const { return m_delkey; } std::string getStateHashPrefix() const { return "_"; } }; } #endif