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:
Programmatically using an API
Administration tools
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.
![]() | |
Calling |
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