Disabled partition behavior

When a partition is disabled, any objects in the partition are migrated to the new active node for the partition. The Example 7.9, “Method execution in disabled partition” demonstrates this behavior. An object is created in a partition, and then the active node for the partition is disabled. A method is executed on the object before and after the partition is disabled to show the method execution migrating from the initial active node to the new active node.

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.

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

  4. All nodes in the cluster wait until the partition is defined and enabled on all nodes.

  5. A method is executed on the object on all nodes in the cluster.

  6. The partition is disabled on node A.

  7. All nodes wait for the partition to no longer be active on node A.

  8. A method is executed on the object on all nodes in the cluster.

  9. The example waits to be stopped.

Example 7.9. Method execution in disabled partition

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

import com.kabira.platform.ManagedObject;
import com.kabira.platform.Transaction;
import com.kabira.platform.Transaction.Rollback;
import com.kabira.platform.annotation.Managed;
import com.kabira.platform.highavailability.Partition;
import com.kabira.platform.highavailability.PartitionManager;
import static com.kabira.platform.highavailability.PartitionManager.DisableAction.LEAVE_CLUSTER;
import static com.kabira.platform.highavailability.PartitionManager.EnableAction.JOIN_CLUSTER;
import com.kabira.platform.highavailability.PartitionMapper;
import com.kabira.platform.highavailability.PartitionNotFound;
import com.kabira.platform.highavailability.ReplicaNode;
import static com.kabira.platform.highavailability.ReplicaNode.ReplicationType.SYNCHRONOUS;
import com.kabira.platform.property.Status;

/**
 * This snippet demonstrates remote method invocation behavior for highly
 * available objects
 * <p>
 * <h2> Target Nodes</h2>
 * <ul>
 * <li> <b>domainname</b> = Development
 * </ul>
 */
public class MethodInvocation
{
    @Managed
    private static class X
    {
        //
        //  Returns node name where executed
        //
        String whereExecuted()
        {
            return System.getProperty(Status.NODE_NAME);
        }
    }
    
    //
    //  Partition mapper that puts objects in known partition
    //
    private static class NodeMapper extends PartitionMapper
    {

        @Override
        public String getPartition(Object obj)
        {
            return PARTITION_NAME;
        }
    }
    
    /**
     * Main entry point
     *
     * @param args Not used
     * @throws java.lang.InterruptedException
     */
    public static void main(String[] args) throws InterruptedException
    {
        //
        //  Initialize snippet
        //
        initialize();

        //
        //  Create an object on node A
        //
        if (m_nodeName.equals("A") == true)
        {
            new Transaction("Create object")
            {
                @Override
                protected void run() throws Rollback
                {
                    new X();
                }
            }.execute();
        }
        
        //
        //  Wait for partition to be active on node A
        //
        waitForPartition();

        //
        //  Execute object method
        //
        executeMethod();

        //
        //  Disable the partition on node A
        //
        disablePartition();

        //
        //  Wait for partition to no longer be active on node A
        //
        waitForDisable();

        //
        //  Execute object method
        //
        executeMethod();

        //
        //  Wait for termination
        //
        waitForStop();
    }
    
    private static void initialize()
    {
        new Transaction("Initialization")
        {
            @Override
            protected void run() throws Rollback
            {
                //
                //  Install the partition mapper
                //
                PartitionManager.setMapper(X.class, new NodeMapper());

                //
                //  Set up replica node list
                //
                ReplicaNode[] replicas = new ReplicaNode[]
                {
                    new ReplicaNode("B", SYNCHRONOUS),
                    new ReplicaNode("C", SYNCHRONOUS)
                };
                
                //
                //  Force replication
                //
                Partition.Properties properties =  new Partition.Properties();
                properties.forceReplication(true);

                //
                //  Define the partition
                //
                PartitionManager.definePartition(
                    PARTITION_NAME, properties, "A", replicas);
                
                //
                //  Enable the partition
                //
                Partition partition = PartitionManager.getPartition(PARTITION_NAME);
                partition.enable(JOIN_CLUSTER);
            }
        }.execute();
    }

