[jgroups-users] BUG / Nodes not determining the same merge leader when merging 2 clusters

Questions/problems related to using JGroups <[email protected]> Fri, 1 Sep 2017 13:54:43 +0000
Newsgroups gmane.comp.java.javagroups.general
Message-ID <mailman.16153.1504276173.7456.javagroups-users@lists.sourceforge.net>
Hello,

First, thanks to JGroups community for all the great work done !

I've been working on a distributed system using JGroups 2.12.1.Final to solve some issues.
And I suspect a bug on "Discovery" class/protocol.


*         Environment context

-          JGroups version : 2.12.1.Final

-          11 nodes

-          Tested environment : Ability to split and isolate the network into 2 sub networks

o   Sub network A of 3 nodes

o   Sub network B of 8 nodes

-          JGroups configuration using TCPPING -> see attached file


*         Issue : Nodes not determining the same merge leader when merging 2 clusters

*         Problem analysis :

MERGE2 triggers a scheduled task with a fixed delay to discover new groups in regular basis. It sends an event 'FIND_ALL_VIEWS' down from the protocol stack. It is handled by TCPPING which is responsible to build and send a PING to all initial hosts set in jgroups configuration files. PING is a 'GET_MBRS_REQ' message to discover new groups. As it is implemented in JGroups 2.12.1  and as it is shown below in "Discovery.java" class, the 'GET_MBRS_REQ' message is handled by the distant "initial hosts" only if the message is coming from a member in different view. Otherwise, the message is discarded.

    public Object up(Event evt) {

        switch(evt.getType()) {

            case Event.MSG:
                ...
                switch(hdr.type) {

                    case PingHeader.GET_MBRS_REQ:   // return Rsp(local_addr, coord)
                        ...
                        if(return_entire_cache && hdr.view_id == null && rank != 0) {
                            ...
                        }
                        else {
                            if(hdr.view_id != null) {

                                // If the discovery request is merge-triggered, and we the ViewId shipped with it
                                // is the same as ours, we don't respond (JGRP-1315).
                                ViewId my_view_id=view != null? view.getViewId() : null;
                                if(my_view_id != null && Util.sameViewId(my_view_id, hdr.view_id))
                                    return null;
                            }
                        ...

When the new subgroups discovered is returned back to the MERGE2 protocol stack, and if the current node is coordinator, it adds its group to the discovered ones to determine the "merge leader".

        private List<PingData> findAllViews() {
            List<PingData> retval=(List<PingData>)down_prot.down(new Event(Event.FIND_ALL_VIEWS));
            if(retval == null) return Collections.emptyList();
            if(is_coord && local_addr != null) {
                PingData tmp=new PingData(local_addr, view, true);
                //let's make sure that we add ourself as a coordinator
                if(!retval.contains(tmp))
                    retval.add(tmp);
            }
            return retval;
        }


*** The issue *** is that it happens that a non-coordinator node triggered the FindSubgroupsTask in MERGE2 protocol stack when merging clusters. The result is that it ends off a different groups discovered and different merge leader determined, which may cause merge to fail !



*         Solution : Fix the regression introduced by JGRP-1315 by responding to the GET_MBRS_REQ when the 'GET_MBRS_REQ' message receiver is coordinator.

*         Below is the proposed patch in Discovery.java :

                            if(hdr.view_id != null) {
                                // If the discovery request is merge-triggered, and we the ViewId shipped with it
                                // is the same as ours, we don't respond (JGRP-1315).
                                ViewId my_view_id=view != null? view.getViewId() : null;

-                                if(my_view_id != null && Util.sameViewId(my_view_id, hdr.view_id)) {
+                                if(my_view_id != null && Util.sameViewId(my_view_id, hdr.view_id) && !Util.isCoordinator(view, local_addr)) {
                                    log.debug("... Not responding to 'GET_MBRS_REQ' [ping_view_id=" + hdr.view_id + ", my_view_id=" + my_view_id + ", rank=" + rank + "]");
                                    return null;
                                }
                            }

Many thanks,
Thomas UNG

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot

_______________________________________________
javagroups-users mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/javagroups-users
rdf-nm-tcp-conf.xml (application/xml, 2.2 KB)
<config xmlns="urn:org:jgroups"
		xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
		xsi:schemaLocation="urn:org:jgroups http://www.jgroups.org/schema/JGroups-2.12.xsd">

	<TCP
		bind_port="${rdf.jgroups.tcp_protocol.bind_port:7800}"
		bind_addr="${rdf.jgroups.all_protocols.bind_address:localhost}"
		singleton_name="RDF_TCP_TP"
		loopback="true"
		recv_buf_size="20M"
		send_buf_size="640K"
		discard_incompatible_packets="true" 
		max_bundle_size="64K"
		max_bundle_timeout="30" 
		enable_bundling="false" 
		use_send_queues="true"
		sock_conn_timeout="300"

		timer_type="new"
		timer.min_threads="4"
		timer.max_threads="10"
		timer.keep_alive_time="3000"
		timer.queue_max_size="500"

		thread_pool.enabled="true"
		thread_pool.min_threads="1" 
		thread_pool.max_threads="20"
		thread_pool.keep_alive_time="10000" 
		thread_pool.queue_enabled="false"
		thread_pool.queue_max_size="100" 
		thread_pool.rejection_policy="discard"

		oob_thread_pool.enabled="true" 
		oob_thread_pool.min_threads="1"
		oob_thread_pool.max_threads="8" 
		oob_thread_pool.keep_alive_time="5000"
		oob_thread_pool.queue_enabled="false" 
		oob_thread_pool.queue_max_size="100"
		oob_thread_pool.rejection_policy="discard" 

		log_discard_msgs="false"/>

	<TCPPING
		timeout="3000"
		initial_hosts="${rdf.jgroups.tcpping_protocol.initial_hosts}"
		port_range="0"
		num_initial_members="2"
		num_ping_requests="1"/>

	<MERGE2 
		max_interval="30000" 
		min_interval="10000" />

	<FD_SOCK/>

	<!--
		<FD timeout="10000" max_tries="5" />
	-->

	<FD_ALL/>

	<VERIFY_SUSPECT 
		timeout="1500" />

	<pbcast.NAKACK 
		use_mcast_xmit="false" 
		gc_lag="100"
		retransmit_timeout="600,1200,2400,4800"
		discard_delivered_msgs="true"/>

	<UNICAST 
		timeout="300,600,1200" />

	<pbcast.STABLE 
		stability_delay="1000" 
		desired_avg_gossip="20000"
		max_bytes="4m" />

	<pbcast.GMS 
		print_local_addr="true" 
		join_timeout="5000"
		view_bundling="true"/>

	<UFC max_credits="2M"
		min_threshold="0.4"/>

	<MFC max_credits="2M"
		min_threshold="0.4"/>

	<FRAG2
		frag_size="60000" />

	<pbcast.STREAMING_STATE_TRANSFER />

	<pbcast.FLUSH/>

	<CENTRAL_LOCK
		num_backups="3" />

</config>