Updating partition mapping

Objects can be dynamically reassigned to other partitions. This allows objects to be split or merged into partitions based on changing application states or available system resources.

There are two mechanisms to update the partition mapping:

Programmatic re-partitioning is covered in this section. See the ActiveSpaces® Transactions Administration Guide for details on re-partitioning objects using the administration tools.

Object re-partitioning requires either a partition mapper that makes dynamic partitioning decisions or the installation of a new partition mapper. A dynamic partition mapper can return different partition names for the same object when called at different times.

Re-partitioning is triggered by the partition.update() or PartitionManager.repartitionInstance(...) methods. These methods cause the partition mappers to be called for the objects being re-partitioned. Re-partitioning using the partition.update() method must be done on the active node for the partition.

The partition properties specified when a partition was defined can be overridden when using the partition.update() method. This only affects the properties for the duration of the partition.update() execution. It does not change the default properties associated with a partition.

[Warning]

Calling partition.update() with any partitioned objects locked in the transaction causes the objects locked per transactions property to be treated as if it had a value of zero. This causes all objects in the partition to be locked in the calling transaction during the re-partitioning. It is strongly recommended that partition.update() always be called from a transaction with no partitioned objects locked.

Example 7.4, “Migrating a partition” shows an example of splitting a partition and then migrating the new partition to a new node.

Example 7.7. Splitting a partition

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

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.EnableAction.JOIN_CLUSTER;
import com.kabira.platform.highavailability.PartitionMapper;
import com.kabira.platform.highavailability.ReplicaNode;
import static com.kabira.platform.highavailability.ReplicaNode.ReplicationType.SYNCHRONOUS;
import com.kabira.platform.property.Status;

/**
 * This snippet demonstrates how to split a partition by repartitioning and then
 * migrating one of the partitions
 * <p>
 * <h2> Target Nodes</h2>
 * <ul>
 * <li> <b>domainname</b> = "Development"
 * </ul>
 */
public class Repartition
{
    @Managed
    private static class A
    {
        A(int value)
        {
            m_value = value;
        }
        private final int m_value;
    }

    private static class DynamicMapper extends PartitionMapper
    {
        @Override
        public String getPartition(Object object)
        {

            if (m_split == false)
            {
                return _PARTITION_ORG;
            }

            assert object instanceof A : object;
            A a = (A) object;

            String name = null;
            if ((a.m_value % 2) == 0)
            {
                name = _PARTITION_ORG;
            }
            else
            {
                name = _PARTITION_NEW;
            }
            return name;
        }

        void setSplit()
        {
            m_split = true;
        }
        private boolean m_split = false;
    }

    /**
     * Main entry point
     *
     * @param args Not used
     * @throws java.lang.InterruptedException
     */
    public static void main(final String[] args) throws InterruptedException
    {

        initialize();

        //
        //    Create some objects on A
        //
        if (m_nodeName.equals("A") == true)
        {
            createObjects();
        }
        else
        {
            waitForPartitionActive(_PARTITION_ORG, "A");
        }

        displayPartitionCounts();

        //
        //    Add a new partition
        //
        addPartition();

        waitForPartitionActive(_PARTITION_NEW, "A");

        //
        //    Update the partition on node A
        //
        if (m_nodeName.equals("A") == true)
        {
            updatePartition();
        }

        //
        //    Migrate the new partition to node B
        //
        if (m_nodeName.equals("A") == true)
        {
            migratePartition();
        }
        else
        {
            waitForPartitionActive(_PARTITION_NEW, "B");
        }

        displayPartitionCounts();
    }

    private static void initialize()
    {
        new Transaction("Initialize")
        {
            @Override
            protected void run() throws Rollback
            {
                m_mapper = new DynamicMapper();
                PartitionManager.setMapper(A.class, m_mapper);

                ReplicaNode[] replicas = new ReplicaNode[]
                {
                    new ReplicaNode("B", SYNCHRONOUS),
                    new ReplicaNode("C", SYNCHRONOUS)
                };

                PartitionManager.definePartition(_PARTITION_ORG, null, "A", replicas);
                PartitionManager.enablePartitions(JOIN_CLUSTER);
            }
        }.execute();
    }

