-
Notifications
You must be signed in to change notification settings - Fork 291
[PINS] Add ConsumerNotifier for PINS response path #547
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
Changes from all commits
7bca368
9eb5bc1
cdfe5b7
eba0bb3
2e343cb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
#include "consumernotifier.h" | ||
|
||
#include "select.h" | ||
#include "selectable.h" | ||
|
||
namespace swss | ||
{ | ||
|
||
ConsumerNotifier::ConsumerNotifier(const std::string ¬ifier_channel_name, DBConnector *db_connector) | ||
{ | ||
notifier_channel_name_ = std::string{notifier_channel_name}; | ||
notification_consumer_ = std::make_unique<NotificationConsumer>(db_connector, notifier_channel_name_); | ||
} | ||
|
||
bool ConsumerNotifier::WaitForNotificationAndPop(std::string &op, std::string &data, | ||
std::vector<FieldValueTuple> &values, int64_t timeout_ms) | ||
{ | ||
int message_available = notification_consumer_->peek(); | ||
if (message_available == -1) | ||
{ | ||
return false; | ||
} | ||
|
||
// Wait for notification only when the queue is empty. | ||
if (message_available == 0) | ||
{ | ||
Select s; | ||
s.addSelectable(notification_consumer_.get()); | ||
Selectable *sel; | ||
|
||
int error = s.select(&sel, timeout_ms); | ||
if (error != Select::OBJECT) | ||
{ | ||
return false; | ||
} | ||
} | ||
notification_consumer_->pop(op, data, values); | ||
return true; | ||
} | ||
|
||
} // namespace swss |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
#pragma once | ||
|
||
#include <memory> | ||
#include <vector> | ||
|
||
#include "consumernotifierinterface.h" | ||
#include "dbconnector.h" | ||
#include "notificationconsumer.h" | ||
|
||
namespace swss | ||
{ | ||
|
||
// Wrapper class to use swss::NotificationConsumer. | ||
class ConsumerNotifier : public ConsumerNotifierInterface | ||
{ | ||
public: | ||
ConsumerNotifier(const std::string ¬ifier_channel_name, DBConnector *db_connector); | ||
|
||
bool WaitForNotificationAndPop(std::string &op, std::string &data, std::vector<FieldValueTuple> &values, | ||
int64_t timeout_ms = 60000LL) override; | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Robert's proposal
Alternative:
|
||
private: | ||
std::unique_ptr<NotificationConsumer> notification_consumer_; | ||
std::string notifier_channel_name_; | ||
}; | ||
|
||
} // namespace swss |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
#pragma once | ||
|
||
#include <vector> | ||
|
||
#include "table.h" | ||
|
||
namespace swss | ||
{ | ||
|
||
// Wrapper class to use swss::NotificationConsumer. | ||
class ConsumerNotifierInterface | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can, but the other issue these interface classes address is the base class constructors requiring a redis db to be active. If we remove this do you have any objections to having a protected constructor that doesn't initialize a redis connection? |
||
{ | ||
public: | ||
virtual ~ConsumerNotifierInterface() = default; | ||
|
||
// Waits for notifications to be posted on the response channel, | ||
// this goes into a blocking wait (default timeout of 60 seconds) until a | ||
// response is received and then uses swss::pop to get the response values. | ||
virtual bool WaitForNotificationAndPop(std::string &op, std::string &data, std::vector<FieldValueTuple> &values, | ||
int64_t timeout_ms = 60000LL) = 0; | ||
}; | ||
|
||
} // namespace swss |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,6 @@ | ||
#include <string.h> | ||
#include <stdint.h> | ||
#include <iterator> | ||
#include <vector> | ||
#include <unistd.h> | ||
#include <errno.h> | ||
|
@@ -750,6 +751,19 @@ int64_t DBConnector::decr(const string &key) | |
return r.getContext()->integer; | ||
} | ||
|
||
#ifndef SWIG | ||
std::unordered_map<std::string, std::string> DBConnector::hgetall( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @rhalstea what do you think? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The templates didn't work with the virtual interfaces. Where this is needed in the P4RT App could be updated to the Table class. We can remove this once that happens, but that change may be difficult with the MVP timelines. However, internally we do want it planned for the next push. |
||
const std::string &key){ | ||
return hgetall<std::unordered_map<std::string, std::string>>(key); | ||
} | ||
#endif | ||
|
||
void DBConnector::hmset( | ||
const std::string &key, | ||
const std::vector<std::pair<std::string, std::string>> &values) { | ||
hmset(key, values.begin(), values.end()); | ||
} | ||
|
||
shared_ptr<string> DBConnector::get(const string &key) | ||
{ | ||
RedisCommand sget; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,6 +9,7 @@ | |
#include <mutex> | ||
|
||
#include <hiredis/hiredis.h> | ||
#include "dbconnectorinterface.h" | ||
#include "rediscommand.h" | ||
#include "redisreply.h" | ||
#define EMPTY_NAMESPACE std::string() | ||
|
@@ -137,7 +138,7 @@ class RedisContext | |
redisContext *m_conn; | ||
}; | ||
|
||
class DBConnector : public RedisContext | ||
class DBConnector : public RedisContext, public DBConnectorInterface | ||
{ | ||
public: | ||
static constexpr const char *DEFAULT_UNIXSOCKET = "/var/run/redis/redis.sock"; | ||
|
@@ -174,7 +175,7 @@ class DBConnector : public RedisContext | |
|
||
PubSub *pubsub(); | ||
|
||
int64_t del(const std::string &key); | ||
int64_t del(const std::string &key) override; | ||
|
||
#ifdef SWIG | ||
// SWIG interface file (.i) globally rename map C++ `del` to python `delete`, | ||
|
@@ -187,7 +188,7 @@ class DBConnector : public RedisContext | |
%} | ||
#endif | ||
|
||
bool exists(const std::string &key); | ||
bool exists(const std::string &key) override; | ||
|
||
int64_t hdel(const std::string &key, const std::string &field); | ||
|
||
|
@@ -199,11 +200,14 @@ class DBConnector : public RedisContext | |
ReturnType hgetall(const std::string &key); | ||
|
||
#ifndef SWIG | ||
std::unordered_map<std::string, std::string> hgetall( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Qi's suggestion: make this a standalone function in sonic-pins; first parameter is DBConnector; implementation calls the template version. |
||
const std::string &key) override; | ||
|
||
template <typename OutputIterator> | ||
void hgetall(const std::string &key, OutputIterator result); | ||
#endif | ||
|
||
std::vector<std::string> keys(const std::string &key); | ||
std::vector<std::string> keys(const std::string &key) override; | ||
|
||
std::pair<int, std::vector<std::string>> scan(int cursor = 0, const char *match = "", uint32_t count = 10); | ||
|
||
|
@@ -217,9 +221,14 @@ class DBConnector : public RedisContext | |
|
||
void hmset(const std::unordered_map<std::string, std::vector<std::pair<std::string, std::string>>>& multiHash); | ||
|
||
void hmset(const std::string &key, | ||
const std::vector<std::pair<std::string, std::string>> &values) | ||
override; | ||
|
||
std::shared_ptr<std::string> get(const std::string &key); | ||
|
||
std::shared_ptr<std::string> hget(const std::string &key, const std::string &field); | ||
std::shared_ptr<std::string> hget(const std::string &key, | ||
const std::string &field) override; | ||
|
||
bool hexists(const std::string &key, const std::string &field); | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
#pragma once | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Qi concerned about performance penalty for virtual functions. Can this be done without this interface and virtual functions? If this is needed for new feature, then it's okay. But we shouldn't incur a performance penalty just for testing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Update: See comment on main PR conversation regarding virtual functions: #547 (comment) |
||
|
||
#include <map> | ||
#include <memory> | ||
#include <vector> | ||
|
||
namespace swss | ||
{ | ||
|
||
class DBConnectorInterface | ||
{ | ||
public: | ||
virtual ~DBConnectorInterface() = default; | ||
|
||
virtual int64_t del(const std::string &key) = 0; | ||
|
||
virtual void del(const std::vector<std::string> &keys) = 0; | ||
|
||
virtual bool exists(const std::string &key) = 0; | ||
|
||
virtual std::unordered_map<std::string, std::string> hgetall(const std::string &glob) = 0; | ||
|
||
virtual std::vector<std::string> keys(const std::string &glob) = 0; | ||
|
||
virtual void hmset(const std::string &key, const std::vector<std::pair<std::string, std::string>> &values) = 0; | ||
|
||
virtual std::shared_ptr<std::string> hget(const std::string &key, const std::string &field) = 0; | ||
}; | ||
|
||
} // namespace swss |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -358,4 +358,9 @@ void ProducerStateTable::apply_temp_view() | |
m_tempViewActive = false; | ||
} | ||
|
||
std::string ProducerStateTable::get_table_name() const | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This function does not add value, can you remove? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We use this in the P4RT server application. @rhalstea what do you think? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's useful at various points in P4RT App to know the table name. Particularly around logging. The function was added here to make it available as part of the interface. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's already defined in TableBase::getTableName(), and TableBase could be treated as an interface. |
||
{ | ||
return getTableName(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
#pragma once | ||
|
||
#include <memory> | ||
#include <vector> | ||
|
||
#include "table.h" | ||
|
||
namespace swss | ||
{ | ||
|
||
class ProducerStateTableInterface | ||
{ | ||
public: | ||
virtual ~ProducerStateTableInterface() = default; | ||
|
||
virtual void set(const std::string &key, const std::vector<FieldValueTuple> &values, | ||
const std::string &op = SET_COMMAND, const std::string &prefix = EMPTY_PREFIX) = 0; | ||
|
||
virtual void del(const std::string &key, const std::string &op = DEL_COMMAND, | ||
const std::string &prefix = EMPTY_PREFIX) = 0; | ||
|
||
virtual std::string get_table_name() const = 0; | ||
}; | ||
|
||
} // namespace swss |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
#include "swss/fakes/fake_consumer_notifier.h" | ||
|
||
#include "glog/logging.h" | ||
|
||
namespace swss | ||
{ | ||
|
||
FakeConsumerNotifier::FakeConsumerNotifier(FakeSonicDbTable *app_db_table) : app_db_table_(app_db_table) | ||
{ | ||
LOG_IF(FATAL, app_db_table == nullptr) << "FakeSonicDbTable cannot be nullptr."; | ||
} | ||
|
||
bool FakeConsumerNotifier::WaitForNotificationAndPop(std::string &op, std::string &data, SonicDbEntry &values, | ||
int64_t timeout_ms) | ||
{ | ||
app_db_table_->GetNextNotification(op, data, values); | ||
return true; | ||
} | ||
|
||
} // namespace swss |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
#pragma once | ||
|
||
#include <vector> | ||
|
||
#include "absl/status/status.h" | ||
#include "swss/consumernotifierinterface.h" | ||
#include "swss/fakes/fake_sonic_db_table.h" | ||
|
||
namespace swss | ||
{ | ||
|
||
// Fakes the OrchAgent response path behavior for AppDb table entries. | ||
// | ||
// Every write into an AppDb table is handled by the OrchAgent. The write can | ||
// either succeed or fail. In the latter case a failed StatusCode should be | ||
// returned by the fake. | ||
class FakeConsumerNotifier final : public ConsumerNotifierInterface | ||
{ | ||
public: | ||
explicit FakeConsumerNotifier(FakeSonicDbTable *app_db_table); | ||
|
||
// Not copyable or moveable. | ||
FakeConsumerNotifier(const FakeConsumerNotifier &) = delete; | ||
FakeConsumerNotifier &operator=(const FakeConsumerNotifier &) = delete; | ||
|
||
// Faked methods. | ||
bool WaitForNotificationAndPop(std::string &op, std::string &data, SonicDbEntry &values, | ||
int64_t timeout_ms = 60000LL) override; | ||
|
||
private: | ||
// The AppDb table maintains a list of notifications that this fake can | ||
// request. | ||
FakeSonicDbTable *app_db_table_; // No ownership. | ||
}; | ||
|
||
} // namespace swss |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic is applicable to any Selectable or Select, which contains multiple Selectable. Could you move it to their class?