com.gemstone.gemfire.internal.cache.ColocationHelper Maven / Gradle / Ivy
Show all versions of gemfire-core Show documentation
/*
* 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.internal.cache;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import com.gemstone.gemfire.cache.EntryDestroyedException;
import com.gemstone.gemfire.cache.Region;
import com.gemstone.gemfire.cache.RegionDestroyedException;
import com.gemstone.gemfire.cache.execute.Function;
import com.gemstone.gemfire.cache.execute.FunctionService;
import com.gemstone.gemfire.distributed.internal.membership.InternalDistributedMember;
import com.gemstone.gemfire.i18n.LogWriterI18n;
import com.gemstone.gemfire.internal.Assert;
import com.gemstone.gemfire.internal.cache.execute.InternalRegionFunctionContext;
import com.gemstone.gemfire.internal.cache.partitioned.PRLocallyDestroyedException;
import com.gemstone.gemfire.internal.cache.persistence.PRPersistentConfig;
import com.gemstone.gemfire.internal.i18n.LocalizedStrings;
/**
* An utility class to retrieve colocated regions in a colocation hierarchy in
* various scenarios
*
* @author Yogesh Mahajan
* @author Kishor Bachhav
*
* @since 6.0
*/
public class ColocationHelper {
/**
* An utility method to retrieve colocated region name of a given partitioned
* region without waiting on initialize
*
* @param partitionedRegion
* @return colocated PartitionedRegion
* @since cheetah
*/
public static PartitionedRegion getColocatedRegionName(
final PartitionedRegion partitionedRegion) {
Assert.assertTrue(partitionedRegion != null); // precondition1
String colocatedWith = partitionedRegion.getPartitionAttributes().getColocatedWith();
if (colocatedWith == null) {
// the region is not colocated with any region
return null;
}
PartitionedRegion colocatedPR = partitionedRegion.getColocatedWithRegion();
if (colocatedPR != null && !colocatedPR.isLocallyDestroyed
&& !colocatedPR.isDestroyed()) {
return colocatedPR;
}
Region prRoot = PartitionedRegionHelper.getPRRoot(partitionedRegion
.getCache());
PartitionRegionConfig prConf = (PartitionRegionConfig)prRoot
.get(getRegionIdentifier(colocatedWith));
int prID = -1;
try {
if (prConf == null) {
colocatedPR = getColocatedPR(partitionedRegion, colocatedWith);
}
else {
prID = prConf.getPRId();
colocatedPR = PartitionedRegion.getPRFromId(prID);
if (colocatedPR == null && prID > 0) {
// colocatedPR might have not called registerPartitionedRegion() yet, but since prID is valid,
// we are able to get colocatedPR and do colocatedPR.waitOnBucketMetadataInitialization()
colocatedPR = getColocatedPR(partitionedRegion, colocatedWith);
}
}
}
catch (PRLocallyDestroyedException e) {
LogWriterI18n logger = partitionedRegion.getLogWriterI18n();
if (logger.fineEnabled()) {
logger.fine("PRLocallyDestroyedException : Region with prId=" + prID
+ " is locally destroyed on this node", e);
}
}
return colocatedPR;
}
private static PartitionedRegion getColocatedPR(
final PartitionedRegion partitionedRegion, final String colocatedWith) {
LogWriterI18n logger = partitionedRegion.getLogWriterI18n();
logger.info(
LocalizedStrings.HOPLOG_0_COLOCATE_WITH_REGION_1_NOT_INITIALIZED_YET,
new Object[] { partitionedRegion.getFullPath(), colocatedWith });
PartitionedRegion colocatedPR = (PartitionedRegion) partitionedRegion
.getCache().getPartitionedRegion(colocatedWith, false);
assert colocatedPR != null;
return colocatedPR;
}
/**
* An utility method to retrieve colocated region of a given partitioned
* region
*
* @param partitionedRegion
* @return colocated PartitionedRegion
* @since 5.8Beta
*/
public static PartitionedRegion getColocatedRegion(
final PartitionedRegion partitionedRegion) {
Assert.assertTrue(partitionedRegion != null); // precondition1
String colocatedWith = partitionedRegion.getPartitionAttributes()
.getColocatedWith();
if (colocatedWith == null) {
// the region is not colocated with any region
return null;
}
int prID = -1;
PartitionedRegion colocatedPR = partitionedRegion.getColocatedWithRegion();
try {
if (colocatedPR != null && !colocatedPR.isLocallyDestroyed
&& !colocatedPR.isDestroyed()) {
colocatedPR.waitOnBucketMetadataInitialization();
return colocatedPR;
}
Region prRoot = PartitionedRegionHelper.getPRRoot(partitionedRegion
.getCache());
PartitionRegionConfig prConf = (PartitionRegionConfig)prRoot
.get(getRegionIdentifier(colocatedWith));
prID = prConf.getPRId();
colocatedPR = PartitionedRegion.getPRFromId(prID);
colocatedPR.waitOnBucketMetadataInitialization();
} catch (PRLocallyDestroyedException e) {
LogWriterI18n logger = partitionedRegion.getLogWriterI18n();
if (logger.fineEnabled()) {
if (prID == -1 && colocatedPR != null) {
prID = colocatedPR.getPRId();
}
logger.fine("PRLocallyDestroyedException : Region with prId=" + prID
+ " is locally destroyed on this node", e);
}
}
return colocatedPR;
}
/**
* An utility to make sure that a member contains all of the partitioned
* regions that are colocated with a given region on other members.
* TODO rebalance - this is rather inefficient, and probably all this junk should
* be in the advisor.
*/
public static boolean checkMembersColocation(PartitionedRegion partitionedRegion, InternalDistributedMember member) {
List colocatedRegions = new ArrayList();
List tempcolocatedRegions = new ArrayList();
Region prRoot = PartitionedRegionHelper.getPRRoot(partitionedRegion
.getCache());
PartitionRegionConfig regionConfig =(PartitionRegionConfig) prRoot.get(partitionedRegion.getRegionIdentifier());
//The region was probably concurrently destroyed
if(regionConfig == null) {
return false;
}
tempcolocatedRegions.add(regionConfig);
colocatedRegions.addAll(tempcolocatedRegions);
PartitionRegionConfig prConf = null;
do {
PartitionRegionConfig tempToBeColocatedWith = tempcolocatedRegions
.remove(0);
for (Iterator itr = prRoot.keySet().iterator(); itr.hasNext();) {
String prName = (String)itr.next();
try {
prConf = (PartitionRegionConfig)prRoot.get(prName);
}
catch (EntryDestroyedException ede) {
continue;
}
if (prConf == null) {
// darrel says: I'm seeing an NPE in this code after pr->rem
// merge so I added this check and continue
continue;
}
if (prConf.getColocatedWith() != null) {
if (prConf.getColocatedWith().equals(
tempToBeColocatedWith.getFullPath())
|| ("/" + prConf.getColocatedWith())
.equals(tempToBeColocatedWith.getFullPath())) {
colocatedRegions.add(prConf);
tempcolocatedRegions.add(prConf);
}
}
}
} while (!tempcolocatedRegions.isEmpty());
PartitionRegionConfig tempColocatedWith = regionConfig;
prConf = null;
while (true) {
String colocatedWithRegionName = tempColocatedWith.getColocatedWith();
if (colocatedWithRegionName == null)
break;
else {
try {
prConf = (PartitionRegionConfig)prRoot
.get(getRegionIdentifier(colocatedWithRegionName));
}
catch (EntryDestroyedException ede) {
throw ede;
}
if (prConf == null) {
break;
}
colocatedRegions.add(tempColocatedWith);
tempColocatedWith = prConf;
}
}
//Now check to make sure that all of the colocated regions
//Have this member.
//We don't need a hostname because the equals method doesn't check it.
for(PartitionRegionConfig config: colocatedRegions) {
if(config.isColocationComplete() && !config.containsMember(member)) {
return false;
}
}
//Check to make sure all of the persisted regions that are colocated
//with this region have been created.
if(hasOfflineColocatedChildRegions(partitionedRegion)) {
return false;
}
return true;
}
/**
* Returns true if there are regions that are persisted on this member and
* were previously colocated with the given region, but have not yet been created.
*
* @param region The parent region
* @return true if there are any child regions that are persisted on this
* member, but have not yet been created.
*/
private static boolean hasOfflineColocatedChildRegions(PartitionedRegion region) {
int oldLevel = LocalRegion.setThreadInitLevelRequirement(LocalRegion.ANY_INIT);
try {
GemFireCacheImpl cache = region.getCache();
Collection stores = cache.listDiskStores();
//Look through all of the disk stores for offline colocated child regions
for(DiskStoreImpl diskStore: stores) {
//Look at all of the partitioned regions.
for(Map.Entry entry : diskStore.getAllPRs().entrySet()) {
PRPersistentConfig config = entry.getValue();
String childName = entry.getKey();
//Check to see if they're colocated with this region.
if(region.getFullPath().equals(config.getColocatedWith())) {
PartitionedRegion childRegion = (PartitionedRegion) cache.getRegion(childName);
if(childRegion == null || !childRegion.isAfterBucketMetadataSetup()) {
//If the child region is offline, return true
return true;
} else {
//Otherwise, look for offline children of that region.
if(hasOfflineColocatedChildRegions(childRegion)) {
return true;
}
}
}
}
}
} finally {
LocalRegion.setThreadInitLevelRequirement(oldLevel);
}
return false;
}
/** A utility to check to see if a region has been created on
* all of the VMs that host the regions this region is colocated with.
*/
public static boolean isColocationComplete(PartitionedRegion region) {
Region prRoot = PartitionedRegionHelper.getPRRoot(region
.getCache());
PartitionRegionConfig config = (PartitionRegionConfig) prRoot.get(region.getRegionIdentifier());
//Fix for bug 40075. There is race between this call and the region being concurrently
//destroyed.
if(config == null) {
Assert.assertTrue(region.isDestroyed(), "Region is not destroyed, but there is no entry in the prRoot for region " + region);
return false;
}
return config.isColocationComplete();
}
/**
* An utility method to retrieve all partitioned regions(excluding self) in a
* colocation chain
*
* For example, shipmentPR is colocated with orderPR and orderPR is colocated
* with customerPR
*
* getAllColocationRegions(customerPR) --> List{orderPR, shipmentPR}
* getAllColocationRegions(orderPR) --> List{customerPR, shipmentPR}
* getAllColocationRegions(shipmentPR) --> List{customerPR, orderPR}
*
* @param partitionedRegion
* @return List of all partitioned regions (excluding self) in a colocated
* chain
* @since 5.8Beta
*/
public static Map getAllColocationRegions(
PartitionedRegion partitionedRegion) {
Map colocatedRegions = new HashMap();
List colocatedByRegion = partitionedRegion.getColocatedByList();
if (colocatedByRegion.size() != 0) {
List tempcolocatedRegions = new ArrayList();
tempcolocatedRegions.addAll(colocatedByRegion);
do {
PartitionedRegion pRegion = tempcolocatedRegions.remove(0);
pRegion.waitOnBucketMetadataInitialization();
colocatedRegions.put(pRegion.getFullPath(), pRegion);
tempcolocatedRegions.addAll(pRegion.getColocatedByList());
} while (!tempcolocatedRegions.isEmpty());
}
PartitionedRegion tempColocatedWith = partitionedRegion;
while (true) {
PartitionedRegion colocatedWithRegion = tempColocatedWith
.getColocatedWithRegion();
if (colocatedWithRegion == null)
break;
else {
colocatedRegions.put(colocatedWithRegion.getFullPath(),
colocatedWithRegion);
tempColocatedWith = colocatedWithRegion;
}
}
return colocatedRegions;
}
/**
* gets local data of colocated regions on a particular data store
*
* @param partitionedRegion
* @return map of region name to local colocated regions
* @since 5.8Beta
*/
public static Map getAllColocatedLocalDataSets(
PartitionedRegion partitionedRegion, InternalRegionFunctionContext context) {
Map colocatedRegions = getAllColocationRegions(partitionedRegion);
Map colocatedLocalRegions = new HashMap();
for (Iterator itr = colocatedRegions.entrySet().iterator(); itr.hasNext();) {
Map.Entry me = (Entry)itr.next();
final Region pr = (Region) me.getValue();
colocatedLocalRegions.put((String)me.getKey(), context.getLocalDataSet(pr));
}
return colocatedLocalRegions;
}
public static Map constructAndGetAllColocatedLocalDataSet(
PartitionedRegion region, Set bucketSet, TXStateInterface tx) {
Map colocatedLocalDataSets = new HashMap();
if (region.getColocatedWith() == null && (!region.isColocatedBy())) {
colocatedLocalDataSets.put(region.getFullPath(), new LocalDataSet(region,
bucketSet, tx));
return colocatedLocalDataSets;
}
Map colocatedRegions = ColocationHelper
.getAllColocationRegions(region);
for (Region colocatedRegion : colocatedRegions.values()) {
colocatedLocalDataSets.put(colocatedRegion.getFullPath(),
new LocalDataSet((PartitionedRegion)colocatedRegion, bucketSet, tx));
}
colocatedLocalDataSets.put(region.getFullPath(), new LocalDataSet(region,
bucketSet, tx));
return colocatedLocalDataSets;
}
public static Map getColocatedLocalDataSetsForBuckets(
PartitionedRegion region, Set bucketSet, TXStateInterface tx) {
if (region.getColocatedWith() == null && (!region.isColocatedBy())) {
return Collections.emptyMap();
}
Map ret = new HashMap();
Map colocatedRegions = ColocationHelper
.getAllColocationRegions(region);
for (Region colocatedRegion : colocatedRegions.values()) {
ret.put(colocatedRegion.getFullPath(), new LocalDataSet(
(PartitionedRegion)colocatedRegion, bucketSet, tx));
}
return ret;
}
/**
* A utility method to retrieve all child partitioned regions that are
* directly colocated to the specified partitioned region.
*
* For example, shipmentPR is colocated with orderPR and orderPR is colocated
* with customerPR.
* getColocatedChildRegions(customerPR) will return List{orderPR}
* getColocatedChildRegions(orderPR) will return List{shipmentPR}
* getColocatedChildRegions(shipmentPR) will return empty List{}
*
* @param partitionedRegion
* @return list of all child partitioned regions colocated with the region
* @since 5.8Beta
*/
public static List getColocatedChildRegions(
PartitionedRegion partitionedRegion) {
List colocatedChildRegions = new ArrayList();
Region prRoot = PartitionedRegionHelper.getPRRoot(partitionedRegion
.getCache());
PartitionRegionConfig prConf = null;
// final List allPRNamesList = new ArrayList(prRoot.keySet());
Iterator itr = prRoot.keySet().iterator();
while ( itr.hasNext()) {
try {
String prName = (String)itr.next();
/*if (prName == prName) {
continue;
}*/
try {
prConf = (PartitionRegionConfig)prRoot.get(prName);
}
catch (EntryDestroyedException ede) {
continue;
}
if (prConf == null) {
// darrel says: I'm seeing an NPE in this code after pr->rem
// merge so I added this check and continue
continue;
}
int prID = prConf.getPRId();
PartitionedRegion prRegion = PartitionedRegion.getPRFromId(prID);
if (prRegion != null) {
if (prRegion.getColocatedWith() != null) {
if (prRegion.getColocatedWith().equals(
partitionedRegion.getFullPath())
|| ("/" + prRegion.getColocatedWith()).equals(partitionedRegion
.getFullPath())) {
// only regions directly colocatedWith partitionedRegion are
// added to the list...
prRegion.waitOnBucketMetadataInitialization();
colocatedChildRegions.add(prRegion);
}
}
}
}
catch (PRLocallyDestroyedException e) {
LogWriterI18n logger = partitionedRegion.getLogWriterI18n();
if (logger.fineEnabled()) {
logger.fine("PRLocallyDestroyedException : Region ="
+ prConf.getPRId() + " is locally destroyed on this node", e);
}
}
catch (RegionDestroyedException e) {
LogWriterI18n logger = partitionedRegion.getLogWriterI18n();
if (logger.fineEnabled()) {
logger.fine("RegionDestroyedException : Region ="
+ prConf.getPRId() + " is destroyed.", e);
}
}
}
//Fix for 44484 - Make the list of colocated child regions
//is always in the same order on all nodes.
Collections.sort(colocatedChildRegions, new Comparator() {
@Override
public int compare(PartitionedRegion o1, PartitionedRegion o2) {
if(o1.isShadowPR() == o2.isShadowPR()) {
return o1.getFullPath().compareTo(o2.getFullPath());
}
if(o1.isShadowPR()) {
return 1;
}
return -1;
}
});
return colocatedChildRegions;
}
//TODO why do we have this method here?
public static Function getFunctionInstance(Serializable function) {
Function functionInstance = null;
if (function instanceof String) {
functionInstance = FunctionService.getFunction((String)function);
Assert.assertTrue(functionInstance != null, "Function " + function
+ " is not registered on this node ");
}
else {
functionInstance = (Function)function;
}
return functionInstance;
}
public static PartitionedRegion getLeaderRegion(PartitionedRegion prRegion) {
PartitionedRegion parentRegion;
while((parentRegion = getColocatedRegion(prRegion)) != null) {
prRegion = parentRegion;
}
return prRegion;
}
// Gemfirexd will skip initialization for PR, so just get region name without waitOnInitialize
public static PartitionedRegion getLeaderRegionName(PartitionedRegion prRegion) {
PartitionedRegion parentRegion;
while((parentRegion = getColocatedRegionName(prRegion)) != null) {
prRegion = parentRegion;
}
return prRegion;
}
private static String getRegionIdentifier(String regionName) {
if (regionName.startsWith("/")) {
return regionName.replace("/", "#");
}
else {
return "#" + regionName.replace("/", "#");
}
}
/**
* Test to see if there are any persistent child regions
* of a partitioned region.
*/
public static boolean hasPersistentChildRegion(
PartitionedRegion region) {
for(PartitionedRegion child : getColocatedChildRegions(region)) {
if(child.getDataPolicy().withPersistence()) {
return true;
}
}
return false;
}
}