Skip to content

Commit 23f79b6

Browse files
committed
[swss-common] Enhanced NotificationConsumer queue to avoid memory leaks in some cases
Why I did it In the current notification mechanism, the NotificationConsumer stores notification messages in bulk to an internal queue. However, when the various Orch consumers receive notifications, they only retrieve one notification from the queue for processing at a time. This approach has a potential risk of memory leaks. If the device is hit by a large number of FDB move or link up/down flapping events, a significant increase in memory consumption by the orchagent can be observed, eventually leading to a system crash or hang. (Testing has been done on SONiC 202111, where the orchagent's memory consumption reached up to 10GB and continued to increase) How I did it Modified the internal queue of the NotificationConsumer class to replace std::queue. Now, when processing existing duplicate messages, the NotificationConsumer will no longer enqueue them and will instead move the existing message to the end of the queue (as a penalty for DDOS). This significantly reduces the number of duplicate notify messages, and the time complexity for both lookup and insertion in the new queue is O(1).
1 parent dce55c3 commit 23f79b6

File tree

1 file changed

+51
-1
lines changed

1 file changed

+51
-1
lines changed

common/notificationconsumer.h

+51-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
#include <string>
55
#include <vector>
66
#include <queue>
7+
#include <unordered_map>
8+
#include <list>
79

810
#include <hiredis/hiredis.h>
911

@@ -18,6 +20,54 @@ namespace swss {
1820

1921
static constexpr size_t DEFAULT_NC_POP_BATCH_SIZE = 2048;
2022

23+
template <typename T>
24+
class Queue
25+
{
26+
private:
27+
std::list<T> dq;
28+
std::unordered_map<T, typename std::list<T>::iterator> map;
29+
30+
public:
31+
void push(const T& value)
32+
{
33+
auto it = map.find(value);
34+
if (it != map.end())
35+
dq.erase(it->second);
36+
dq.push_back(value);
37+
map[value] = --dq.end();
38+
}
39+
40+
void pop()
41+
{
42+
if (dq.empty())
43+
return;
44+
map.erase(dq.front());
45+
dq.pop_front();
46+
}
47+
48+
T front() const
49+
{
50+
if (dq.empty())
51+
throw std::runtime_error("Queue is empty");
52+
return dq.front();
53+
}
54+
55+
size_t size() const {
56+
return dq.size();
57+
}
58+
59+
void swap(Queue& other)
60+
{
61+
std::swap(dq, other.dq);
62+
std::swap(map, other.map);
63+
}
64+
65+
bool empty() const
66+
{
67+
return dq.empty();
68+
}
69+
};
70+
2171
class NotificationConsumer : public Selectable
2272
{
2373
public:
@@ -55,7 +105,7 @@ class NotificationConsumer : public Selectable
55105
swss::DBConnector *m_db;
56106
swss::DBConnector *m_subscribe;
57107
std::string m_channel;
58-
std::queue<std::string> m_queue;
108+
Queue<std::string> m_queue;
59109
};
60110

61111
}

0 commit comments

Comments
 (0)