001// 002// Name 003// $RCSfile: Partition.java,v $ 004// 005// Copyright 006// Copyright 2010-2014 Cloud Software Group, Inc. ALL RIGHTS RESERVED. 007// Cloud Software Group, Inc. Confidential Information 008// 009// History 010// $Revision: 1.1.2.30 $ $Date: 2015/01/27 03:10:25 $ 011// 012package com.kabira.platform.highavailability; 013 014import com.kabira.platform.ResourceUnavailableException; 015import com.kabira.platform.disteng.DEPartitionManager; 016import com.kabira.platform.disteng.DENodeMismatch; 017import com.kabira.platform.disteng.DEReplicationType; 018import com.kabira.platform.disteng.DEProperties; 019import com.kabira.platform.disteng.DEPartitionStats; 020import com.kabira.platform.disteng.DESparsePartitionAudit; 021import com.kabira.platform.disteng.DERemoteEnableAction; 022import com.kabira.platform.disteng.DEPartitionBroadcast; 023import java.util.Date; 024 025/** 026 * The Partition class. This non-managed class contains a snapshot of a 027 * shared memory partition. Direct access to the Partition instance in 028 * shared memory is not allowed to avoid transaction locking issues. 029 * <p> 030 * Note that partitions are only visible on their active and replica 031 * nodes, not all nodes in the cluster. 032 */ 033public final class Partition 034{ 035 /** 036 * The Partition states 037 */ 038 public enum State 039 { 040 /** The partition has been defined, but not enabled */ 041 INITIAL, 042 /** The partition is active */ 043 ACTIVE, 044 /** 045 * The partition definition is being updated. Entered when 046 * partition membership (i.e. the objects associated with the 047 * partition) is being updated to split or merge partitions. 048 */ 049 UPDATING, 050 /** 051 * The partition owner is being updated. Each object in the 052 * partition is sent to the new owner and the location definition 053 * of all instances are updated. 054 */ 055 MIGRATING, 056 /** 057 * The partition replicas are being updated. Each object in the 058 * partition is sent to the new replica list. 059 */ 060 REPLICATING, 061 /** 062 * The partition is not active because all associated nodes have 063 * failed, the node was taken off-line because it is in a 064 * non-quorum state, or the node was restarted. 065 */ 066 UNAVAILABLE 067 } 068 069 /** 070 * The Partition status 071 */ 072 public enum Status 073 { 074 /** The partition has been disabled */ 075 LOCAL_DISABLED, 076 /** The partition has been defined, but not enabled */ 077 LOCAL_DEFINED, 078 /** The partition has been defined and enabled. */ 079 LOCAL_ENABLED, 080 /** The partition has been defined remotely. */ 081 REMOTE_DEFINED, 082 /** 083 * The partition has been defined and enabled on a remote node 084 * but not defined on this node. 085 */ 086 REMOTE_ENABLED 087 } 088 089 /** 090 * The Partition properties used when defining a partition 091 */ 092 public final static class Properties 093 { 094 /** 095 * An enumeration of the possible audits that can be taken when 096 * enabling a sparse Partition. 097 * <p> 098 * A sparse Partition is a partition that doesn't have the local 099 * node as the active node, or in the replica node list. When 100 * enabled, no data migration takes place; instead the node list 101 * is verified against the node list that is present on the 102 * active node to verify they match. 103 * <p> 104 * When restoring multiple nodes, it may be necessary to disable 105 * the auditing of node lists, since the active node may have a 106 * node list that is the result of a previous failover. 107 * <p> 108 * The default audit is defined by {@link #DefaultSparseAudit}, 109 */ 110 public enum SparseAudit 111 { 112 /** 113 * Verify that the defined node list matches the active node's 114 * node list. 115 */ 116 VERIFY_NODE_LIST, 117 /** 118 * Do not perform any verification of node lists. 119 */ 120 IGNORE_NODE_LIST 121 } 122 123 /** 124 * An enumeration of the possible actions to be taken when a remote 125 * node enables a Partition. 126 * <p> 127 * When a remote node enables a partition, it will enable the 128 * partition on all nodes in the node list, and update the partition 129 * status and state. This is done even on nodes that have explicitly 130 * disabled the partition. This behaviour can be changed by setting 131 * the RemoteEnableAction to LEAVE_DISABLED. Note that this only 132 * controls the behaviour on the local node, it does not affect 133 * partitions defined on remote nodes. 134 * <p> 135 * The default action is defined by {@link #DefaultRemoteEnableAction}, 136 */ 137 public enum RemoteEnableAction 138 { 139 /** 140 * Allow remote nodes to enable the partition on the local node. 141 */ 142 ENABLE_PARTITION, 143 /** 144 * Do not allow remote nodes to enable the partition on the 145 * local node if it has been explicitly disabled. 146 */ 147 LEAVE_DISABLED 148 } 149 150 /** 151 * Default constructor. 152 */ 153 public Properties() 154 { 155 m_restoreFromNode = null; 156 m_forceReplication = false; 157 m_objectsLockedPerTransaction = DefaultObjectsLockedPerTransaction; 158 m_numberOfThreads = DefaultNumberOfThreads; 159 m_sparseAudit = DefaultSparseAudit; 160 m_remoteEnableAction = DefaultRemoteEnableAction; 161 m_broadcastUpdates = DEPartitionBroadcast.BroadcastDefault; 162 } 163 164 /** 165 * Define the node that the partition should be restored from. 166 * <p> 167 * When this property is set, the partition defined on the given 168 * remote node is loaded to the local node. This should be done 169 * when restoring a node from a split-brain situation, where 170 * <b>nodeName</b> is the node in the cluster where all objects 171 * should be preserved, and the local node is the node being 172 * restored. Any conflicts during restore will preserve the 173 * objects on <b>nodeName</b>, and remove the conflicting objects 174 * on the local node. 175 * <p> 176 * A restore is needed when multiple nodes are currently the active 177 * node for a partition in a cluster due to a split-brain scenario. 178 * In this case, the application needs to decide which active node 179 * will be the node where the objects are preserved during a 180 * restore. Note that the <b>nodeName</b> does not necessarily have 181 * to be the node which becomes the partition's active node after the 182 * restore completes. 183 * <p> 184 * The actual restore of the partition is done in the 185 * enable() or enablePartitions() method when the JOIN_CLUSTER_RESTORE 186 * EnableAction is used. If any other EnableAction is used, 187 * object data isn't preserved, and no restoration of partition 188 * objects is done. 189 * <p> 190 * If restoreFromNode isn't set after a split-brain scenario, the 191 * runtime will perform a cluster wide broadcast to find the 192 * current active node, and use that node to restore instances in 193 * the partition. If multiple active nodes are found, the first 194 * responder is chosen. 195 * <p> 196 * @param nodeName The remote node to use when restoring the 197 * partition's objects. 198 * 199 * @exception IllegalArgumentException 200 * The nodeName was empty. 201 * @see PartitionManager#definePartition(String, 202 * Partition.Properties, String, ReplicaNode []) 203 * @see PartitionManager.EnableAction 204 */ 205 public void restoreFromNode(String nodeName) 206 throws IllegalArgumentException 207 { 208 if (nodeName.isEmpty()) 209 { 210 throw new IllegalArgumentException( 211 "nodeName cannot be empty"); 212 } 213 m_restoreFromNode = nodeName; 214 } 215 216 /** 217 * Get the current restoreFromNode property value. 218 * @return String containing value. 219 */ 220 public final String getRestoreFromNode() 221 { 222 return m_restoreFromNode; 223 } 224 225 /** 226 * Determine how objects are replicated during a migrate of 227 * an object partition. 228 * <p> 229 * When set to true, a migrate() or 230 * definePartition()/enablePartitions() will force the copy of 231 * partitioned objects to all pre-existing replica nodes. The 232 * default value for this property is false, objects are only 233 * copied to new replicas as they are added since the objects 234 * should already exist on the pre-existing replica nodes. 235 * <p> 236 * Normally, a migrate will skip the replication of objects to 237 * pre-existing nodes in the partition's replica node list. This 238 * allows applications to incrementally add replica nodes without 239 * having to copy the objects to replicas that already exist in 240 * the partition. However, if one or more replicas have gone 241 * offline, or were not discovered when the partition was first 242 * enabled, this property can be set to insure that objects are 243 * pushed to all replicas in the cluster. 244 * <p> 245 * Warning: This is performance hostile, and should only be done 246 * if the replica can't be manually taken offline and restored. 247 * <p> 248 * The value passed into definePartition() is stored and used in 249 * failover. The value passed to migrate() overrides 250 * the value passed to definePartition(). 251 * 252 * @param enabled If true, force the copy of objects to all replicas 253 * when a migrate() or enablePartition() is executed. 254 * 255 */ 256 public void forceReplication(boolean enabled) 257 { 258 m_forceReplication = enabled; 259 } 260 261 /** 262 * Get the current forceReplication property value. 263 * @return boolean containing value. 264 */ 265 public final boolean getForceReplication() 266 { 267 return m_forceReplication; 268 } 269 270 /** 271 * Define the number of objects locked in a transaction when performing 272 * a migrate() or update(). 273 * <p> 274 * When distribution performs a migrate() or update(), or when it 275 * performs a migrate() as part of failover, the work is done in 276 * units of work defined by objectsLockedPerTransaction. This 277 * allows applications to concurrently run while the work is in 278 * progress, otherwise all partitioned instances would be locked 279 * in the calling transaction, preventing application code from 280 * establishing a write lock on instances on the active node, or 281 * establishing a read lock on instances on replica nodes. 282 * <p> 283 * The default is defined by {@link #DefaultObjectsLockedPerTransaction} 284 * <p> 285 * If objectsLockedPerTransaction is set to zero, all instances will 286 * be processed in the caller's transaction. 287 * <p> 288 * If the calling transaction has one or more partitioned 289 * instances locked in the transaction, 290 * objectsLockedPerTransaction is ignored, and all work is done 291 * in the caller's transaction. To insure that migrate() or update() 292 * minimizes the number of locks taken, they should be run in separate 293 * transactions that have no partitioned instances locked. 294 * <p> 295 * The value passed into definePartition() is stored and used in 296 * failover. The value passed to migrate() and update() override 297 * the value passed to definePartition(). 298 * 299 * @param objectsLockedPerTransaction Number of objects locked 300 * per transaction when sending data to remote nodes. 301 * @exception IllegalArgumentException The objectsLockedPerTransaction 302 * value was negative. 303 */ 304 public void setObjectsLockedPerTransaction( 305 long objectsLockedPerTransaction) 306 { 307 if (objectsLockedPerTransaction < 0) 308 { 309 throw new IllegalArgumentException( 310 "invalid objectsLockedPerTransaction: " + 311 objectsLockedPerTransaction); 312 } 313 m_objectsLockedPerTransaction = objectsLockedPerTransaction; 314 } 315 316 /** 317 * Get the current objectsLockedPerTransaction property value. 318 * @return long containing value. 319 */ 320 public final long getObjectsLockedPerTransaction() 321 { 322 return m_objectsLockedPerTransaction; 323 } 324 325 /** 326 * Define the number of threads used when performing a migrate(). 327 * <p> 328 * When distribution performs a migrate(), either explicitly or 329 * as part of the enablePartitions() call, the work is done in 330 * multiple threads in order to scale to the number of CPUs 331 * available in the system. 332 * <p> 333 * The default is defined by {@link #DefaultNumberOfThreads} 334 * <p> 335 * If the number of partitioned instances is less that 336 * objectsLockedPerTransaction, only one thread will be used. 337 * <p> 338 * If objectsLockedPerTransaction is zero, or the calling 339 * transaction has one or more partitioned instances locked in 340 * the transaction, numberOfThreads is ignored, and all work is 341 * done in the caller's transaction. 342 * <p> 343 * @param numberOfThreads Number of threads to use 344 * when sending data to remote nodes. 345 * @exception IllegalArgumentException The numberOfThreads 346 * value was less than one. 347 */ 348 public void setNumberOfThreads(long numberOfThreads) 349 { 350 if (numberOfThreads < 1) 351 { 352 throw new IllegalArgumentException( 353 "invalid numberOfThreads: " + numberOfThreads); 354 } 355 m_numberOfThreads = numberOfThreads; 356 } 357 358 /** 359 * Get the current numberOfThreads property value. 360 * @return long containing value. 361 */ 362 public final long getNumberOfThreads() 363 { 364 return m_numberOfThreads; 365 } 366 367 /** 368 * Set the sparseAudit property value. 369 * <p> 370 * The default is defined by {@link #DefaultSparseAudit} 371 * 372 * @param sparseAudit What auditing should be done. 373 * 374 */ 375 public void setSparseAudit(final SparseAudit sparseAudit) 376 { 377 m_sparseAudit = sparseAudit; 378 } 379 380 /** 381 * Get the current sparseAudit property value. 382 * @return SparseAudit enum. 383 */ 384 public final SparseAudit getSparseAudit() 385 { 386 return m_sparseAudit; 387 } 388 389 /** 390 * Set the remoteEnableAction property value. 391 * <p> 392 * The default is defined by {@link #DefaultRemoteEnableAction} 393 * 394 * @param remoteEnableAction The action to take when remote nodes enable 395 * a partition. 396 * 397 */ 398 public void setRemoteEnableAction(final RemoteEnableAction remoteEnableAction) 399 { 400 m_remoteEnableAction = remoteEnableAction; 401 } 402 403 /** 404 * Get the current remoteEnableAction property value. 405 * @return RemoteEnableAction enum. 406 */ 407 public final RemoteEnableAction getRemoteEnableAction() 408 { 409 return m_remoteEnableAction; 410 } 411 412 /** 413 * Set the broadcastUpdates property value. 414 * <p> 415 * By default, this property is enabled. Updates to the partition's 416 * definition are broadcast to all nodes in the cluster so that 417 * the partition definition is consistent across the cluster. 418 * <p> 419 * Warning: The ability to disable this property is only provided 420 * to support the testing of multi-master scenarios. 421 * Disabling this property for any other reason is not recommended. 422 * 423 * @param enabled If true, broadcast partition definition updates 424 * to all nodes in the cluster. 425 */ 426 public void broadcastUpdates(boolean enabled) 427 { 428 m_broadcastUpdates = (enabled == true) 429 ? DEPartitionBroadcast.BroadcastEnabled 430 : DEPartitionBroadcast.BroadcastDisabled; 431 } 432 433 /** 434 * Get the current broadcastUpdates property value. 435 * @return boolean containing value. 436 */ 437 public final boolean getBroadcastUpdates() 438 { 439 // Map both BroadcastDefault and BroadcastEnabled to true. 440 return (m_broadcastUpdates == DEPartitionBroadcast.BroadcastDisabled) 441 ? false : true; 442 } 443 444 // 445 // package private constructor 446 // 447 Properties( 448 boolean forceReplication, 449 long objectChunks, 450 long numThreads, 451 SparseAudit sparseAudit, 452 RemoteEnableAction remoteEnableAction, 453 DEPartitionBroadcast broadcastUpdates) 454 { 455 m_restoreFromNode = null; 456 m_forceReplication = forceReplication; 457 m_objectsLockedPerTransaction = objectChunks; 458 m_numberOfThreads = numThreads; 459 m_sparseAudit = sparseAudit; 460 m_remoteEnableAction = remoteEnableAction; 461 m_broadcastUpdates = broadcastUpdates; 462 } 463 464 String m_restoreFromNode; 465 boolean m_forceReplication; 466 long m_objectsLockedPerTransaction; 467 long m_numberOfThreads; 468 SparseAudit m_sparseAudit; 469 RemoteEnableAction m_remoteEnableAction; 470 DEPartitionBroadcast m_broadcastUpdates; 471 } 472 473 /** 474 * Partition statistics 475 * <p> 476 * Partition statistics are captured for the local node only, any 477 * operations done on remote nodes are not reflected in the stats. As 478 * an example, if the local node is a replica, and the node executes 479 * 10 object creates, the createCount will be set to 10. If objects 480 * are created on the active node, or on other replica nodes, the 481 * createCount will not be updated. 482 * <p> 483 * All partition statistics are reset each time an engine containing 484 * the highavailability component is restarted. They do not persist 485 * across engine restarts. 486 */ 487 public final static class Statistics 488 { 489 /** 490 * The number of instances created in this partition. 491 */ 492 public long createCount; 493 /** 494 * The number of updates that have been applied to instance in this 495 * partition. 496 */ 497 public long updateCount; 498 /** 499 * The number of instances deleted in this partition. 500 */ 501 public long deleteCount; 502 /** 503 * The number of synchronous create failures that occurred in 504 * this partition. 505 */ 506 public long createFailures; 507 /** 508 * The number of synchronous update failures that occurred in 509 * this partition. 510 */ 511 public long updateFailures; 512 /** 513 * The number of synchronous delete failures that occurred in 514 * this partition. 515 */ 516 public long deleteFailures; 517 /** 518 * The number of asynchronous create failures that occurred in 519 * this partition. 520 */ 521 public long asyncCreateFailures; 522 /** 523 * The number of asynchronous update failures that occurred in 524 * this partition. 525 */ 526 public long asyncUpdateFailures; 527 /** 528 * The number of asynchronous delete failures that occurred in 529 * this partition. 530 */ 531 public long asyncDeleteFailures; 532 533 // package private constructor 534 Statistics(DEPartitionStats stats) 535 { 536 this.createCount = stats.createCount; 537 this.updateCount = stats.updateCount; 538 this.deleteCount = stats.deleteCount; 539 this.createFailures = stats.createFailures; 540 this.updateFailures = stats.updateFailures; 541 this.deleteFailures = stats.deleteFailures; 542 this.asyncCreateFailures = stats.asyncCreateFailures; 543 this.asyncUpdateFailures = stats.asyncUpdateFailures; 544 this.asyncDeleteFailures = stats.asyncDeleteFailures; 545 } 546 // private constructor 547 private Statistics() { } 548 } 549 550 /** Index to the active node in the nodes array */ 551 public final static int ActiveNodeIndex = 0; 552 553 /** 554 * The default number of objects locked per transaction when 555 * performing a migrate or update. 556 */ 557 public final static long DefaultObjectsLockedPerTransaction = 1000; 558 559 /** 560 * The default number of threads to use when performing a migrate. 561 */ 562 public final static long DefaultNumberOfThreads = 1; 563 564 /** 565 * The default audit used for sparse partitions. 566 * The value is {@link Properties.SparseAudit#VERIFY_NODE_LIST} 567 */ 568 public final static Properties.SparseAudit DefaultSparseAudit = 569 Properties.SparseAudit.VERIFY_NODE_LIST; 570 571 /** 572 * The default action used for remote partition enables. 573 * The value is {@link Properties.RemoteEnableAction#ENABLE_PARTITION} 574 */ 575 public final static Properties.RemoteEnableAction DefaultRemoteEnableAction = 576 Properties.RemoteEnableAction.ENABLE_PARTITION; 577 578 /** 579 * Get the partition name. 580 * @return Partition name. 581 */ 582 public final String getName() 583 { 584 return m_name; 585 } 586 587 /** 588 * Get the active node for the partition. 589 * @return Active node name. 590 */ 591 public final String getActiveNode() 592 { 593 assert( m_nodes.length >= 1 ); 594 return m_nodes[ActiveNodeIndex]; 595 } 596 597 /** 598 * Get the replica node list for the partition. 599 * @return Array of replica node names. 600 */ 601 public final String [] getReplicaNodes() 602 { 603 assert( m_nodes.length >= 1 ); 604 String [] replicas = new String[m_nodes.length - 1]; 605 for (int i = 0; i < m_nodes.length - 1; i++) 606 { 607 replicas[i] = m_nodes[i + 1]; 608 } 609 return replicas; 610 } 611 612 /** 613 * Get the complete node list for the partition. 614 * <p> 615 * The node list is a String array of node names, with the active 616 * node being at ActiveNodeIndex, followed by a prioritized list of 617 * replica nodes. 618 * @return Array of node names. 619 */ 620 public final String [] getNodeList() 621 { 622 return m_nodes; 623 } 624 625 /** 626 * Get the current state for the partition. 627 * @return Current state of partition. 628 */ 629 public final State getCurrentState() 630 { 631 return m_currentState; 632 } 633 634 /** 635 * Get the current status for the partition. 636 * @return Current status of partition. 637 */ 638 public final Status getCurrentStatus() 639 { 640 return m_currentStatus; 641 } 642 643 /** 644 * Get the ReplicaNode instance for the given replica node index. 645 * @param idx Index into the ReplicaNode array. 646 * @return ReplicaNode instance. 647 * @exception IndexOutOfBoundsException 648 * The idx isn't valid. 649 */ 650 public final ReplicaNode getReplicaNode(int idx) 651 throws IndexOutOfBoundsException 652 { 653 // m_nodes[0] is active node, so we need to disallow -1 654 if (idx < 0) throw new IndexOutOfBoundsException(Integer.toString(idx)); 655 return new ReplicaNode(m_nodes[idx + 1], 656 PartitionManager.mapDEReplicationType( 657 m_replicationTypes[idx + 1])); 658 } 659 660 /** 661 * Get the ReplicaNode instance for the given replica node name. 662 * @param nodeName Name of replica node. 663 * @return ReplicaNode instance, or null if not found. 664 * @exception IllegalArgumentException The nodeName was invalid. 665 */ 666 public final ReplicaNode getReplicaNode(final String nodeName) 667 throws IllegalArgumentException 668 { 669 if (nodeName.isEmpty()) 670 { 671 throw new IllegalArgumentException("nodeName cannot be empty"); 672 } 673 for (int idx = 1; idx < m_nodes.length; idx++) 674 { 675 if (m_nodes[idx].equals(nodeName)) 676 { 677 return getReplicaNode(idx - 1); 678 } 679 } 680 return null; 681 } 682 683 /** 684 * Get the last time the state for the partition was updated 685 * @return Date of last state change for partition. 686 */ 687 public final Date getLastStateChangeTime() 688 { 689 return m_lastUpdated; 690 } 691 692 /** 693 * Get the properties currently defined for the partition. 694 * <p> 695 * The restoreFromNode property is not stored in the runtime since it 696 * has no meaning outside of the initial definePartition(). So this method 697 * will return a Properties instance with a null restoreFromNode value. 698 * 699 * @return Properties instance for partition. 700 */ 701 public final Properties getProperties() 702 { 703 return new Properties(m_forceReplication, m_objectChunks, 704 m_numThreads, m_sparseAudit, m_remoteEnableAction, m_broadcastUpdates); 705 } 706 707 /** 708 * Get the statistics for the partition. 709 * 710 * @return A Statistics instance for the partition. 711 */ 712 public final Statistics getStatistics() 713 { 714 DEPartitionManager pm = new DEPartitionManager(); 715 return new Statistics(pm.getPartitionStats(m_name)); 716 } 717 718 /** 719 * Clear the statistics for the partition. 720 */ 721 public final void clearStatistics() 722 { 723 DEPartitionManager pm = new DEPartitionManager(); 724 pm.clearPartitionStats(m_name); 725 } 726 727 /** 728 * Use {@link #update(Properties)} instead. 729 */ 730 @Deprecated 731 public final void update() throws NodeMismatch, NotActiveNode 732 { 733 update(null); 734 } 735 736 /** 737 * Update the partition. 738 * <p> 739 * This method is used to re-partition all instances that exist in a 740 * partition. For each instance in the partition, the {@link 741 * PartitionMapper} defined for that type is accessed, and the 742 * instance re-assigned to the partition that the {@link 743 * PartitionMapper#getPartition} method returns. The update() method 744 * must be called on the current active node for the partition. 745 * <p> 746 * To split a partition, the applications should create the new 747 * partition, install a new PartitionMapper for all types managed by 748 * the partition, and call the update() method for the existing 749 * partition. 750 * <p> 751 * To merge two or more partitions, the applications should install a 752 * new PartitionMapper for all types managed by the partition(s), and 753 * call the update() method for all the partitions that need to 754 * be merged. 755 * <p> 756 * It is important that all partitions returned when executing the 757 * PartitionMapper's getPartition() call for a type all contain 758 * identical node lists. If the partition returned contains a 759 * different node list than this partition, a NodeMismatch exception 760 * is thrown, and the update() terminates. To fix this, perform a 761 * migrate() on all partitions that will be split or merged to insure 762 * that they have identical node lists before performing an update(). 763 * 764 * @param partitionProperties Optional properties for the partition. 765 * If null, the default property values are used. 766 * 767 * @exception NodeMismatch 768 * The re-partitioning of instances was done using partitions that 769 * have different node lists. 770 * @exception NotActiveNode 771 * The current node is not the active node for the partition. 772 */ 773 public final void update(final Properties partitionProperties) 774 throws NodeMismatch, NotActiveNode 775 { 776 DEProperties props = copyDEProperties(partitionProperties); 777 778 PartitionManager.updatePartition(m_name, props); 779 } 780 781 /** 782 * Use {@link #migrate(Properties, String, ReplicaNode [])} instead. 783 * @param nodes An ordered list of nodes for the partition. 784 */ 785 @Deprecated 786 public final void migrate(String [] nodes) 787 throws NotActiveNode, IllegalArgumentException 788 { 789 PartitionManager.validateNodeList(nodes); 790 DEReplicationType [] drl = new DEReplicationType[nodes.length]; 791 for (int i = 0; i < drl.length; i++) 792 { 793 drl[i] = DEReplicationType.DataSynchronous; 794 } 795 796 DEProperties props = defaultDEProperties(); 797 798 PartitionManager.migratePartition(m_name, props, nodes, drl); 799 } 800 801 /** 802 * Migrate the partition. 803 * <p> 804 * This method is used to migrate all instances that exist in a 805 * partition. For each instance in the partition, the object is 806 * migrated as needed to all nodes in the replicas array, and to the 807 * new activeNode if it is different from the current active node. This 808 * method must be called on the currently active node for the partition. 809 * <p> 810 * Any properties passed in only apply to the current migrate() command, 811 * properties passed into definePartition() are saved and used during 812 * failover. 813 * 814 * @param partitionProperties Optional properties for the partition. 815 * If null, the default property values are used. 816 * @param activeNode Active node after migrate completes. 817 * @param replicas An ordered list of replica nodes for the partition. 818 * Should be passed in as a null instance or a zero length array if 819 * no replicas exists. 820 * 821 * @exception NotActiveNode 822 * The current node is not the active node for the partition. 823 * @exception IllegalArgumentException 824 * The activeNode or replica array was invalid. 825 */ 826 public final void migrate( 827 final Properties partitionProperties, 828 String activeNode, 829 ReplicaNode [] replicas) 830 throws NotActiveNode, IllegalArgumentException 831 { 832 PartitionManager.validateReplicaList(replicas); 833 834 int nodeLength = (replicas == null) ? 1 : replicas.length + 1; 835 String [] nodes = new String[nodeLength]; 836 837 nodes[0] = activeNode; 838 if (replicas != null ) 839 { 840 for (int i = 0; i < replicas.length; i++) 841 { 842 assert( i + 1 < nodes.length ); 843 nodes[i + 1] = replicas[i].nodeName; 844 } 845 } 846 PartitionManager.validateNodeList(nodes); 847 848 DEReplicationType [] drl = new DEReplicationType[nodes.length]; 849 drl[0] = DEReplicationType.DataSynchronous; 850 if (replicas != null) 851 { 852 for (int i = 0; i < replicas.length; i++) 853 { 854 assert( i + 1 < nodes.length ); 855 drl[i + 1] = PartitionManager.mapReplicationType( 856 replicas[i].replicationType); 857 } 858 } 859 boolean forceReplication = (partitionProperties != null) 860 ? partitionProperties.m_forceReplication : false; 861 862 DEProperties props = copyDEProperties(partitionProperties); 863 864 PartitionManager.migratePartition(m_name, props, nodes, drl); 865 } 866 867 /** 868 * Enable high availability for this partition. 869 * <p> 870 * This method is used to enable this partition. If the partition is 871 * already in the <b>ACTIVE</b> state, no action is taken. 872 * This method blocks until all required object migration 873 * is complete. 874 * 875 * @param enableAction The action to take when enabling partitions. 876 * 877 * @exception ResourceUnavailableException 878 * The minimum number of nodes needed to establish a quorum has not been 879 * seen, or the partition has not been defined. 880 * @exception NodeMismatch 881 * The partition does not have the local node in the node list, and the 882 * active node for the partition has a different node list. 883 * @see PartitionManager#enablePartitions(PartitionManager.EnableAction) 884 */ 885 public final void enable( 886 final PartitionManager.EnableAction enableAction) 887 throws NodeMismatch, ResourceUnavailableException 888 { 889 DEPartitionManager pm = new DEPartitionManager(); 890 try 891 { 892 pm.enablePartition(m_name, 893 PartitionManager.mapEnable(enableAction)); 894 } 895 catch (DENodeMismatch ex) 896 { 897 throw new NodeMismatch(ex.errMsg); 898 } 899 } 900 901 /** 902 * Disable high availability for this partition. 903 * <p> 904 * This method is used to remove the local node from the node list 905 * of this partition, and move the partition state to <b>UNAVAILABLE</b>. 906 * If called multiple times, the additional calls have no effect. 907 * <p> 908 * If the local node does not exist in the partition, no action is taken. 909 * 910 * @param disableAction The action to take when disabling partitions. 911 * 912 * @exception ResourceUnavailableException 913 * The partition would be in the <b>UNAVAILABLE</b> state after the 914 * disable executes, not thrown if <b>LEAVE_CLUSTER_FORCE</b> is used. 915 * @see PartitionManager#disablePartitions(PartitionManager.DisableAction) 916 */ 917 public final void disable( 918 final PartitionManager.DisableAction disableAction) 919 throws ResourceUnavailableException 920 { 921 DEPartitionManager pm = new DEPartitionManager(); 922 pm.disablePartition(m_name, PartitionManager.mapDisable(disableAction)); 923 } 924 925 /** 926 * Associated a notifier with the partition. 927 * <p> 928 * This method is used to associate a user defined {@link PartitionNotifier} 929 * instance with a partition. 930 * <p> 931 * PartitionNotifier instances are local to a node, this method 932 * should be executed on all nodes which need to determine if a 933 * partition state change occurs. 934 * <p> 935 * The same PartitionNotifier instance can be associated with multiple 936 * partitions. Multiple PartitionNotifier instances can be installed for 937 * a given Partition. When a state change occurs, all instances are 938 * executed, no order is guaranteed when executing the notifiers. 939 * To remove a notifier, the PartitionNotifier instance should be deleted. 940 * 941 * @param partitionNotifier User defined notifier instance. 942 * 943 */ 944 public final void setNotifier(PartitionNotifier partitionNotifier) 945 { 946 PartitionManager.setNotifier(m_name, partitionNotifier); 947 } 948 949 /** 950 * Returns the number of instances in this partition. 951 * <p> 952 * This method does not establish transaction locks on the 953 * partition or any of the individual objects in the partition. 954 * This means the number returned by cardinality() may 955 * change even as observed within a single transaction. 956 * <p> 957 * Warning: This method requires a scan of all objects for each class in 958 * the partition to determine the count, which is computationally 959 * expensive. 960 * 961 * @return Number of instances found. 962 */ 963 public final long cardinality() 964 { 965 DEPartitionManager pm = new DEPartitionManager(); 966 return pm.getCardinality(m_name); 967 } 968 969 /** The name of the partition */ 970 private final String m_name; 971 972 /** The list of nodes for a partition */ 973 private final String [] m_nodes; 974 975 /** The list of nodes for a partition */ 976 private final DEReplicationType [] m_replicationTypes; 977 978 /** The current state of the partition */ 979 private final State m_currentState; 980 981 /** The current status of the partition */ 982 private final Status m_currentStatus; 983 984 /** The time it was updated */ 985 private final Date m_lastUpdated; 986 987 /** stored property */ 988 private final boolean m_forceReplication; 989 990 /** stored property */ 991 private final long m_objectChunks; 992 993 /** stored property */ 994 private final long m_numThreads; 995 996 /** stored property */ 997 private final Properties.SparseAudit m_sparseAudit; 998 999 /** stored property */ 1000 private final Properties.RemoteEnableAction m_remoteEnableAction; 1001 1002 /** stored property */ 1003 private final DEPartitionBroadcast m_broadcastUpdates; 1004 1005 // 1006 // FIX THIS: Add stats attributes... 1007 // 1008 1009 // 1010 // Package private constructor 1011 // 1012 Partition( 1013 String name, 1014 String [] nodes, 1015 DEReplicationType [] replicationTypes, 1016 State currentState, 1017 Status currentStatus, 1018 Date lastUpdated, 1019 boolean forceReplication, 1020 long objectChunks, 1021 long numThreads, 1022 Properties.SparseAudit sparseAudit, 1023 Properties.RemoteEnableAction remoteEnableAction, 1024 DEPartitionBroadcast broadcastUpdates) 1025 { 1026 this.m_name = name; 1027 this.m_nodes = nodes; 1028 this.m_replicationTypes = replicationTypes; 1029 this.m_currentState = currentState; 1030 this.m_currentStatus = currentStatus; 1031 this.m_lastUpdated = lastUpdated; 1032 this.m_forceReplication = forceReplication; 1033 this.m_objectChunks = objectChunks; 1034 this.m_numThreads = numThreads; 1035 this.m_sparseAudit = sparseAudit; 1036 this.m_remoteEnableAction = remoteEnableAction; 1037 this.m_broadcastUpdates = broadcastUpdates; 1038 } 1039 // 1040 // Never should be called 1041 // 1042 private Partition() 1043 { 1044 this.m_name = null; 1045 this.m_nodes = null; 1046 this.m_replicationTypes = null; 1047 this.m_currentState = null; 1048 this.m_currentStatus = null; 1049 this.m_lastUpdated = null; 1050 this.m_forceReplication = false; 1051 this.m_objectChunks = 0; 1052 this.m_numThreads = 0; 1053 this.m_sparseAudit = null; 1054 this.m_remoteEnableAction = null; 1055 this.m_broadcastUpdates = DEPartitionBroadcast.BroadcastDefault; 1056 } 1057 1058 // 1059 // Copy properties to DEProperties. 1060 // 1061 static DEProperties copyDEProperties(Properties partitionProperties) 1062 { 1063 if (partitionProperties == null) 1064 { 1065 return defaultDEProperties(); 1066 } 1067 1068 DEProperties props = new DEProperties(); 1069 1070 props.forceReplication = partitionProperties.m_forceReplication; 1071 props.objectChunks = 1072 partitionProperties.m_objectsLockedPerTransaction; 1073 props.numThreads = partitionProperties.m_numberOfThreads; 1074 props.sparseAudit = 1075 PartitionManager.mapSparseAudit(partitionProperties.m_sparseAudit); 1076 props.remoteEnableAction = 1077 PartitionManager.mapRemoteEnableAction(partitionProperties.m_remoteEnableAction); 1078 props.restoreFromNode = partitionProperties.m_restoreFromNode; 1079 props.broadcastUpdates = partitionProperties.m_broadcastUpdates; 1080 1081 return props; 1082 } 1083 1084 // 1085 // Allocate and initialize default DEProperties. 1086 // 1087 static DEProperties defaultDEProperties() 1088 { 1089 DEProperties props = new DEProperties(); 1090 1091 // 1092 // Note: This needs to match the Properties() constructor. 1093 // 1094 props.forceReplication = false; 1095 props.objectChunks = DefaultObjectsLockedPerTransaction; 1096 props.numThreads = DefaultNumberOfThreads; 1097 props.sparseAudit = DESparsePartitionAudit.VerifyNodeList; 1098 props.remoteEnableAction = DERemoteEnableAction.EnablePartition; 1099 props.restoreFromNode = null; 1100 props.broadcastUpdates = DEPartitionBroadcast.BroadcastDefault; 1101 1102 return props; 1103 } 1104}