    //
    //  Wait for termination
    //
    private static void waitForStop() throws InterruptedException
    {
        System.out.println("Waiting for stop...");

        while (true)
        {
            Thread.sleep(5000);
        }
    }

    //
    //  Disable partition
    //
    private static void disablePartition()
    {
        new Transaction("Disable partition")
        {
            @Override
            protected void run()
            {
                if (m_nodeName.equals("A") == false)
                {
                    return;
                }
                PartitionManager.disablePartitions(LEAVE_CLUSTER);
                System.out.println(PARTITION_NAME + " disabled.");
            }
        }.execute();
    }

    //
    //  Wait for partition to be defined
    //
    private static void waitForPartition() throws InterruptedException
    {
        System.out.println("Waiting for partition...");

        while (m_partitionFound == false)
        {
            Thread.sleep(5000);

            new Transaction("Wait for Partition")
            {
                @Override
                protected void run()
                {
                    try
                    {
                        Partition partition = PartitionManager.getPartition(PARTITION_NAME);
                        
                        //
                        //  Wait for partition to go active on node A
                        //
                        m_partitionFound = partition.getActiveNode().equals("A");
                    }
                    catch (PartitionNotFound ex)
                    {
                        // not available yet
                    }
                }
            }.execute();
        }

        System.out.println("Partition " + PARTITION_NAME + " found.");
    }

    //
    //  Wait for partition to move off of A
    //
    private static void waitForDisable() throws InterruptedException
    {
        System.out.println("Partition still active on node A...");

        while (m_activeOnA == true)
        {
            Thread.sleep(5000);

            new Transaction("Wait for Partition")
            {
                @Override
                protected void run()
                {
                    Partition p = PartitionManager.getPartition(PARTITION_NAME);

                    m_activeOnA = p.getActiveNode().equals("A");
                }
            }.execute();
        }

        System.out.println("Partition no longer active on node A");
    }

    //
    //  Execute method on objects
    //
    private static void executeMethod()
    {
        new Transaction("Execute Method")
        {
            @Override
            protected void run()
            {
                for (X x : ManagedObject.extent(X.class))
                {
                    System.out.println("Executed on: " + x.whereExecuted());
                }
            }
        }.execute();
    }
    
    private static Boolean m_activeOnA = true;
    private static Boolean m_partitionFound = false;
    private static final String m_nodeName = System.getProperty(Status.NODE_NAME);
    private static final String PARTITION_NAME = MethodInvocation.class.getSimpleName();
}

When the snippet is executed it outputs the information (annotation added and messages reordered for clarity) in Example 7.10, “Disabled partition output”.

Example 7.10. Disabled partition output

#
#    All nodes are waiting for the partition to be enabled
#
[A] Waiting for partition...
[B] Waiting for partition...
[C] Waiting for partition...

#
#    The partition is found active by all of the nodes
#
[B] Partition Method Invocation found.
[A] Partition Method Invocation found.
[C] Partition Method Invocation found.

#
#   Method executed on object created in partition.  
#   Method executes on node A (current active node).
#
[B] Executed on: A
[A] Executed on: A
[C] Executed on: A

#
#   Partition disabled on node A
#
[A] Method Invocation disabled.

#
#    All nodes waiting for partition to be disabled on node A
#
[B] Partition still active on node A...
[A] Partition still active on node A...
[C] Partition still active on node A...

#
#   Partition no longer active on node A
#
[B] Partition no longer active on node A
[A] Partition no longer active on node A
[C] Partition no longer active on node A

#
#   Method executed on object.  Now executes on node B (new active node)
#   Object no longer found on node A since partition migrated to
#   node B removing object from node A.
#
[B] Executed on: B
[C] Executed on: B

#
#   Snippet waiting for termination
#
[B] Waiting for stop...
[A] Waiting for stop...
[C] Waiting for stop...