Transparent failover

When the active node for a partition fails, it transparently fails over to the first replica for the partition. Any in-flight transactions not started on the node that failed transparently failover to the new active node for the partition. This ensures that any object modifications or method invocations complete successfully without any application action.

Example 7.11, “Transparent failover” shows this behavior for a method invocation. These steps are executed in the snippet:

  1. A partition mapper is installed on all nodes.

  2. A partition is defined and enabled on all nodes with node B as the active node and node C as a replica.

  3. An object is created in the partition on node A.

  4. A method is executed on the object that is dispatched to node B.

  5. The method causes node B to fail.

  6. The partition falls over to the replica node C.

  7. The method transparently executes on node C.

[Warning]

This snippet causes a hard node failure.

Example 7.11. Transparent failover

//     $Revision: 1.1.4.2 $
package com.kabira.snippets.highavailability;

import com.kabira.platform.Transaction;
import com.kabira.platform.annotation.Managed;
import com.kabira.platform.highavailability.PartitionManager;
import com.kabira.platform.highavailability.PartitionManager.EnableAction;
import com.kabira.platform.highavailability.PartitionMapper;
import com.kabira.platform.highavailability.ReplicaNode;
import com.kabira.platform.property.Status;

/**
 * Transparently fail-over following an active node failure.
 * <p>
 * <h2> Target Nodes</h2>
 * <ul>
 * <li> <b>domainname</b> = Development
 * </ul>
 */
public class TransparentFailover
{
    @Managed
    private static class A
    {
        void hello(final String fromNode)
        {
            String nodeName = System.getProperty(Status.NODE_NAME);
            System.err.println("INFO: On node " + nodeName
                + ":From node " + fromNode);

            //
            //	Force the engine down if on active node
            //
            if (nodeName.equals(_ACTIVE_NODE) == true)
            {
                System.err.println("WARNING: Forcing down node " + nodeName);

                //
                //	Just to let messages get to deployment tool
                //
                try
                {
                    Thread.sleep(1000);
                }
                catch (InterruptedException ex)
                {
                    // Don't care
                }
                Runtime.getRuntime().halt(0);
            }
        }
    }

    private static class Mapper extends PartitionMapper
    {
        @Override
        public String getPartition(Object o)
        {
            return _PARTITION_NAME;
        }
    }

    /**
     * Run the transparent failover snippet
     *
     * @param args None
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException
    {

        final String nodeName = System.getProperty(Status.NODE_NAME);

        initialize();

        if (nodeName.equals(_TEST_NODE) == false)
        {
            System.out.println("INFO: Sleeping on node " + nodeName);
            Thread.sleep(10000);
            return;
        }

        new Transaction()
        {
            @Override
            protected void run() throws Transaction.Rollback
            {
                A a = new A();
                a.hello(nodeName);
            }
        }.execute();
    }

    private static void initialize()
    {
        new Transaction()
        {
            @Override
            protected void run() throws Transaction.Rollback
            {
                PartitionManager.setMapper(A.class, new Mapper());
                ReplicaNode[] replicaNodes =
                {
                    new ReplicaNode(_REPLICA_NODE, ReplicaNode.ReplicationType.SYNCHRONOUS)
                };
                PartitionManager.definePartition(
                    _PARTITION_NAME, null, _ACTIVE_NODE, replicaNodes);
                PartitionManager.enablePartitions(EnableAction.JOIN_CLUSTER_PURGE);
            }
        }.execute();
    }

    private static final String _TEST_NODE = "A";
    private static final String _ACTIVE_NODE = "B";
    private static final String _REPLICA_NODE = "C";
    private static final String _PARTITION_NAME = "Transparent Failover";
}


When this snippet is executed it outputs the following messages (annotation added):

[C] INFO: Sleeping on node C
[B] INFO: Sleeping on node B

#
#  Method called from node A executes on node B
#
[B] INFO: On node B:From node A

#
#  Node B is forced down with an in-flight transaction
#
[B] WARNING: Forcing down node B

#
#  Method is transparently reexecuted on node C
#
[C] INFO: On node C:From node A