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
hubble/peer: handle burst of change notifications #12024
Conversation
test-me-please |
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.
Looks good! One optional nit.
There was also an offline discussion about maybe using a CondVar
in the implementation instead of the notify channel trick, but I want to run some benchmarks first before changing things prematurely (especially since this is not on a any critical path).
Signed-off-by: Robin Hahling <robin.hahling@gw-computing.net>
When a client calls Notify, a change notification is sent for every node in the cluster which allows the client to discover all nodes in the cluster. Subsequently, only changes are notified (when a node is updated or deleted or a new node is added to the cluster). With a sufficiently large cluster, the initial burst of notifications can be massive and the previous implementation did not accommodate for this as it used a default buffer of size 10, assuming it would be large enough to serialize notifications and send them to the client before new notifications are being pushed by the node manager. It appears that the node manager iterates over a hash map while holding a lock when it calls the notification handler and this process is much faster than serializing and sending these notifications over to the remote client via gRPC. However, as the initial buffer implementation used a channel, increasing the buffer size to a very large value would dramatically increase memory usage and most of it would be unused in most cases (in Go, a buffered channel allocates enough memory to hold every element it has be able to buffer). This commit fixes this problem by using a concurrent safe buffer implementation backed by a slice that is only allowed to grow to a predefined maximum size. This size defaults to 65_536 entries, which should be enough to accommodate bursts of notifications for large clusters in cluster-mesh. Signed-off-by: Robin Hahling <robin.hahling@gw-computing.net>
f1abeb0
to
e27ee36
Compare
test-me-please |
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.
lgtm a couple of questions.
- will there be a corresponding change in command line option / helm template?
- i'm always curious to see the actual difference in resource usage whenever we introduce optimization.
This option has never been made configurable.
Good question! The memory allocation for a channel of this type and size would have been in the order of ~1M (verified using |
K8s-1.17-Kernel-4.19 failure is a known flake (IPv4 fragments) |
When a client calls Notify, a change notification is sent for every node
in the cluster which allows the client to discover all nodes in the
cluster. Subsequently, only changes are notified (when a node is updated
or deleted or a new node is added to the cluster).
With a sufficiently large cluster, the initial burst of notifications
can be massive and the previous implementation did not accommodate for
this as it used a default buffer of size 10, assuming it would be large
enough to serialize notifications and send them to the client before
new notifications are being pushed by the node manager. It appears that
the node manager iterates over a hash map while holding a lock when it
calls the notification handler and this process is much faster than
serializing and sending these notifications over to the remote client
via gRPC.
However, as the initial buffer implementation used a channel, increasing
the buffer size to a very large value would dramatically increase memory
usage and most of it would be unused in most cases (in Go, a buffered
channel allocates enough memory to hold every element it has be able to
buffer).
This PR fixes this problem by using a concurrent safe buffer
implementation backed by a slice that is only allowed to grow to a
predefined maximum size. This size defaults to 65_536 entries, which
should be enough to accommodate bursts of notifications for large
clusters in cluster-mesh.