    private static void addPartition()
    {
        new Transaction("Add Partition")
        {
            @Override
            protected void run() throws Rollback
            {
                ReplicaNode[] replicas = new ReplicaNode[]
                {
                    new ReplicaNode("B", SYNCHRONOUS),
                    new ReplicaNode("C", SYNCHRONOUS)
                };

                PartitionManager.definePartition(_PARTITION_NEW, null, "A", replicas);
                Partition partition = PartitionManager.getPartition(_PARTITION_NEW);
                partition.enable(JOIN_CLUSTER);
            }
        }.execute();
    }

    private static void createObjects()
    {
        new Transaction("Create Objects")
        {
            @Override
            protected void run() throws Rollback
            {
                for (int i = 0; i < _NUMBER_OF_OBJECTS; i++)
                {
                    new A(i);
                }
            }
        }.execute();
    }

    private static void updatePartition()
    {
        new Transaction("Configure Partition Mapper")
        {

            @Override
            protected void run() throws Rollback
            {
                m_mapper.setSplit();
            }
        }.execute();

        new Transaction("Update Objects")
        {
            @Override
            protected void run() throws Rollback
            {
                //
                //    Update the original partition
                //
                Partition partition = PartitionManager.getPartition(_PARTITION_ORG);
                partition.update(null);
            }
        }.execute();
    }

    private static void migratePartition()
    {
        System.out.println("Migrating new partition to node B");

        new Transaction("Migrate Partition")
        {
            @Override
            protected void run() throws Transaction.Rollback
            {
                Partition partition = PartitionManager.getPartition(_PARTITION_NEW);

                //
                //    Migrate partition to node B
                //
                ReplicaNode[] replicas = new ReplicaNode[]
                {
                    new ReplicaNode("C", SYNCHRONOUS),
                    new ReplicaNode("A", SYNCHRONOUS)
                };
                partition.migrate(null, "B", replicas);
            }
        }.execute();
    }

    private static void displayPartitionCounts()
    {
        new Transaction("Display Partition Counts")
        {
            @Override
            protected void run()
            {
                for (Partition p : PartitionManager.getPartitions())
                {
                    System.out.println(p.cardinality()
                        + " objects in "
                        + p.getName()
                        + " active on node " 
                        + p.getActiveNode());
                }
            }
        }.execute();
    }

    private static void waitForPartitionActive(
        final String name,
        final String nodename) throws InterruptedException
    {
        m_active = false;

        while (m_active == false)
        {
            new Transaction("Wait for Partition")
            {
                @Override
                protected void run()
                {
                    Partition partition = PartitionManager.getPartition(name);
                    assert partition != null : name;
                    if ((partition.getCurrentState() == Partition.State.ACTIVE)
                        && (partition.getActiveNode().equals(nodename)))
                    {
                        m_active = true;
                    }
                }
            }.execute();

            Thread.sleep(1000);
        }
    }

    private static final String m_nodeName = System.getProperty(Status.NODE_NAME);
    private static final int _NUMBER_OF_OBJECTS = 10;
    private static final String _PARTITION_ORG = Repartition.class.getSimpleName();
    private static final String _PARTITION_NEW = Repartition.class.getSimpleName() + "_new";

    private static boolean m_active = false;
    private static DynamicMapper m_mapper;
}

When Example 7.7, “Splitting a partition” is run it outputs the information in Example 7.8, “Partition split example output” (annotation added and output reordered for clarity).

Example 7.8. Partition split example output

#
#  Create 10 objects in original partition
#
[C] 0 objects in Repartition active on node A
[C] 0 objects in Repartition active on node A
[A] 10 objects in Repartition active on node A
[A] 0 objects in Repartition_new active on node A
[B] 0 objects in Repartition_new active on node A
[B] 10 objects in Repartition active on node A

#
#  Update and migrate the new partition to node B
#
[A] Migrating new partition to node B

#
#  Partition has been spilt and the new partition is now
#  active on node B
#
[A] 5 objects in Repartition_new active on node B
[A] 5 objects in Repartition active on node A
[C] 5 objects in Repartition_new active on node B
[C] 5 objects in Repartition active on node A
[B] 5 objects in Repartition_new active on node B
[B] 5 objects in Repartition active on node A