To be Reviewed By: 7 April 2020
Authors: Dan Smith
Status: Draft Draft | Discussion | Active | Dropped | Superseded
Superseded by: N/A
Related: N/A
Problem
Geode uses UDP messaging through JGroups for peer-to-peer messaging related to membership. Geode does not actually use jgroups group membership system, only it's reliable UDP messaging. Using UDP messaging through JGroups has a couple of issues:
...
Rather than continue with using UDP, we would like to replace the UDP messaging for membership with a TCP-based messaging system. This will allow us to use the standard encryption protocols for all peer to peer messaging, and it will remove our dependence on jgroups in the long term.
Goals
- Set us up for removing JGroups as dependency in future versions by moving to a protocol that does not require JGroups
- Support rolling upgrades from the old JGroups protocol to the new TCP-based protocol
- Use the existing SSL settings to control how membership messages are encrypted
- Be as reliable in the face of networking failures as the previous protocol
Anti-Goals
- It is not a goal to replace the existing and separate peer-to-peer TCP messaging system that is used for cache operations. The new messaging system is initially targeted only at replacing our use of jgroups UDP for membership messages.
Design
All of the messaging related to membership is handled by the JGroupsMessenger class, which implements the Messenger interface. We will create a new implementation of Messenger that uses TCP sockets, rather than JGroups and UDP sockets.
Our proposal for the new Messenger is to implement a TCP server and client using Netty. The Netty server and client will use the existing cluster SSL configuration. So if cluster SSL is enabled no additional properties will be required. See https://geode.apache.org/docs/guide/latest/managing/security/implementing_ssl.html for information on the relevant properties. The server socket will use the existing bind-address and membership-port-range properties to determine it's address and port.
The Netty based messenger will maintain one connection to each peer. When sending a message the messenger will create a connection to all destinations if no connection exists yet. Once a connection is established, the connection will remain open until the messenger is told to shut it down.
Messages received by the Netty server will be dispatched from Netty event loop threads. For this reason, it is important that message processing should not block, or it will prevent other messages from being received. The old JGroupsMessenger dispatched messages using a single jgroups receiver thread.
Backwards Compatibility and Upgrade Path
We need to be able to do a rolling upgrade from the old JGroups-based protocol to the new protocol. We will need to continue to support rolling upgrades for a certain range of versions before we can drop JGroups.
...
For the last two options, we will need to version either InternalDistributedMember itself, or the FindCoordinatorResponse and GMSMembershipView classes. Old members will only receive the UDP port, newer members will receive both ports.
Handling TCP connection failures
The contract of the Messenger is that it keeps Messenger is used to send messages to destinations that may or may not be part of the current membership view. If a member is in the view, the Messenger needs to keep trying to deliver messages to the destination that member as long as those destinations are that member is still in the view. Because individual TCP connections can fail, this basically forces us to implement a reliability layer above TCP that will continue to retry messages until a member is removed from the view. This layer needs to be able to:
...
The Messenger is also used by membership to check for a quorum of members, after a network partition event. So we need to ensure that we retain the ability to send a ping message out to all members and check for responses within a certain time window.
Changes and Additions to Public Interfaces
Because we are eventually getting rid of udp messaging, the following udp related gemfire properties will be deprecated:
udp-fragment-size
udp-recv-buffer-size
udp-send-buffer-size
security-udp-dhalgo
mcast-address
- mcast-ttl
- mcast-flow-control
- mcast-port
- mcast-recv-buffer-size
- mcast-send-buffer-size
- disable-tcp
Performance Impact
This proposal is only targeted at replacing the UDP messages used for membership events, which are fairly low traffic. Region operations like puts and gets go over separate TCP connections that are not going to be changed as part of this proposal. Therefore we don't anticipate any performance impact for region operations.
In the future we could consider switching all peer-to-peer messages to flow through this new TCP messaging system. We would only do that if the new system has similar or better performance than the old system.
Alternatives
Use DTLS
One option to address concerns with using custom encryption protocol would be to continue using jgroups, but remove our old security-udp-dhalgo property in favor of using DTLS. However, this approach is complicated by the fact the the JDK does not have DTLS support built in to Java 8, which would require us to backport and maintain an implementation. In addition this would not help us fix the problem of being tied to an outdated jgroups stack that cannot be upgraded.
Use the existing P2P tcp socket code
One the face of it, just using our existing TCP sockets for membership messages as well seems attractive. However this code as it is written is dependent on the membership system to bootstrap itself. For example it will not even send messages until the member has completely joined and some startup messages are exchanged. It would take some refactoring to be able to send membership messages over this system in order to bootstrap membership. The existing P2P socket code is also fairly old, and we feel a better path forward is to move towards a new system based on netty that is less entangled with the rest of the system.