blob: 02c85514a3b43e3cb4ef3cb62881b088adc687c9 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license
* agreements. See the NOTICE file distributed with this work for additional information regarding
* copyright ownership. The ASF licenses this file to You 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.
*/
package org.apache.geode.internal.cache.partitioned;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.logging.log4j.Logger;
import org.apache.geode.DataSerializable;
import org.apache.geode.DataSerializer;
import org.apache.geode.annotations.Immutable;
import org.apache.geode.cache.InterestPolicy;
import org.apache.geode.cache.LowMemoryException;
import org.apache.geode.cache.Region;
import org.apache.geode.distributed.DistributedMember;
import org.apache.geode.distributed.internal.DistributionConfig;
import org.apache.geode.distributed.internal.ProfileListener;
import org.apache.geode.distributed.internal.membership.InternalDistributedMember;
import org.apache.geode.internal.Assert;
import org.apache.geode.internal.InternalDataSerializer;
import org.apache.geode.internal.cache.BucketAdvisor;
import org.apache.geode.internal.cache.BucketAdvisor.BucketProfile;
import org.apache.geode.internal.cache.BucketAdvisor.ServerBucketProfile;
import org.apache.geode.internal.cache.BucketPersistenceAdvisor;
import org.apache.geode.internal.cache.BucketRegion;
import org.apache.geode.internal.cache.BucketServerLocation66;
import org.apache.geode.internal.cache.CacheDistributionAdvisor;
import org.apache.geode.internal.cache.FixedPartitionAttributesImpl;
import org.apache.geode.internal.cache.InternalRegionArguments;
import org.apache.geode.internal.cache.PartitionedRegion;
import org.apache.geode.internal.cache.PartitionedRegionStats;
import org.apache.geode.internal.cache.ProxyBucketRegion;
import org.apache.geode.internal.cache.control.MemoryThresholds;
import org.apache.geode.internal.cache.control.ResourceAdvisor;
import org.apache.geode.internal.logging.LogService;
import org.apache.geode.internal.logging.log4j.LogMarker;
public class RegionAdvisor extends CacheDistributionAdvisor {
private static final Logger logger = LogService.getLogger();
/**
* Number of threads allowed to concurrently volunteer for bucket primary.
*/
public static final short VOLUNTEERING_THREAD_COUNT = Integer
.getInteger(DistributionConfig.GEMFIRE_PREFIX + "RegionAdvisor.volunteeringThreadCount", 1)
.shortValue();
/**
* Non-thread safe queue for volunteering for primary bucket. Each BucketAdvisor for this PR uses
* this queue. The thread that uses this queue is a waiting pool thread. Any thread using this
* queue must synchronize on this queue.
*/
private final Queue<Runnable> volunteeringQueue = new ConcurrentLinkedQueue<>();
/**
* Semaphore with {@link #VOLUNTEERING_THREAD_COUNT} number of permits to control number of
* threads volunteering for bucket primaries.
*/
private final Semaphore volunteeringSemaphore = new Semaphore(VOLUNTEERING_THREAD_COUNT);
private volatile int lastActiveProfiles = 0;
private volatile int numDataStores = 0;
protected volatile ProxyBucketRegion[] buckets;
private Queue<QueuedBucketProfile> preInitQueue;
private final Object preInitQueueMonitor = new Object();
private ConcurrentHashMap<Integer, Set<ServerBucketProfile>> clientBucketProfilesMap;
private RegionAdvisor(PartitionedRegion region) {
super(region);
synchronized (preInitQueueMonitor) {
preInitQueue = new ConcurrentLinkedQueue<>();
}
clientBucketProfilesMap = new ConcurrentHashMap<>();
}
public static RegionAdvisor createRegionAdvisor(PartitionedRegion region) {
RegionAdvisor advisor = new RegionAdvisor(region);
advisor.initialize();
return advisor;
}
public PartitionedRegionStats getPartitionedRegionStats() {
return getPartitionedRegion().getPrStats();
}
public synchronized void initializeRegionAdvisor() {
if (buckets != null) {
return;
}
PartitionedRegion p = getPartitionedRegion();
int numBuckets = p.getAttributes().getPartitionAttributes().getTotalNumBuckets();
ProxyBucketRegion[] bucs = new ProxyBucketRegion[numBuckets];
InternalRegionArguments args = new InternalRegionArguments();
args.setPartitionedRegionAdvisor(this);
for (int i = 0; i < bucs.length; i++) {
bucs[i] = new ProxyBucketRegion(i, p, args);
bucs[i].initialize();
}
buckets = bucs;
}
/**
* Process those profiles which were received during the initialization period. It is safe to
* process these profiles potentially out of order due to the profiles version which is
* established on the sender.
*/
public void processProfilesQueuedDuringInitialization() {
synchronized (preInitQueueMonitor) {
Iterator pi = preInitQueue.iterator();
boolean finishedInitQueue = false;
try {
while (pi.hasNext()) {
Object o = pi.next();
QueuedBucketProfile qbp = (QueuedBucketProfile) o;
if (!qbp.isRemoval) {
if (logger.isTraceEnabled(LogMarker.DISTRIBUTION_ADVISOR_VERBOSE)) {
logger.trace(LogMarker.DISTRIBUTION_ADVISOR_VERBOSE,
"applying queued profile addition for bucket {}", qbp.bucketId);
}
getBucket(qbp.bucketId).getBucketAdvisor().putProfile(qbp.bucketProfile);
} else if (qbp.memberDeparted
|| !getDistributionManager().isCurrentMember(qbp.memberId)) {
boolean crashed;
if (qbp.memberDeparted) {
crashed = qbp.crashed;
} else {
// TODO not necessarily accurate, but how important is this?
crashed = !stillInView(qbp.memberId);
}
if (logger.isTraceEnabled(LogMarker.DISTRIBUTION_ADVISOR_VERBOSE)) {
logger.trace(LogMarker.DISTRIBUTION_ADVISOR_VERBOSE,
"applying queued member departure for all buckets for {}", qbp.memberId);
}
for (ProxyBucketRegion bucket : buckets) {
BucketAdvisor ba = bucket.getBucketAdvisor();
ba.removeId(qbp.memberId, crashed, qbp.destroyed, qbp.fromMembershipListener);
} // for
} else { // apply removal for member still in the view
if (logger.isTraceEnabled(LogMarker.DISTRIBUTION_ADVISOR_VERBOSE)) {
logger.trace(LogMarker.DISTRIBUTION_ADVISOR_VERBOSE,
"applying queued profile removal for all buckets for {}", qbp.memberId);
}
for (int i = 0; i < buckets.length; i++) {
BucketAdvisor ba = buckets[i].getBucketAdvisor();
int serial = qbp.serials[i];
if (serial != ILLEGAL_SERIAL) {
ba.removeIdWithSerial(qbp.memberId, serial, qbp.destroyed);
}
} // for
} // apply removal for member still in the view
} // while
finishedInitQueue = true;
} finally {
preInitQueue = null; // prevent further additions to the queue
preInitQueueMonitor.notifyAll();
if (!finishedInitQueue && !getAdvisee().getCancelCriterion().isCancelInProgress()) {
logger.error("Failed to process all queued BucketProfiles for {}",
getAdvisee());
}
}
}
}
@Override
protected Profile instantiateProfile(InternalDistributedMember memberId, int version) {
return new PartitionProfile(memberId, version);
}
/**
* Returns the {@link #volunteeringQueue} used to queue primary volunteering tasks by this PR's
* BucketAdvisors.
*
* @return the volunteering queue for use by this PR's BucketAdvisors
*/
public Queue<Runnable> getVolunteeringQueue() {
return volunteeringQueue;
}
/**
* Returns the {@link #volunteeringSemaphore} for controlling the number of threads that this PR's
* BucketAdvisors are allowed to use for volunteering to be primary.
*
* @return the semaphore for controlling number of volunteering threads
*/
public Semaphore getVolunteeringSemaphore() {
return volunteeringSemaphore;
}
/**
* Returns an unmodifiable map of bucket IDs to locations hosting the bucket.
*/
public Map<Integer, List<BucketServerLocation66>> getAllClientBucketProfiles() {
Map<Integer, List<BucketServerLocation66>> bucketToServerLocations = new HashMap<>();
for (Integer bucketId : clientBucketProfilesMap.keySet()) {
ArrayList<BucketServerLocation66> clientBucketProfiles = new ArrayList<>();
for (BucketProfile profile : clientBucketProfilesMap.get(bucketId)) {
if (profile.isHosting) {
ServerBucketProfile cProfile = (ServerBucketProfile) profile;
Set<BucketServerLocation66> bucketServerLocations = cProfile.getBucketServerLocations();
// Either we can make BucketServeLocation having ServerLocation with them
// Or we can create bucketServerLocation as it is by iterating over the set of servers
clientBucketProfiles.addAll(bucketServerLocations);
}
}
bucketToServerLocations.put(bucketId, clientBucketProfiles);
}
if (getPartitionedRegion().isDataStore()) {
for (Integer bucketId : getPartitionedRegion().getDataStore().getAllLocalBucketIds()) {
BucketProfile profile = getBucketAdvisor(bucketId).getLocalProfile();
if (logger.isDebugEnabled()) {
logger.debug("The local profile is : {}", profile);
}
if (profile != null) {
List<BucketServerLocation66> clientBucketProfiles =
bucketToServerLocations.computeIfAbsent(bucketId, k -> new ArrayList<>());
if ((profile instanceof ServerBucketProfile) && profile.isHosting) {
ServerBucketProfile cProfile = (ServerBucketProfile) profile;
Set<BucketServerLocation66> bucketServerLocations = cProfile.getBucketServerLocations();
// Either we can make BucketServeLocation having ServerLocation with
// them
// Or we can create bucketServerLocation as it is by iterating over
// the set of servers
clientBucketProfiles.removeAll(bucketServerLocations);
clientBucketProfiles.addAll(bucketServerLocations);
}
}
}
}
return bucketToServerLocations;
}
public ConcurrentHashMap<Integer, Set<ServerBucketProfile>> getAllClientBucketProfilesTest() {
ConcurrentHashMap<Integer, Set<ServerBucketProfile>> map = new ConcurrentHashMap<>();
Map<Integer, List<BucketServerLocation66>> testMap =
new HashMap<>(getAllClientBucketProfiles());
for (Integer bucketId : testMap.keySet()) {
Set<ServerBucketProfile> parr = new HashSet<>(clientBucketProfilesMap.get(bucketId));
map.put(bucketId, parr);
}
if (getPartitionedRegion().isDataStore()) {
for (Integer bucketId : getPartitionedRegion().getDataStore().getAllLocalBucketIds()) {
BucketProfile profile = getBucketAdvisor(bucketId).getLocalProfile();
if ((profile instanceof ServerBucketProfile) && profile.isHosting) {
map.get(bucketId).add((ServerBucketProfile) profile);
}
}
}
if (logger.isDebugEnabled()) {
logger.debug("This maps is sksk {} and size is {}", map, map.keySet().size());
}
return map;
}
public Set<ServerBucketProfile> getClientBucketProfiles(Integer bucketId) {
return clientBucketProfilesMap.get(bucketId);
}
public void setClientBucketProfiles(Integer bucketId, Set<ServerBucketProfile> profiles) {
clientBucketProfilesMap.put(bucketId, Collections.unmodifiableSet(profiles));
}
/**
* Close the bucket advisors, releasing any locks for primary buckets
*/
public void closeBucketAdvisors() {
if (buckets != null) {
for (ProxyBucketRegion pbr : buckets) {
pbr.close();
}
}
}
/**
* Close the adviser and all bucket advisors.
*/
@Override
public void close() {
super.close();
if (buckets != null) {
for (ProxyBucketRegion bucket : buckets) {
bucket.close();
}
}
}
@Override
public boolean removeId(ProfileId memberId, boolean crashed, boolean regionDestroyed,
boolean fromMembershipListener) {
// It's important that we remove member from the bucket advisors first
// Calling super.removeId triggers redundancy satisfaction, so the bucket
// advisors must have up to data information at that point.
boolean removeBuckets = true;
synchronized (preInitQueueMonitor) {
if (preInitQueue != null) {
// Queue profile during pre-initialization
QueuedBucketProfile qbf = new QueuedBucketProfile((InternalDistributedMember) memberId,
crashed, regionDestroyed, fromMembershipListener);
preInitQueue.add(qbf);
removeBuckets = false;
}
} // synchronized
if (removeBuckets && buckets != null) {
for (ProxyBucketRegion pbr : buckets) {
BucketAdvisor pbra = pbr.getBucketAdvisor();
boolean shouldSync = false;
Profile profile = null;
InternalDistributedMember mbr = null;
if (memberId instanceof InternalDistributedMember) {
mbr = (InternalDistributedMember) memberId;
shouldSync = pbra.shouldSyncForCrashedMember(mbr);
if (shouldSync) {
profile = pbr.getBucketAdvisor().getProfile(memberId);
}
}
boolean removed = pbr.getBucketAdvisor().removeId(memberId, crashed, regionDestroyed,
fromMembershipListener);
if (removed && shouldSync) {
pbra.syncForCrashedMember(mbr, profile);
}
}
}
boolean removedId;
removedId = super.removeId(memberId, crashed, regionDestroyed, fromMembershipListener);
if (logger.isTraceEnabled(LogMarker.DISTRIBUTION_ADVISOR_VERBOSE)) {
logger.trace(LogMarker.DISTRIBUTION_ADVISOR_VERBOSE,
"RegionAdvisor#removeId: removing member from region {}: {}; removed = {}; crashed = {}",
getPartitionedRegion().getName(), memberId, removedId, crashed);
}
return removedId;
}
/**
* Clear the knowledge of given member from this advisor. In particular, clear the knowledge of
* remote Bucket locations so that we avoid sending partition messages to buckets that will soon
* be destroyed.
*
* @param memberId member that has closed the region
* @param prSerial serial number of this partitioned region
* @param serials serial numbers of buckets that need to be removed
*/
public void removeIdAndBuckets(InternalDistributedMember memberId, int prSerial, int[] serials,
boolean regionDestroyed) {
if (logger.isTraceEnabled(LogMarker.DISTRIBUTION_ADVISOR_VERBOSE)) {
logger.trace(LogMarker.DISTRIBUTION_ADVISOR_VERBOSE,
"RegionAdvisor#removeIdAndBuckets: removing member from region {}: {}; buckets = ({}) serials",
getPartitionedRegion().getName(), memberId,
(serials == null ? "null" : serials.length));
}
synchronized (preInitQueueMonitor) {
if (preInitQueue != null) {
// Queue profile during pre-initialization
QueuedBucketProfile qbf = new QueuedBucketProfile(memberId, serials, regionDestroyed);
preInitQueue.add(qbf);
return;
}
}
// OK, apply the update NOW
if (buckets != null) {
Objects.requireNonNull(serials);
if (logger.isTraceEnabled(LogMarker.DISTRIBUTION_ADVISOR_VERBOSE)) {
logger.trace(LogMarker.DISTRIBUTION_ADVISOR_VERBOSE,
"RegionAdvisor#removeIdAndBuckets: removing buckets for member{};{}", memberId, this);
}
for (int i = 0; i < buckets.length; i++) {
int s = serials[i];
if (s != ILLEGAL_SERIAL) {
if (logger.isTraceEnabled(LogMarker.DISTRIBUTION_ADVISOR_VERBOSE)) {
logger.trace(LogMarker.DISTRIBUTION_ADVISOR_VERBOSE,
"RegionAdvisor#removeIdAndBuckets: removing bucket #{} serial {}", i, s);
}
buckets[i].getBucketAdvisor().removeIdWithSerial(memberId, s, regionDestroyed);
}
}
super.removeIdWithSerial(memberId, prSerial, regionDestroyed);
}
}
/**
* Iterates over all buckets and marks them sick if the given member hosts the bucket.
*
* @param sick true if the bucket should be marked sick, false if healthy
*/
public void markBucketsOnMember(DistributedMember member, boolean sick) {
// The health profile exchange at cache level should take care of preInitQueue
if (buckets == null) {
return;
}
for (int i = 0; i < buckets.length; i++) {
if (sick && !buckets[i].getBucketOwners().contains(member)) {
continue;
}
buckets[i].setBucketSick(member, sick);
if (logger.isDebugEnabled()) {
logger.debug("Marked bucket ({}) {}", getPartitionedRegion().bucketStringForLogs(i),
(buckets[i].isBucketSick() ? "sick" : "healthy"));
}
}
}
public void updateBucketStatus(int bucketId, DistributedMember member, boolean profileRemoved) {
if (profileRemoved) {
buckets[bucketId].setBucketSick(member, false);
} else {
ResourceAdvisor advisor = getPartitionedRegion().getCache().getResourceAdvisor();
boolean sick = advisor.adviseCriticalMembers().contains(member);
if (logger.isDebugEnabled()) {
logger.debug("updateBucketStatus:({}):member:{}:sick:{}",
getPartitionedRegion().bucketStringForLogs(bucketId), member, sick);
}
buckets[bucketId].setBucketSick(member, sick);
}
}
/**
* throws LowMemoryException if the given bucket is hosted on a member which has crossed the
* ResourceManager threshold.
*
* @param key for bucketId used in exception
*/
public void checkIfBucketSick(final int bucketId, final Object key) throws LowMemoryException {
if (MemoryThresholds.isLowMemoryExceptionDisabled()) {
return;
}
assert buckets != null;
assert buckets[bucketId] != null;
if (buckets[bucketId].isBucketSick()) {
Set<DistributedMember> sm = buckets[bucketId].getSickMembers();
if (sm.isEmpty()) {
// check again as this list is obtained under synchronization
// fixes bug 50845
return;
}
if (logger.isDebugEnabled()) {
logger.debug("For bucket {} sick members are {}.",
getPartitionedRegion().bucketStringForLogs(bucketId), sm);
}
throw new LowMemoryException(String.format(
"PartitionedRegion: %s cannot process operation on key %s because members %s are running low on memory",
getPartitionedRegion().getFullPath(), key, sm), sm);
}
}
/**
* Profile information for a remote counterpart.
*/
public static class PartitionProfile extends CacheProfile {
/**
* The number of Mb the VM is allowed to use for the PR
* {@link PartitionedRegion#getLocalMaxMemory()}
*/
public int localMaxMemory;
/**
* A data store is a VM that has a non-zero local max memory, Since the localMaxMemory is
* already sent, there is no need to send this state as it's implied in localMaxMemory
*/
public transient boolean isDataStore = false;
/**
* requiresNotification determines whether a member needs to be notified of cache operations so
* that cache listeners and other hooks can be engaged
*
* @since GemFire 5.1
*/
public boolean requiresNotification = false;
/**
* Track the number of buckets this data store may have, implies isDataStore == true This value
* is NOT sent directly but updated when {@link org.apache.geode.internal.cache.BucketAdvisor}s
* receive updates
*/
public transient short numBuckets = 0;
/**
* represents the list of the fixed partitions defined for this region.
*/
public List<FixedPartitionAttributesImpl> fixedPAttrs;
// Indicate the status of shutdown request
public int shutDownAllStatus = PartitionedRegion.RUNNING_MODE;
/** for internal use, required for DataSerializer.readObject */
public PartitionProfile() {}
public PartitionProfile(InternalDistributedMember memberId, int version) {
super(memberId, version);
isPartitioned = true;
}
@Override
protected int getIntInfo() {
int s = super.getIntInfo();
if (requiresNotification)
s |= REQUIRES_NOTIFICATION_MASK;
return s;
}
@Override
protected void setIntInfo(int s) {
super.setIntInfo(s);
requiresNotification = (s & REQUIRES_NOTIFICATION_MASK) != 0;
}
@Override
public void fromData(DataInput in) throws IOException, ClassNotFoundException {
super.fromData(in);
localMaxMemory = in.readInt();
isDataStore = localMaxMemory > 0;
fixedPAttrs = DataSerializer.readObject(in);
shutDownAllStatus = in.readInt();
}
@Override
public void toData(DataOutput out) throws IOException {
super.toData(out);
out.writeInt(localMaxMemory);
DataSerializer.writeObject(fixedPAttrs, out);
out.writeInt(shutDownAllStatus);
}
@Override
public StringBuilder getToStringHeader() {
return new StringBuilder("RegionAdvisor.PartitionProfile");
}
@Override
public void fillInToString(StringBuilder sb) {
super.fillInToString(sb);
sb.append("; isDataStore=").append(isDataStore).append("; requiresNotification=")
.append(requiresNotification).append("; localMaxMemory=").append(localMaxMemory)
.append("; numBuckets=").append(numBuckets);
if (fixedPAttrs != null) {
sb.append("; FixedPartitionAttributes=").append(fixedPAttrs);
}
sb.append("; filterProfile=").append(filterProfile);
sb.append("; shutDownAllStatus=").append(shutDownAllStatus);
}
@Override
public int getDSFID() {
return PARTITION_PROFILE;
}
} // end class PartitionProfile
public int getNumDataStores() {
final int numProfs = getNumProfiles();
if (lastActiveProfiles != numProfs) {
numDataStores = adviseDataStore().size();
lastActiveProfiles = numProfs;
}
return numDataStores;
}
public Set<InternalDistributedMember> adviseDataStore() {
return adviseDataStore(false);
}
/**
* Returns the set of data stores that have finished initialization.
*/
public Set<InternalDistributedMember> adviseInitializedDataStore() {
return adviseFilter(profile -> {
// probably not needed as all profiles for a partitioned region are Partition profiles
if (profile instanceof PartitionProfile) {
PartitionProfile p = (PartitionProfile) profile;
return p.isDataStore && (!p.dataPolicy.withPersistence() || p.regionInitialized);
}
return false;
});
}
/**
* Returns the set of members that are not arrived at specified shutDownAll status
*/
private Set<InternalDistributedMember> adviseNotAtShutDownAllStatus(final int status) {
return adviseFilter(profile -> {
// probably not needed as all profiles for a partitioned region are Partition profiles
if (profile instanceof PartitionProfile) {
PartitionProfile p = (PartitionProfile) profile;
return p.isDataStore && p.shutDownAllStatus < status;
}
return false;
});
}
public void waitForProfileStatus(int status) {
ProfileShutdownListener listener = new ProfileShutdownListener();
addProfileChangeListener(listener);
try {
int memberNum;
String regionName = getPartitionedRegion().getFullPath();
do {
Region pr = getPartitionedRegion().getCache().getRegion(regionName);
if (pr == null || pr.isDestroyed())
break;
Set members = adviseNotAtShutDownAllStatus(status);
memberNum = members.size();
if (memberNum > 0) {
if (logger.isDebugEnabled()) {
logger.debug("waitForProfileStatus {} at PR:{}, expecting {} members: {}", status,
getPartitionedRegion().getFullPath(), memberNum, members);
}
listener.waitForChange();
}
} while (memberNum > 0);
} finally {
removeProfileChangeListener(listener);
}
}
/**
* Return a real Set if set to true, which can be modified
*
* @param realHashSet true if a real set is needed
* @return depending on the realHashSet value may be a HashSet
*/
public Set<InternalDistributedMember> adviseDataStore(boolean realHashSet) {
Set<InternalDistributedMember> s = adviseFilter(profile -> {
// probably not needed as all profiles for a partitioned region are Partition profiles
if (profile instanceof PartitionProfile) {
PartitionProfile p = (PartitionProfile) profile;
return p.isDataStore;
}
return false;
});
if (realHashSet) {
if (s == Collections.EMPTY_SET) {
s = new HashSet<>();
}
}
if (logger.isTraceEnabled(LogMarker.DISTRIBUTION_ADVISOR_VERBOSE)) {
logger.trace(LogMarker.DISTRIBUTION_ADVISOR_VERBOSE, "adviseDataStore returning {} from {}",
s, toStringWithProfiles());
}
return s;
}
/**
* return the set of the distributed members on which the given partition name is defined.
*
*/
public Set<InternalDistributedMember> adviseFixedPartitionDataStores(final String partitionName) {
Set<InternalDistributedMember> s = adviseFilter(profile -> {
// probably not needed as all profiles for a partitioned region are
// Partition profiles
if (profile instanceof PartitionProfile) {
PartitionProfile p = (PartitionProfile) profile;
if (p.fixedPAttrs != null) {
for (FixedPartitionAttributesImpl fpa : p.fixedPAttrs) {
if (fpa.getPartitionName().equals(partitionName)) {
return true;
}
}
}
}
return false;
});
if (s == Collections.EMPTY_SET) {
s = new HashSet<>();
}
if (logger.isTraceEnabled(LogMarker.DISTRIBUTION_ADVISOR_VERBOSE)) {
logger.trace(LogMarker.DISTRIBUTION_ADVISOR_VERBOSE,
"adviseFixedPartitionDataStore returning {} from {}", s, toStringWithProfiles());
}
return s;
}
/**
* return a distributed members on which the primary partition for given bucket is defined
*
*/
public InternalDistributedMember adviseFixedPrimaryPartitionDataStore(final int bucketId) {
final List<InternalDistributedMember> fixedPartitionDataStore = new ArrayList<>(1);
fetchProfiles(profile -> {
if (profile instanceof PartitionProfile) {
PartitionProfile p = (PartitionProfile) profile;
if (p.fixedPAttrs != null) {
for (FixedPartitionAttributesImpl fpa : p.fixedPAttrs) {
if (fpa.isPrimary() && fpa.hasBucket(bucketId)) {
fixedPartitionDataStore.add(0, p.getDistributedMember());
return true;
}
}
}
}
return false;
});
if (logger.isTraceEnabled(LogMarker.DISTRIBUTION_ADVISOR_VERBOSE)) {
logger.trace(LogMarker.DISTRIBUTION_ADVISOR_VERBOSE,
"adviseFixedPartitionDataStore returning {} from {}", fixedPartitionDataStore,
toStringWithProfiles());
}
if (fixedPartitionDataStore.isEmpty()) {
return null;
}
return fixedPartitionDataStore.get(0);
}
/**
* Returns the list of all remote FixedPartitionAttributes defined across all members for the
* given partitioned region
*
* @return list of all partitions(primary as well as secondary) defined on remote nodes
*/
public List<FixedPartitionAttributesImpl> adviseAllFixedPartitionAttributes() {
final List<FixedPartitionAttributesImpl> allFPAs = new ArrayList<>();
fetchProfiles(profile -> {
if (profile instanceof PartitionProfile) {
final PartitionProfile pp = (PartitionProfile) profile;
if (pp.fixedPAttrs != null) {
allFPAs.addAll(pp.fixedPAttrs);
return true;
}
}
return false;
});
return allFPAs;
}
/**
* Returns the list of all FixedPartitionAttributes defined across all members of given
* partitioned region for a given FixedPartitionAttributes
*
* @return the list of same partitions defined on other nodes(can be primary or secondary)
*/
public List<FixedPartitionAttributesImpl> adviseSameFPAs(final FixedPartitionAttributesImpl fpa) {
final List<FixedPartitionAttributesImpl> sameFPAs = new ArrayList<>();
fetchProfiles(profile -> {
if (profile instanceof PartitionProfile) {
final PartitionProfile pp = (PartitionProfile) profile;
List<FixedPartitionAttributesImpl> fpaList = pp.fixedPAttrs;
if (fpaList != null) {
int index = fpaList.indexOf(fpa);
if (index != -1) {
sameFPAs.add(fpaList.get(index));
}
return true;
}
}
return false;
});
return sameFPAs;
}
/**
* Returns the list of all remote primary FixedPartitionAttributes defined across members for the
* given partitioned region
*
* @return list of all primary partitions defined on remote nodes
*/
public List<FixedPartitionAttributesImpl> adviseRemotePrimaryFPAs() {
final List<FixedPartitionAttributesImpl> remotePrimaryFPAs = new ArrayList<>();
fetchProfiles(profile -> {
if (profile instanceof PartitionProfile) {
final PartitionProfile pp = (PartitionProfile) profile;
List<FixedPartitionAttributesImpl> fpaList = pp.fixedPAttrs;
if (fpaList != null) {
for (FixedPartitionAttributesImpl fpa : fpaList) {
if (fpa.isPrimary()) {
remotePrimaryFPAs.add(fpa);
return true;
}
}
}
}
return false;
});
return remotePrimaryFPAs;
}
public Set<InternalDistributedMember> adviseAllPRNodes() {
return adviseFilter(profile -> {
CacheProfile prof = (CacheProfile) profile;
return prof.isPartitioned;
});
}
Set adviseAllServersWithInterest() {
return adviseFilter(profile -> {
CacheProfile prof = (CacheProfile) profile;
return prof.hasCacheServer && prof.filterProfile != null
&& prof.filterProfile.hasInterest();
});
}
@Immutable
private static final Filter prServerWithInterestFilter = profile -> {
CacheProfile prof = (CacheProfile) profile;
return prof.isPartitioned && prof.hasCacheServer && prof.filterProfile != null
&& prof.filterProfile.hasInterest();
};
public boolean hasPRServerWithInterest() {
return satisfiesFilter(prServerWithInterestFilter);
}
/**
* return the set of all members who must receive operation notifications
*
* @since GemFire 5.1
*/
public Set<InternalDistributedMember> adviseRequiresNotification() {
return adviseFilter(profile -> {
if (profile instanceof PartitionProfile) {
PartitionProfile prof = (PartitionProfile) profile;
if (prof.isPartitioned) {
if (prof.hasCacheListener) {
InterestPolicy pol = prof.subscriptionAttributes.getInterestPolicy();
if (pol == InterestPolicy.ALL) {
return true;
}
}
return prof.requiresNotification;
}
}
return false;
});
}
@Override
public synchronized boolean putProfile(Profile p) {
assert p instanceof CacheProfile;
CacheProfile profile = (CacheProfile) p;
PartitionedRegion pr = getPartitionedRegion();
if (profile.hasCacheLoader) {
pr.setHaveCacheLoader();
}
// don't keep FilterProfiles around in accessors. They're needed only for
// routing messages in data stors
if (profile.filterProfile != null) {
if (!pr.isDataStore()) {
profile.filterProfile = null;
}
}
return super.putProfile(profile);
}
public PartitionProfile getPartitionProfile(InternalDistributedMember id) {
return (PartitionProfile) getProfile(id);
}
public boolean isPrimaryForBucket(int bucketId) {
if (buckets == null) {
return false;
}
return buckets[bucketId].isPrimary();
}
/**
* Returns true if the bucket is currently being hosted locally. Note that as soon as this call
* returns, this datastore may begin to host the bucket, thus two calls in a row may be different.
*
* @param bucketId the index of the bucket to check
* @return true if the bucket is currently being hosted locally
*/
public boolean isBucketLocal(int bucketId) {
if (buckets == null) {
return false;
}
return buckets[bucketId].getHostedBucketRegion() != null;
}
public boolean areBucketsInitialized() {
return buckets != null;
}
/**
* Returns the real BucketRegion if it's currently locally hosted. Otherwise the ProxyBucketRegion
* is returned. Note that this member may be in the process of hosting the real bucket. Until that
* has completed, getBucket will continue to return the ProxyBucketRegion.
*
* @param bucketId the index of the bucket to retrieve
* @return the bucket identified by bucketId
*/
public Bucket getBucket(int bucketId) {
Assert.assertTrue(buckets != null);
ProxyBucketRegion pbr = buckets[bucketId];
Bucket ret = pbr.getHostedBucketRegion();
if (ret != null) {
return ret;
} else {
return pbr;
}
}
/**
* Returns the BucketAdvisor for the specified bucket.
*
* @param bucketId the index of the bucket to retrieve the advisor for
* @return the bucket advisor identified by bucketId
*/
public BucketAdvisor getBucketAdvisor(int bucketId) {
Assert.assertTrue(buckets != null);
ProxyBucketRegion pbr = buckets[bucketId];
Bucket ret = pbr.getHostedBucketRegion();
if (ret != null) {
return ret.getBucketAdvisor();
} else {
return pbr.getBucketAdvisor();
}
}
public Map<Integer, BucketAdvisor> getAllBucketAdvisors() {
Assert.assertTrue(buckets != null);
Map<Integer, BucketAdvisor> map = new HashMap<>();
for (ProxyBucketRegion pbr : buckets) {
Bucket ret = pbr.getHostedBucketRegion();
if (ret != null) {
map.put(ret.getId(), ret.getBucketAdvisor());
}
}
return map;
}
/**
*
* @return array of serial numbers for buckets created locally
*/
public int[] getBucketSerials() {
if (buckets == null) {
return new int[0];
}
int[] result = new int[buckets.length];
for (int i = 0; i < result.length; i++) {
ProxyBucketRegion pbr = buckets[i];
Bucket b = pbr.getCreatedBucketRegion();
if (b == null) {
result[i] = ILLEGAL_SERIAL;
} else {
result[i] = b.getSerialNumber();
}
}
return result;
}
/**
* Returns the bucket identified by bucketId after waiting for initialization to finish processing
* queued profiles. Call synchronizes and waits on {@link #preInitQueueMonitor}.
*
* @param bucketId the bucket identifier
* @return the bucket identified by bucketId
* @throws org.apache.geode.distributed.DistributedSystemDisconnectedException if interrupted for
* shutdown cancellation
*/
public Bucket getBucketPostInit(int bucketId) {
synchronized (preInitQueueMonitor) {
boolean interrupted = false;
try {
while (preInitQueue != null) {
try {
preInitQueueMonitor.wait(); // spurious wakeup ok
} catch (InterruptedException e) {
interrupted = true;
getAdvisee().getCancelCriterion().checkCancelInProgress(e);
}
}
} finally {
if (interrupted) {
Thread.currentThread().interrupt();
}
}
}
return getBucket(bucketId);
}
/**
* Get the most recent primary node for the bucketId. Returns null if no primary can be found
* within {@link DistributionConfig#getMemberTimeout}.
*
* @return the Node managing the primary copy of the bucket
*/
public InternalDistributedMember getPrimaryMemberForBucket(int bucketId) {
Assert.assertTrue(buckets != null);
Bucket b = buckets[bucketId];
return b.getBucketAdvisor().getPrimary();
}
/**
* Return the node favored for reading for the given bucket
*
* @param bucketId the bucket we want to read
* @return the member, possibly null if no member is available
*/
public InternalDistributedMember getPreferredNode(int bucketId) {
Assert.assertTrue(buckets != null);
Bucket b = buckets[bucketId];
return b.getBucketAdvisor().getPreferredNode();
}
public boolean isStorageAssignedForBucket(int bucketId) {
Assert.assertTrue(buckets != null);
return buckets[bucketId].getBucketRedundancy() >= 0;
}
/**
* @param bucketId the bucket to check redundancy on
* @param minRedundancy the amount of expected redundancy; ignored if wait is false
* @param wait true if caller wants us to wait for redundancy
* @return true if redundancy on given bucket is detected
*/
public boolean isStorageAssignedForBucket(int bucketId, int minRedundancy, boolean wait) {
if (!wait) {
return isStorageAssignedForBucket(bucketId);
} else {
Assert.assertTrue(buckets != null);
return buckets[bucketId].getBucketAdvisor().waitForRedundancy(minRedundancy);
}
}
/**
* Get the redundancy of the this bucket, taking into account the local bucket, if any.
*
* @return number of redundant copies for a given bucket, or -1 if there are no instances of the
* bucket.
*/
public int getBucketRedundancy(int bucketId) {
Assert.assertTrue(buckets != null);
return buckets[bucketId].getBucketRedundancy();
}
/**
* Return the set of all members who currently own the bucket, including the local owner, if
* applicable
*
* @return a set of {@link InternalDistributedMember}s that own the bucket
*/
public Set<InternalDistributedMember> getBucketOwners(int bucketId) {
Assert.assertTrue(buckets != null);
return buckets[bucketId].getBucketOwners();
}
/**
* Return the set of buckets which have storage assigned
*
* @return set of Integer bucketIds
*/
public Set<Integer> getBucketSet() {
Assert.assertTrue(buckets != null);
return new BucketSet();
}
public ProxyBucketRegion[] getProxyBucketArray() {
return buckets;
}
private class BucketSet extends AbstractSet<Integer> {
final ProxyBucketRegion[] pbrs;
BucketSet() {
pbrs = buckets;
Assert.assertTrue(pbrs != null);
}
/*
* Note: The consistency between size(), hasNext() and next() is weak, meaning that the state of
* the backing Set may change causing more or less elements to be available after calling size()
*/
@Override
public int size() {
return pbrs.length;
}
@Override
public Iterator<Integer> iterator() {
return new BucketSetIterator();
}
class BucketSetIterator implements Iterator<Integer> {
private int currentItem = -1;
@Override
public void remove() {
throw new UnsupportedOperationException();
}
/*
* Note: The consistency guarantee between hasNext() and next() is weak. It's possible
* hasNext() will return true and a following call to next() may throw NoSuchElementException
* (due to loss of bucket storage). Its also equally possible for hasNext() to return false
* and a subsequent call to next() will return a valid bucketid.
*/
@Override
public boolean hasNext() {
if (getPartitionedRegion().isFixedPartitionedRegion()) {
if (currentItem + 1 < pbrs.length) {
int possibleBucketId = currentItem;
boolean bucketExists = false;
List<FixedPartitionAttributesImpl> fpaList = adviseAllFixedPartitionAttributes();
List<FixedPartitionAttributesImpl> localFpas =
getPartitionedRegion().getFixedPartitionAttributesImpl();
if (localFpas != null) {
fpaList.addAll(localFpas);
}
while (++possibleBucketId < pbrs.length && !bucketExists) {
for (FixedPartitionAttributesImpl fpa : fpaList) {
if (fpa.hasBucket(possibleBucketId)) {
bucketExists = true;
break;
}
}
}
return bucketExists;
} else {
return false;
}
} else {
return currentItem + 1 < pbrs.length;
}
}
@Override
public Integer next() {
if (++currentItem < pbrs.length) {
if (isStorageAssignedForBucket(currentItem)) {
return currentItem;
} else {
if (getPartitionedRegion().isFixedPartitionedRegion()) {
boolean bucketExists = false;
List<FixedPartitionAttributesImpl> fpaList = adviseAllFixedPartitionAttributes();
List<FixedPartitionAttributesImpl> localFpas =
getPartitionedRegion().getFixedPartitionAttributesImpl();
if (localFpas != null) {
fpaList.addAll(localFpas);
}
do {
for (FixedPartitionAttributesImpl fpa : fpaList) {
if (fpa.hasBucket(currentItem)) {
bucketExists = true;
break;
}
}
if (!bucketExists) {
currentItem++;
}
} while (currentItem < pbrs.length && !bucketExists);
if (bucketExists) {
getPartitionedRegion().createBucket(currentItem, 0, null);
return currentItem;
}
} else {
getPartitionedRegion().createBucket(currentItem, 0, null);
return currentItem;
}
}
}
throw new NoSuchElementException();
}
}
}
/**
* Obtain the ordered {@link ArrayList} of data stores limited to those specified in the provided
* memberFilter.
*
* @param memberFilter the set of members allowed to be in the list.
* @return a list of DataStoreBuckets
*/
public ArrayList<DataStoreBuckets> adviseFilteredDataStores(
final Set<InternalDistributedMember> memberFilter) {
final HashMap<InternalDistributedMember, Integer> memberToPrimaryCount = new HashMap<>();
for (ProxyBucketRegion pbr : buckets) {
// quick dirty check
InternalDistributedMember p = pbr.getBucketAdvisor().basicGetPrimaryMember();
if (p != null) {
memberToPrimaryCount.merge(p, 1, Integer::sum);
}
}
final ArrayList<DataStoreBuckets> ds = new ArrayList<>(memberFilter.size());
adviseFilter(profile -> {
if (profile instanceof PartitionProfile) {
PartitionProfile p = (PartitionProfile) profile;
if (memberFilter.contains(p.getDistributedMember())) {
Integer priCount = memberToPrimaryCount.get(p.getDistributedMember());
int primaryCount = 0;
if (priCount != null) {
primaryCount = priCount;
}
ds.add(new DataStoreBuckets(p.getDistributedMember(), p.numBuckets, primaryCount,
p.localMaxMemory));
}
}
return false;
});
return ds;
}
public void incrementBucketCount(Profile p) {
PartitionProfile pp = (PartitionProfile) getProfile(p.getDistributedMember());
if (pp != null) {
Assert.assertTrue(pp.isDataStore);
pp.numBuckets++;
}
}
public void decrementsBucketCount(Profile p) {
PartitionProfile pp = (PartitionProfile) getProfile(p.getDistributedMember());
if (pp != null) {
Assert.assertTrue(pp.isDataStore);
pp.numBuckets--;
if (pp.numBuckets < 0) {
pp.numBuckets = 0;
}
}
}
/**
* Dumps out all profiles in this advisor AND all buckets. Callers should check for debug enabled.
*
* @param infoMsg prefix message to log
*/
@Override
public void dumpProfiles(String infoMsg) {
if (logger.isDebugEnabled()) {
logger.debug("[dumpProfiles] dumping {}", toStringWithProfiles());
}
// 1st dump all profiles for this RegionAdvisor
super.dumpProfiles(infoMsg);
// 2nd dump all profiles for each BucketAdvisor
ProxyBucketRegion[] pbrs = buckets;
if (pbrs == null) {
return;
}
for (ProxyBucketRegion pbr : pbrs) {
pbr.getBucketAdvisor().dumpProfiles(infoMsg);
BucketPersistenceAdvisor persistentAdvisor = pbr.getPersistenceAdvisor();
if (persistentAdvisor != null) {
persistentAdvisor.dump(infoMsg);
}
}
}
public void notPrimary(int bucketId, InternalDistributedMember wasPrimary) {
Assert.assertTrue(buckets != null);
ProxyBucketRegion b = buckets[bucketId];
b.getBucketAdvisor().notPrimary(wasPrimary);
}
/**
* Find the set of members which own primary buckets, including the local member
*
* @return set of InternalDistributedMember ids
*/
public Set advisePrimaryOwners() {
Assert.assertTrue(buckets != null);
ProxyBucketRegion[] bucs = buckets;
HashSet<InternalDistributedMember> hs = new HashSet<>();
for (int i = 0; i < bucs.length; i++) {
if (isStorageAssignedForBucket(i)) {
InternalDistributedMember mem = bucs[i].getBucketAdvisor().getPrimary();
if (mem != null) {
hs.add(mem);
}
}
}
return hs;
}
/**
* A visitor interface for the buckets of this region used by
* {@link RegionAdvisor#accept(BucketVisitor, Object)}.
*/
public interface BucketVisitor<T> {
/**
* Visit a given {@link ProxyBucketRegion} accumulating the results in the given aggregate.
* Returns false when the visit has to be terminated.
*/
boolean visit(RegionAdvisor advisor, ProxyBucketRegion pbr, T aggregate);
}
/**
* Invoke the given {@link BucketVisitor} on all the {@link ProxyBucketRegion} s exiting when the
* {@link BucketVisitor#visit} method returns false.
*
* @param <T> the type of object used for aggregation of results
* @param visitor the {@link BucketVisitor} to use for the visit
* @param aggregate an aggregate object that will be used to for aggregation of results by the
* {@link BucketVisitor#visit} method; this allows the {@link BucketVisitor} to not
* maintain any state so that in most situations a global static object encapsulating the
* required behaviour will work
*
* @return true when the full visit completed, and false if it was terminated due to
* {@link BucketVisitor#visit} returning false
*/
public <T> boolean accept(BucketVisitor<T> visitor, T aggregate) {
final ProxyBucketRegion[] bucs = buckets;
Objects.requireNonNull(bucs);
for (ProxyBucketRegion pbr : bucs) {
if (!visitor.visit(this, pbr, aggregate)) {
return false;
}
}
return true;
}
public PartitionedRegion getPartitionedRegion() {
return (PartitionedRegion) getAdvisee();
}
/**
* Update or create a bucket's meta-data If this advisor has not completed initialization, upon
* return the profile will be enqueued for processing during initialization, otherwise the profile
* will be immediately processed. This architecture limits the blockage of threads during
* initialization.
*
* @param bucketId the unique identifier of the bucket
* @param profile the bucket meta-data from a particular member with the bucket
*/
void putBucketProfile(int bucketId, BucketProfile profile) {
synchronized (preInitQueueMonitor) {
if (preInitQueue != null) {
// Queue profile during pre-initialization
QueuedBucketProfile qbf = new QueuedBucketProfile(bucketId, profile);
preInitQueue.add(qbf);
return;
}
}
// Directly process profile post-initialization
getBucket(bucketId).getBucketAdvisor().putProfile(profile);
}
static class QueuedBucketProfile {
protected final int bucketId;
final BucketProfile bucketProfile;
/** true means that this member has departed the view */
protected final boolean memberDeparted;
/** true means that this profile needs to be removed */
final boolean isRemoval;
/** true means that the peer crashed */
protected final boolean crashed;
/**
* true means that this QueuedBucketProfile was created because of MembershipListener invocation
*/
final boolean fromMembershipListener;
protected final boolean destroyed;
protected final InternalDistributedMember memberId;
final int[] serials;
/**
* Queue up an addition
*
* @param bId the bucket being added
* @param p the profile to add
*/
QueuedBucketProfile(int bId, BucketProfile p) {
bucketId = bId;
bucketProfile = p;
isRemoval = false;
crashed = false;
memberDeparted = false;
memberId = null;
serials = null;
destroyed = false;
fromMembershipListener = false;
}
/**
* Queue up a removal due to member leaving the view
*
* @param mbr the member being removed
*/
QueuedBucketProfile(InternalDistributedMember mbr, boolean crashed, boolean destroyed,
boolean fromMembershipListener) {
bucketId = 0;
bucketProfile = null;
isRemoval = true;
this.crashed = crashed;
memberDeparted = true;
memberId = mbr;
serials = null;
this.destroyed = destroyed;
this.fromMembershipListener = fromMembershipListener;
}
/**
* Queue up a removal due to region destroy
*
* @param mbr the member being removed
* @param serials the serials it had
*/
QueuedBucketProfile(InternalDistributedMember mbr, int[] serials, boolean destroyed) {
bucketId = 0;
bucketProfile = null;
isRemoval = true;
crashed = false;
memberDeparted = false;
memberId = mbr;
this.serials = serials;
this.destroyed = destroyed;
fromMembershipListener = false;
}
}
public Set<InternalDistributedMember> adviseBucketProfileExchange() {
return adviseDataStore();
}
public long adviseTotalMemoryAllocation() {
final AtomicLong total = new AtomicLong();
adviseFilter(profile -> {
// probably not needed as all profiles for a partitioned region are Partition profiles
if (profile instanceof PartitionProfile) {
PartitionProfile p = (PartitionProfile) profile;
total.addAndGet(p.localMaxMemory);
}
return false;
});
return total.get();
}
/**
* Returns the total number of buckets created anywhere in the distributed system for this
* partitioned region.
*
* @return the total number of buckets created anywhere for this PR
*/
public int getCreatedBucketsCount() {
final ProxyBucketRegion[] bucs = buckets;
if (bucs == null) {
return 0;
}
int createdBucketsCount = 0;
for (ProxyBucketRegion buc : bucs) {
if (buc.getBucketOwnersCount() > 0) {
createdBucketsCount++;
}
}
return createdBucketsCount;
}
/**
* Returns a possibly null list of this advisor's real bucket profiles. A real bucket profile is
* one that for a bucket that actually has storage in this vm.
*
* @return a list of BucketProfileAndId instances; may be null
* @since GemFire 5.5
*/
public ArrayList getBucketRegionProfiles() {
final ProxyBucketRegion[] bucs = buckets;
if (bucs == null) {
return null;
}
ArrayList<BucketProfileAndId> result = new ArrayList<>(bucs.length);
for (int i = 0; i < bucs.length; i++) {
// Fix for 41436 - we need to include buckets that are still initializing here
// we must start including buckets in this list *before* those buckets exchange
// profiles.
BucketRegion br = bucs[i].getCreatedBucketRegion();
if (br != null) {
result.add(new BucketProfileAndId(br.getProfile(), i));
}
}
if (result.size() == 0) {
result = null;
}
return result;
}
/**
* Takes a list of BucketProfileAndId and adds them to thsi advisors proxy buckets.
*
* @since GemFire 5.5
*/
public void putBucketRegionProfiles(ArrayList<BucketProfileAndId> l) {
for (BucketProfileAndId bp : l) {
int id = bp.getId();
getBucket(id).getBucketAdvisor().putProfile(bp.getBucketProfile());
}
}
/**
* return true if the given member has this advisor's partitioned region
*/
public boolean hasPartitionedRegion(InternalDistributedMember profileId) {
if (getDistributionManager().getId().equals(profileId)) {
return true;
}
return (getProfile(profileId) != null);
}
@Override
protected void profileRemoved(Profile profile) {
if (logger.isDebugEnabled()) {
logger.debug("RA: removing profile {}", profile);
}
if (getAdvisee() instanceof PartitionedRegion) {
((PartitionedRegion) getAdvisee()).removeCriticalMember(profile.peerMemberId);
}
if (buckets != null) {
for (ProxyBucketRegion bucket : buckets) {
bucket.getBucketAdvisor().checkForLostPrimaryElector(profile);
}
}
}
public static class BucketProfileAndId implements DataSerializable {
private static final long serialVersionUID = 332892607792421553L;
/* final */ private int id;
// bid = bucket id
/* final */ private BucketProfile bp;
private boolean isServerBucketProfile = false;
public BucketProfileAndId(Profile bp, int id) {
this.id = id;
this.bp = (BucketProfile) bp;
if (bp instanceof ServerBucketProfile)
isServerBucketProfile = true;
}
public BucketProfileAndId() {}
public int getId() {
return id;
}
BucketProfile getBucketProfile() {
return bp;
}
@Override
public void fromData(DataInput in) throws IOException, ClassNotFoundException {
id = in.readInt();
isServerBucketProfile = in.readBoolean();
if (isServerBucketProfile)
bp = new ServerBucketProfile();
else
bp = new BucketProfile();
InternalDataSerializer.invokeFromData(bp, in);
}
@Override
public void toData(DataOutput out) throws IOException {
out.writeInt(id);
out.writeBoolean(isServerBucketProfile);
InternalDataSerializer.invokeToData(bp, out);
}
@Override
public String toString() {
return "BucketProfileAndId (profile=" + bp + "; id=" + id + ")";
}
}
// profile listener to monitor remote member unexpected leave during shutdownAll
private class ProfileShutdownListener implements ProfileListener {
ProfileShutdownListener() {
}
private boolean profileChanged = false;
void waitForChange() {
Region pr = getPartitionedRegion();
synchronized (this) {
while (!profileChanged && pr != null && !pr.isDestroyed()) {
// the advisee might have been destroyed due to initialization failure
try {
wait(1000);
} catch (InterruptedException ignored) {
}
}
profileChanged = false;
}
}
@Override
public void profileCreated(Profile profile) {
profileUpdated(profile);
}
@Override
public void profileRemoved(Profile profile, boolean regionDestroyed) {
// if a profile is gone, notify
synchronized (this) {
profileChanged = true;
notifyAll();
}
}
@Override
public void profileUpdated(Profile profile) {
// when updated, notify the loop in GFC to check the list again
synchronized (this) {
profileChanged = true;
notifyAll();
}
}
}
}