All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.gemstone.gemfire.cache.partition.PartitionManager Maven / Gradle / Ivy

There is a newer version: 2.0-BETA
Show newest version
/*
 * Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License. You
 * may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License. See accompanying
 * LICENSE file.
 */
package com.gemstone.gemfire.cache.partition;

import java.util.List;
import java.util.Set;

import com.gemstone.gemfire.cache.DataPolicy;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.control.RebalanceFactory;
import com.gemstone.gemfire.distributed.DistributedMember;
import com.gemstone.gemfire.distributed.internal.membership.InternalDistributedMember;
import com.gemstone.gemfire.i18n.LogWriterI18n;
import com.gemstone.gemfire.internal.cache.FixedPartitionAttributesImpl;
import com.gemstone.gemfire.internal.cache.PartitionedRegion;
import com.gemstone.gemfire.internal.cache.PartitionedRegionDataStore;
import com.gemstone.gemfire.internal.cache.PartitionedRegion.RecoveryLock;
import com.gemstone.gemfire.internal.cache.partitioned.RemoveBucketMessage;
import com.gemstone.gemfire.internal.cache.partitioned.RemoveBucketMessage.RemoveBucketResponse;
import com.gemstone.gemfire.internal.i18n.LocalizedStrings;

/**
 * An utility class to manage partitions (aka buckets) on a Partitioned Region
 * without requiring data (e.g. keys).
 * 
 * Note : Please contact [email protected] before using these APIs.
 * 
 * 
* This is an example of how this API can be used to create a view * region with redundancy 0, which is colocated with another region of * redundancy 1, without using the standard gemfire colocated-with attribute, * which only supports colocated regions with the same redundancy level. * * Note that when using this API, the data in the view region is discarded every * time a primary moves. * *
 * public class ColocatingPartitionListener implements PartitionListener,
 *     Declarable {
 *   private Cache cache;
 * 
 *   private List<String> viewRegionNames = new ArrayList<String>();
 * 
 *   public ColocatingPartitionListener() {
 *   }
 * 
 *   public void afterPrimary(int bucketId) { 
 * 
 *     for (String viewRegionName : viewRegionNames) {
 *       Region viewRegion = cache.getRegion(viewRegionName);
 *       PartitionManager.createPrimaryBucket(viewRegion, bucketId, true, true);
 *     }
 *   }
 * 
 *   public void init(Properties props) {
 *     String viewRegions = props.getProperty("viewRegions");
 *     StringTokenizer tokenizer = new StringTokenizer(viewRegions, ",");
 *     while (tokenizer.hasMoreTokens()) {
 *       viewRegionNames.add(tokenizer.nextToken());
 *     }
 *   }
 * 
 *   public void afterRegionCreate(Region<?, ?> region) {
 *     cache = region.getCache();
 *   }
 * }
 * 
* * In the declaration of the parent region in cache.xml, install * the ColocatedPartitionListener as follows :
* *
 * <partition-attributes redundant-copies="1">
 *     <partition-listener>
 *         <class-name>com.myCompany.ColocatingPartitionListener</class-name>
 *          <parameter name="viewRegions">
 *              <string>/customer/ViewA,/customer/ViewB</string>
 *          </parameter>             
 *     </partition-listener>
 * </partition-attributes>
 * 
* * If the regions needs to be rebalanced, use the {@link RebalanceFactory#excludeRegions(Set)} * method to exclude the view regions. * * * @author Yogesh Mahajan * * @since 6.5 */ public final class PartitionManager { private PartitionManager() { } /** * This method creates a copy of the bucket on the current node, if no * copy already exists. Depending on the values of destroyExistingLocal * and destroyExistingRemote, it will first destroy the existing primary * copy of the bucket. * * This behavior of this method is undefined on partitioned regions with * redundancy greater than 0. The behavior is also undefined for partitioned * regions colocated with the colocated-with attribute. * * This method creates primary bucket in the following way: * *

* If the partitioned region does not have a primary bucket for the bucketId, * it creates a primary bucket on the member and returns true. * *

* If the partitioned region does have a primary bucket for the bucketId on * the member :
* a) If destroyExistingLocal passed is true, it destroys the existing bucket, * and then creates a new primary bucket and returns true.
* b) If destroyExistingLocal passed is false, it does nothing and returns * false. * *

* If the partitioned region does have a primary bucket for the bucketId on * remote members :
* a) If destroyExistingRemote passed is true, it destroys the existing bucket * on remote member, and then creates a new primary bucket on this member and * returns true.
* b) If destroyExistingRemote passed is false, it throws * IllegalStateException. * * * @param region * the partitioned region on which to create the bucket * @param bucketId * the identifier of the bucket to create * @param destroyExistingRemote * whether to destroy the remote bucket if it exists * @param destroyExistingLocal * whether to destroy the local bucket if it exists * @return true when the bucket has been created otherwise false * @throws IllegalArgumentException * if the provided region is not a partitioned region * @throws IllegalArgumentException * if the provided bucketId is less than zero or greater than or * equal to the partitioned region's total number of buckets * @throws IllegalStateException * if the partitioned region has the primary bucket for the bucket * id on a remote member and the destroyExistingRemote parameter * provided is false */ public static boolean createPrimaryBucket(final Region region, final int bucketId, final boolean destroyExistingRemote, final boolean destroyExistingLocal) { final PartitionedRegion pr = prCheck(region, bucketId); boolean createdBucket = false; final LogWriterI18n logger = pr.getLogWriterI18n(); //Getting this lock prevents rebalancing while this bucket is created. //It will also prevent conflicts with other members in createPrimaryBucket final RecoveryLock recovLock = pr.getRecoveryLock(); recovLock.lock(); try { DistributedMember primary = PartitionManager.getPrimaryMemberForBucket(pr, bucketId); InternalDistributedMember self = (InternalDistributedMember)pr.getCache() .getDistributedSystem().getDistributedMember(); if (primary == null) { if(logger.fineEnabled()) { logger.fine("createPrimaryBucket: " + pr + ", bucket " + bucketId + " no existing primary, creating primary"); } createdBucket = createBucket(self, pr, bucketId, destroyExistingRemote); } else if (self.equals(primary)) { if (destroyExistingLocal) { logger.fine("createPrimaryBucket: " + pr + ", bucket " + bucketId + " already primary, destroying local primary"); if (dumpBucket(self, region, bucketId)) { createdBucket = createBucket(self, region, bucketId, destroyExistingRemote); } } else { if(logger.fineEnabled()) { logger.fine("createPrimaryBucket: " + pr + ", bucket " + bucketId + " already primary, no action needed"); } } } else { if (destroyExistingRemote) { if(logger.fineEnabled()) { logger.fine("createPrimaryBucket: " + pr + ", bucket " + bucketId + ", " + primary + " is primary, destroying it"); } if (dumpBucket(primary, region, bucketId)) { createdBucket = createBucket(self, region, bucketId, destroyExistingRemote); } } else { Object[] params = new Object[] { self, primary }; throw new IllegalStateException( LocalizedStrings.PartitionManager_BUCKET_CANNOT_BE_MOVED_AS_DESTROYEXISTING_IS_FALSE .toLocalizedString(params)); } } } finally { recovLock.unlock(); } return createdBucket; } /** * Get the current primary owner for a bucket. If the bucket is not yet * created it returns null immediately * * @throws IllegalStateException * if the provided region is something other than a * {@linkplain DataPolicy#PARTITION partitioned Region} * @return the primary member for the bucket, possibly null if a primary is * not yet determined * @since 6.5 * @param region * @param bucketId */ private static DistributedMember getPrimaryMemberForBucket( final Region region, final int bucketId) { PartitionedRegion pr = prCheck(region, bucketId); if (pr.getRegionAdvisor().isStorageAssignedForBucket(bucketId)) { return pr.getBucketPrimary(bucketId); } else { return null; } } private static boolean createBucket(final DistributedMember target, final Region region, final int bucketId, final boolean destroyExistingRemote) { final PartitionedRegion pr = prCheck(region, bucketId); final LogWriterI18n logger = pr.getLogWriterI18n(); InternalDistributedMember createTarget = dataStoreCheck(target, pr); //This is a bit of a hack. If we don't have a source for a bucket move, //createBackupBucketOnMember isn't going to let us exceed redundancy. InternalDistributedMember moveSource = createTarget; boolean createdBucket = pr.getRedundancyProvider().createBackupBucketOnMember( bucketId, createTarget, false, false, moveSource, true); Set currentBucketOwners = pr.getRegionAdvisor() .getBucketOwners(bucketId); boolean includesTarget = currentBucketOwners.remove(target); //Race condition. Someone else may have created the bucket while we were //trying to create it. If they got in first, we could have exceeded redundancy //Based on the flags, we need to get back down to redundancy. //Note that nobody else is going to create the bucket now, because //redundancy is already satisfied. if(currentBucketOwners.size() > pr.getRedundantCopies() && includesTarget) { InternalDistributedMember remoteBucket = currentBucketOwners.iterator().next(); if(destroyExistingRemote) { if(logger.fineEnabled()) { logger.fine("createPrimaryBucket: " + pr + ", bucket " + bucketId + " Redundancy is exceeded due to concurrent bucket create, destroying remote bucket: " + remoteBucket); } dumpBucket(remoteBucket, region, bucketId); } else { if(logger.fineEnabled()) { logger.fine("createPrimaryBucket: " + pr + ", bucket " + bucketId + " Redundancy is exceeded due to concurrent bucket create, destroying my bucket"); } dumpBucket(target, region, bucketId); Object[] params = new Object[] { target, remoteBucket }; throw new IllegalStateException( LocalizedStrings.PartitionManager_BUCKET_CANNOT_BE_MOVED_AS_DESTROYEXISTING_IS_FALSE .toLocalizedString(params)); } } return createdBucket; } private static boolean dumpBucket(final DistributedMember source, final Region region, final int bucketId) { final PartitionedRegion pr = prCheck(region, bucketId); InternalDistributedMember bucketSource = dataStoreCheck(source, pr); Set currentBucketOwners = pr.getRegionAdvisor() .getBucketOwners(bucketId); if (!currentBucketOwners.contains(bucketSource)) { Object[] params = new Object[] { bucketSource, Integer.valueOf(bucketId) }; throw new IllegalArgumentException( LocalizedStrings.PartitionManager_SOURCE_MEMBER_0_BUCKETID_1_DOES_NOT_HAVE_THE_BUCKET .toLocalizedString(params)); } InternalDistributedMember self = (InternalDistributedMember)pr.getCache() .getDistributedSystem().getDistributedMember(); if (bucketSource.equals(self)) { PartitionedRegionDataStore dataStore = pr.getDataStore(); return dataStore.removeBucket(bucketId, true); } else { RemoveBucketResponse response = RemoveBucketMessage.send(bucketSource, pr, bucketId, true); if (response != null) { return response.waitForResponse(); } } return false; } private static PartitionedRegion prCheck(final Region region, final int bucketId) { if (region == null) { throw new IllegalArgumentException( LocalizedStrings.PartitionRegionHelper_ARGUMENT_REGION_IS_NULL .toString()); } if (!(region instanceof PartitionedRegion)) { throw new IllegalArgumentException( LocalizedStrings.PartitionManager_REGION_0_IS_NOT_A_PARTITIONED_REGION .toLocalizedString(region.getFullPath())); } final PartitionedRegion pr = (PartitionedRegion)region; if (bucketId < 0 || bucketId >= pr.getTotalNumberOfBuckets()) { throw new IllegalArgumentException( LocalizedStrings.PartitionManager_BUCKETID_ARG0_RANGE_0_TO_ARG1_PR_ARG2 .toLocalizedString(new Object[] { Integer.valueOf(bucketId), Integer.valueOf((pr.getTotalNumberOfBuckets() - 1)), pr.getFullPath() })); } if (pr.isFixedPartitionedRegion()) { boolean containsBucket = false; List fpas = pr .getFixedPartitionAttributesImpl(); for (FixedPartitionAttributesImpl fpa : fpas) { if (fpa.isPrimary() && (fpa.hasBucket(bucketId))) { containsBucket = true; break; } } if (!containsBucket) throw new IllegalArgumentException( LocalizedStrings.FixedPartitionManager_BUCKETID_ARG_PR_ARG2 .toLocalizedString(new Object[] { Integer.valueOf(bucketId), pr.getFullPath() })); } return pr; } private static InternalDistributedMember dataStoreCheck( final DistributedMember target, final PartitionedRegion pr) { final InternalDistributedMember idmTarget = (InternalDistributedMember)target; final boolean localIsDatastore = pr.isDataStore() && pr.getCache().getDistributedSystem().getDistributedMember().equals( target); if (!(localIsDatastore || pr.getRegionAdvisor().adviseDataStore().contains( idmTarget))) { throw new IllegalArgumentException( LocalizedStrings.PartitionManager_PROVIDED_MEMBER_0_NO_PR_OR_NO_DATASTORE .toLocalizedString(target)); } return idmTarget; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy