/*
* 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 org.apache.geode.DataSerializable;
import org.apache.geode.DataSerializer;
import org.apache.geode.InternalGemFireException;
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.*;
import org.apache.geode.internal.cache.BucketAdvisor.BucketProfile;
import org.apache.geode.internal.cache.BucketAdvisor.ServerBucketProfile;
import org.apache.geode.internal.cache.PRHARedundancyProvider.DataStoreBuckets;
import org.apache.geode.internal.cache.control.MemoryThresholds;
import org.apache.geode.internal.cache.control.ResourceAdvisor;
import org.apache.geode.internal.cache.persistence.PersistenceAdvisor;
import org.apache.geode.internal.cache.persistence.PersistentStateListener;
import org.apache.geode.internal.i18n.LocalizedStrings;
import org.apache.geode.internal.logging.LogService;
import org.apache.geode.internal.logging.log4j.LocalizedMessage;
import org.apache.geode.internal.logging.log4j.LogMarker;
import org.apache.logging.log4j.Logger;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Semaphore;
import java.util.concurrent.atomic.AtomicLong;
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 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 preInitQueue;
private final Object preInitQueueMonitor = new Object();
/**
* Used by to generate redundancy loss alert only once even if more than one bucket or PR has lost
* redundancy. lowRedundancyFlags[0] is true if any bucket in this partitioned region has lower
* than configured redundancy. lowRedundancyFlags[1] is true if a warning has been generated for
* the current actual redundancy of this partitioned region. The caller must synchronize on
* lowRedundancyFlags in order to maintain atomicity of overall redundancy status and alert.
*/
private final boolean[] lowRedundancyFlags = new boolean[2];
private ConcurrentHashMap<Integer, Set<ServerBucketProfile>> clientBucketProfilesMap;
/**
* Caller must synchronize on the return value.
*
* @return the low redundancy flags for this partitioned region
*/
public boolean[] getLowRedundancyFlags() {
return lowRedundancyFlags;
}
private RegionAdvisor(PartitionedRegion region) {
super(region);
synchronized (this.preInitQueueMonitor) {
this.preInitQueue = new ConcurrentLinkedQueue();
}
this.clientBucketProfilesMap = new ConcurrentHashMap<Integer, Set<ServerBucketProfile>>();
}
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 (this.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();
}
this.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 (this.preInitQueueMonitor) {
Iterator pi = this.preInitQueue.iterator();
boolean finishedInitQueue = false;
try {
while (pi.hasNext()) {
Object o = pi.next();
QueuedBucketProfile qbp = (QueuedBucketProfile) o;
if (!qbp.isRemoval) {
if (logger.isTraceEnabled(LogMarker.DA)) {
logger.trace(LogMarker.DA, "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.DA)) {
logger.trace(LogMarker.DA, "applying queued member departure for all buckets for {}",
qbp.memberId);
}
for (int i = 0; i < this.buckets.length; i++) {
BucketAdvisor ba = this.buckets[i].getBucketAdvisor();
ba.removeId(qbp.memberId, crashed, qbp.destroyed, qbp.fromMembershipListener);
} // for
} else { // apply removal for member still in the view
if (logger.isTraceEnabled(LogMarker.DA)) {
logger.trace(LogMarker.DA, "applying queued profile removal for all buckets for {}",
qbp.memberId);
}
for (int i = 0; i < this.buckets.length; i++) {
BucketAdvisor ba = this.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 {
this.preInitQueue = null; // prevent further additions to the queue
this.preInitQueueMonitor.notifyAll();
if (!finishedInitQueue && !getAdvisee().getCancelCriterion().isCancelInProgress()) {
logger.error(LocalizedMessage.create(
LocalizedStrings.RegionAdvisor_FAILED_TO_PROCESS_ALL_QUEUED_BUCKETPROFILES_FOR_0,
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 getVolunteeringQueue() {
return this.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 this.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<Integer, List<BucketServerLocation66>>();
for (Integer bucketId : this.clientBucketProfilesMap.keySet()) {
ArrayList<BucketServerLocation66> clientBucketProfiles =
new ArrayList<BucketServerLocation66>();
for (BucketProfile profile : this.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.get(bucketId);
if (clientBucketProfiles == null) {
clientBucketProfiles = new ArrayList<BucketServerLocation66>();
bucketToServerLocations.put(bucketId, clientBucketProfiles);
}
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<Integer, Set<ServerBucketProfile>>();
Map<Integer, List<BucketServerLocation66>> testMap =
new HashMap<>(this.getAllClientBucketProfiles());
for (Integer bucketId : testMap.keySet()) {
Set<ServerBucketProfile> parr = new HashSet<>(this.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 this.clientBucketProfilesMap.get(bucketId);
}
public void setClientBucketProfiles(Integer bucketId, Set<ServerBucketProfile> profiles) {
this.clientBucketProfilesMap.put(bucketId, Collections.unmodifiableSet(profiles));
}
/**
* Close the bucket advisors, releasing any locks for primary buckets
*
* @return returns a list of primary bucket IDs
*
*/
public List closeBucketAdvisors() {
List primariesHeld = Collections.EMPTY_LIST;
if (this.buckets != null) {
for (int i = 0; i < this.buckets.length; i++) {
ProxyBucketRegion pbr = this.buckets[i];
if (pbr.isPrimary()) {
if (primariesHeld == Collections.EMPTY_LIST) {
primariesHeld = new ArrayList();
}
primariesHeld.add(Integer.valueOf(i));
}
pbr.close();
}
}
return primariesHeld;
}
/**
* Close the adviser and all bucket advisors.
*/
@Override
public void close() {
super.close();
if (this.buckets != null) {
for (int i = 0; i < this.buckets.length; i++) {
this.buckets[i].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 (this.preInitQueueMonitor) {
if (this.preInitQueue != null) {
// Queue profile during pre-initialization
assert memberId instanceof InternalDistributedMember;
QueuedBucketProfile qbf = new QueuedBucketProfile((InternalDistributedMember) memberId,
crashed, regionDestroyed, fromMembershipListener);
this.preInitQueue.add(qbf);
removeBuckets = false;
}
} // synchronized
if (removeBuckets && this.buckets != null) {
for (int i = 0; i < this.buckets.length; i++) {
ProxyBucketRegion pbr = this.buckets[i];
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 = false;
removedId = super.removeId(memberId, crashed, regionDestroyed, fromMembershipListener);
if (logger.isTraceEnabled(LogMarker.DA)) {
logger.trace(LogMarker.DA,
"RegionAdvisor#removeId: removing member from region {}: {}; removed = {}; crashed = {}",
this.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.DA)) {
logger.trace(LogMarker.DA,
"RegionAdvisor#removeIdAndBuckets: removing member from region {}: {}; buckets = ({}) serials",
this.getPartitionedRegion().getName(), memberId,
(serials == null ? "null" : serials.length));
}
synchronized (this.preInitQueueMonitor) {
if (this.preInitQueue != null) {
// Queue profile during pre-initialization
QueuedBucketProfile qbf = new QueuedBucketProfile(memberId, serials, regionDestroyed);
this.preInitQueue.add(qbf);
return;
}
} // synchronized
// OK, apply the update NOW
if (this.buckets != null) {
if (logger.isTraceEnabled(LogMarker.DA)) {
logger.trace(LogMarker.DA,
"RegionAdvisor#removeIdAndBuckets: removing buckets for member{};{}", memberId, this);
}
for (int i = 0; i < this.buckets.length; i++) {
int s = serials[i];
if (s != ILLEGAL_SERIAL) {
if (logger.isTraceEnabled(LogMarker.DA)) {
logger.trace(LogMarker.DA,
"RegionAdvisor#removeIdAndBuckets: removing bucket #{} serial {}", i, s);
}
this.buckets[i].getBucketAdvisor().removeIdWithSerial(memberId, s, regionDestroyed);
}
} // for
super.removeIdWithSerial(memberId, prSerial, regionDestroyed);
// super.removeId(memberId);
}
}
/**
* Iterates over all buckets and marks them sick if the given member hosts the bucket.
*
* @param member
* @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 && !this.buckets[i].getBucketOwners().contains(member)) {
continue;
}
this.buckets[i].setBucketSick(member, sick);
if (logger.isDebugEnabled()) {
logger.debug("Marked bucket ({}) {}", getPartitionedRegion().bucketStringForLogs(i),
(this.buckets[i].isBucketSick() ? "sick" : "healthy"));
}
}
}
public void updateBucketStatus(int bucketId, DistributedMember member, boolean profileRemoved) {
if (profileRemoved) {
this.buckets[bucketId].setBucketSick(member, false);
// getClientBucketProfiles(bucketId).remove();
} else {
ResourceAdvisor advisor = getPartitionedRegion().getCache().getResourceAdvisor();
boolean sick = advisor.adviseCritialMembers().contains(member);
if (logger.isDebugEnabled()) {
logger.debug("updateBucketStatus:({}):member:{}:sick:{}",
getPartitionedRegion().bucketStringForLogs(bucketId), member, sick);
}
this.buckets[bucketId].setBucketSick(member, sick);
}
}
/**
* throws LowMemoryException if the given bucket is hosted on a member which has crossed the
* ResourceManager threshold.
*
* @param bucketId
* @param key for bucketId used in exception
* @throws LowMemoryException
*/
public void checkIfBucketSick(final int bucketId, final Object key) throws LowMemoryException {
if (MemoryThresholds.isLowMemoryExceptionDisabled()) {
return;
}
assert this.buckets != null;
assert this.buckets[bucketId] != null;
if (this.buckets[bucketId].isBucketSick()) {
Set<DistributedMember> sm = this.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(LocalizedStrings.ResourceManager_LOW_MEMORY_PR_0_KEY_1_MEMBERS_2
.toLocalizedString(new Object[] {getPartitionedRegion().getFullPath(), key, sm}), sm);
}
}
/**
* Profile information for a remote counterpart.
*/
public static final 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;
/**
* A lock used to order operations that need to know about the imminent closure/destruction of a
* Region
*/
// private StoppableReentrantReadWriteLock isClosingLock = null;
/**
* 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
* recieve 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);
this.isPartitioned = true;
}
@Override
protected final int getIntInfo() {
int s = super.getIntInfo();
if (this.requiresNotification)
s |= REQUIRES_NOTIFICATION_MASK;
return s;
}
@Override
protected final void setIntInfo(int s) {
super.setIntInfo(s);
this.requiresNotification = (s & REQUIRES_NOTIFICATION_MASK) != 0;
}
@Override
public void fromData(DataInput in) throws IOException, ClassNotFoundException {
super.fromData(in);
this.localMaxMemory = in.readInt();
this.isDataStore = this.localMaxMemory > 0;
this.fixedPAttrs = DataSerializer.readObject(in);
this.shutDownAllStatus = in.readInt();
}
@Override
public void toData(DataOutput out) throws IOException {
super.toData(out);
out.writeInt(this.localMaxMemory);
DataSerializer.writeObject(this.fixedPAttrs, out);
out.writeInt(this.shutDownAllStatus);
}
// public final StoppableReentrantReadWriteLock.StoppableReadLock
// getIsClosingReadLock(CancelCriterion stopper) {
// synchronized (this) {
// if (isClosingLock == null) {
// this.isClosingLock = new StoppableReentrantReadWriteLock(stopper);
// }
// }
// return this.isClosingLock.readLock();
// }
// public final Lock getIsClosingWriteLock() {
// return this.isClosingLock.writeLock();
// }
@Override
public StringBuilder getToStringHeader() {
return new StringBuilder("RegionAdvisor.PartitionProfile");
}
@Override
public void fillInToString(StringBuilder sb) {
super.fillInToString(sb);
sb.append("; isDataStore=").append(this.isDataStore).append("; requiresNotification=")
.append(this.requiresNotification).append("; localMaxMemory=").append(this.localMaxMemory)
.append("; numBuckets=").append(this.numBuckets);
if (this.fixedPAttrs != null) {
sb.append("; FixedPartitionAttributes=").append(this.fixedPAttrs);
}
sb.append("; filterProfile=").append(this.filterProfile);
sb.append("; shutDownAllStatus=").append(this.shutDownAllStatus);
}
@Override
public int getDSFID() {
return PARTITION_PROFILE;
}
} // end class PartitionProfile
public int getNumDataStores() {
final int numProfs = getNumProfiles();
if (this.lastActiveProfiles != numProfs) {
this.numDataStores = adviseDataStore().size();
this.lastActiveProfiles = numProfs;
}
return this.numDataStores;
}
public Set<InternalDistributedMember> adviseDataStore() {
return this.adviseDataStore(false);
}
/**
* Returns the set of data stores that have finished initialization.
*/
public Set<InternalDistributedMember> adviseInitializedDataStore() {
Set<InternalDistributedMember> s = adviseFilter(new Filter() {
public boolean include(Profile 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;
}
});
return s;
}
/**
* Returns the set of members that are not arrived at specified shutDownAll status
*/
public Set<InternalDistributedMember> adviseNotAtShutDownAllStatus(final int status) {
Set<InternalDistributedMember> s = adviseFilter(new Filter() {
public boolean include(Profile 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;
}
});
return s;
}
public void waitForProfileStatus(int status) {
ProfileShutdownListener listener = new ProfileShutdownListener();
addProfileChangeListener(listener);
try {
int memberNum = 0;
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(new Filter() {
public boolean include(Profile 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<InternalDistributedMember>();
}
}
if (logger.isTraceEnabled(LogMarker.DA)) {
logger.trace(LogMarker.DA, "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(new Filter() {
public boolean include(Profile 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<InternalDistributedMember>();
}
if (logger.isTraceEnabled(LogMarker.DA)) {
logger.trace(LogMarker.DA, "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<InternalDistributedMember>(1);
fetchProfiles(new Filter() {
public boolean include(Profile 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.DA)) {
logger.trace(LogMarker.DA, "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<FixedPartitionAttributesImpl>();
fetchProfiles(new Filter() {
public boolean include(final Profile 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
*
* @param fpa
* @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<FixedPartitionAttributesImpl>();
fetchProfiles(new Filter() {
public boolean include(final Profile 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<FixedPartitionAttributesImpl>();
fetchProfiles(new Filter() {
public boolean include(final Profile 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;
}
/**
* TODO remove this when Primary Bucket impl. is permanently in place
*
* @param limitNodeList
* @return the node??
*/
public Node adviseSmallestDataStore(final List limitNodeList) {
final HashMap filtSet = new HashMap(limitNodeList.size());
Node n = null;
for (Iterator filtI = limitNodeList.iterator(); filtI.hasNext();) {
n = (Node) filtI.next();
filtSet.put(n.getMemberId(), n);
}
final Object[] smallest = new Object[1];
adviseFilter(new Filter() {
short numBucks = Short.MAX_VALUE;
public boolean include(Profile profile) {
if (profile instanceof PartitionProfile) {
PartitionProfile p = (PartitionProfile) profile;
if (filtSet.containsKey(p.getDistributedMember()) && p.numBuckets < this.numBucks) {
smallest[0] = p.getDistributedMember();
this.numBucks = p.numBuckets;
}
}
return false;
}
});
return (Node) filtSet.get(smallest[0]);
}
public List<DistributedMember> orderDataStoresUsingBucketCount(final Set nodes) {
final Set<NodeBucketSize> orderedSet = new TreeSet<NodeBucketSize>();
final List<DistributedMember> orderedList = new ArrayList<DistributedMember>();
final DistributedMember self = getDistributionManager().getDistributionManagerId();
adviseFilter(new Filter() {
public boolean include(Profile profile) {
if (profile instanceof PartitionProfile && nodes.contains(profile.getDistributedMember())) {
PartitionProfile p = (PartitionProfile) profile;
orderedSet.add(new NodeBucketSize(p.numBuckets, p.getDistributedMember()));
return true;
} else if (profile instanceof PartitionProfile && nodes.contains(self)) {
orderedSet.add(new NodeBucketSize(getBucketSet().size(), self));
return true;
}
return false;
}
});
if (nodes.contains(self)
&& !orderedSet.contains(new NodeBucketSize(getBucketSet().size(), self))) {
orderedSet.add(new NodeBucketSize(getBucketSet().size(), self));
}
for (NodeBucketSize node : orderedSet) {
orderedList.add(node.member);
}
return orderedList;
}
private class NodeBucketSize implements Comparable {
private final int numBuckets;
private final DistributedMember member;
public NodeBucketSize(final int numBuckets, final DistributedMember member) {
this.numBuckets = numBuckets;
this.member = member;
}
public int compareTo(Object o) {
assert o instanceof NodeBucketSize;
NodeBucketSize node = (NodeBucketSize) o;
if (node.numBuckets > this.numBuckets) {
return 1;
}
return -1;
}
@Override
public String toString() {
return "NodeBucketSize [ member =" + member + " numBuckets = " + numBuckets + "]";
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof NodeBucketSize)) {
return false;
}
NodeBucketSize node = (NodeBucketSize) obj;
if (this.member.getId().equals(node.member.getId())) {
return true;
}
return false;
}
}
public Set adviseAllPRNodes() {
return adviseFilter(new Filter() {
public boolean include(Profile profile) {
CacheProfile prof = (CacheProfile) profile;
return prof.isPartitioned;
}
});
}
/**
* return the set of all members who must receive operation notifications
*
* @since GemFire 5.1
*/
public Set adviseRequiresNotification(final EntryEventImpl event) {
return adviseFilter(new Filter() {
public boolean include(Profile 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;
}
}
if (prof.requiresNotification) {
return true;
}
return false;
}
}
return false;
}
});
}
@Override
final synchronized public 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);
}
final public PartitionProfile getPartitionProfile(InternalDistributedMember id) {
return (PartitionProfile) getProfile(id);
}
public boolean isPrimaryForBucket(int bucketId) {
if (this.buckets == null) {
return false;
}
return this.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 (this.buckets == null) {
return false;
}
return this.buckets[bucketId].getHostedBucketRegion() != null;
}
public boolean areBucketsInitialized() {
return this.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(this.buckets != null);
ProxyBucketRegion pbr = this.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(this.buckets != null);
ProxyBucketRegion pbr = this.buckets[bucketId];
Bucket ret = pbr.getHostedBucketRegion();
if (ret != null) {
return ret.getBucketAdvisor();
} else {
return pbr.getBucketAdvisor();
}
}
public Map<Integer, BucketAdvisor> getAllBucketAdvisors() {
Assert.assertTrue(this.buckets != null);
Map<Integer, BucketAdvisor> map = new HashMap<Integer, BucketAdvisor>();
for (int i = 0; i < buckets.length; i++) {
ProxyBucketRegion pbr = this.buckets[i];
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 (this.buckets == null) {
return new int[0];
}
int result[] = new int[this.buckets.length];
for (int i = 0; i < result.length; i++) {
ProxyBucketRegion pbr = this.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 (this.preInitQueueMonitor) {
boolean interrupted = false;
try {
while (this.preInitQueue != null) {
try {
this.preInitQueueMonitor.wait(); // spurious wakeup ok
} catch (InterruptedException e) {
interrupted = true;
this.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}.
*
* @param bucketId
* @return the Node managing the primary copy of the bucket
*/
final public InternalDistributedMember getPrimaryMemberForBucket(int bucketId) {
Assert.assertTrue(this.buckets != null);
Bucket b = this.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(this.buckets != null);
Bucket b = this.buckets[bucketId];
return b.getBucketAdvisor().getPreferredNode();
}
public boolean isStorageAssignedForBucket(int bucketId) {
Assert.assertTrue(this.buckets != null);
return this.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(this.buckets != null);
return this.buckets[bucketId].getBucketAdvisor().waitForRedundancy(minRedundancy);
}
}
public boolean waitForLocalBucketStorage(int bucketId) {
Assert.assertTrue(this.buckets != null);
return this.buckets[bucketId].getBucketAdvisor().waitForStorage();
}
/**
* 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(this.buckets != null);
return this.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(this.buckets != null);
return this.buckets[bucketId].getBucketOwners();
}
/**
* Return the set of buckets which have storage assigned
*
* @return set of Integer bucketIds
*/
public Set<Integer> getBucketSet() {
Assert.assertTrue(this.buckets != null);
return new BucketSet();
}
public ProxyBucketRegion[] getProxyBucketArray() {
return this.buckets;
}
private class BucketSet extends AbstractSet {
final ProxyBucketRegion[] pbrs;
public BucketSet() {
this.pbrs = RegionAdvisor.this.buckets;
Assert.assertTrue(this.pbrs != null);
}
@Override
public Object[] toArray() {
// A somewhat wasteful impl. but required because the size is not fixed
ArrayList ar = new ArrayList(this.pbrs.length);
try {
for (Iterator e = iterator(); e.hasNext();) {
ar.add(e.next());
}
} catch (NoSuchElementException allDone) {
}
return ar.toArray();
}
@Override
public Object[] toArray(Object p_a[]) {
Object a[] = p_a;
// Some what wasteful, but needed because size is not fixed
Object[] oa = toArray();
if (a.length < oa.length) {
a = (Object[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(),
oa.length);
System.arraycopy(oa, 0, a, 0, oa.length);
}
for (int i = 0; i < oa.length; i++)
a[i] = oa[i];
if (a.length > oa.length)
a[oa.length] = null;
return a;
}
/*
* 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 this.pbrs.length;
}
@Override
public Iterator iterator() {
return new BucketSetIterator();
}
class BucketSetIterator implements Iterator {
private int currentItem = -1;
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.
*/
public boolean hasNext() {
if (getPartitionedRegion().isFixedPartitionedRegion()) {
if (this.currentItem + 1 < BucketSet.this.pbrs.length) {
int possibleBucketId = this.currentItem;
boolean bucketExists = false;
List<FixedPartitionAttributesImpl> fpaList = adviseAllFixedPartitionAttributes();
List<FixedPartitionAttributesImpl> localFpas =
getPartitionedRegion().getFixedPartitionAttributesImpl();
if (localFpas != null) {
fpaList.addAll(localFpas);
}
while (++possibleBucketId < BucketSet.this.pbrs.length && !bucketExists) {
for (FixedPartitionAttributesImpl fpa : fpaList) {
if (fpa.hasBucket(possibleBucketId)) {
bucketExists = true;
break;
}
}
}
return bucketExists;
} else {
return false;
}
} else {
return this.currentItem + 1 < BucketSet.this.pbrs.length;
}
}
public Object next() {
if (++this.currentItem < BucketSet.this.pbrs.length) {
if (isStorageAssignedForBucket(this.currentItem)) {
return Integer.valueOf(this.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(this.currentItem)) {
bucketExists = true;
break;
}
}
if (!bucketExists) {
this.currentItem++;
}
} while (this.currentItem < BucketSet.this.pbrs.length && !bucketExists);
if (bucketExists) {
getPartitionedRegion().createBucket(this.currentItem, 0, null);
return Integer.valueOf(this.currentItem);
}
} else {
getPartitionedRegion().createBucket(this.currentItem, 0, null);
return Integer.valueOf(this.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<InternalDistributedMember, Integer>();
for (int i = 0; i < this.buckets.length; i++) {
ProxyBucketRegion pbr = this.buckets[i];
// quick dirty check
InternalDistributedMember p = pbr.getBucketAdvisor().basicGetPrimaryMember();
if (p != null) {
Integer count = memberToPrimaryCount.get(p);
if (count != null) {
memberToPrimaryCount.put(p, Integer.valueOf(count.intValue() + 1));
} else {
memberToPrimaryCount.put(p, Integer.valueOf(1));
}
}
}
final ArrayList<DataStoreBuckets> ds = new ArrayList<DataStoreBuckets>(memberFilter.size());
adviseFilter(new Filter() {
public boolean include(Profile 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.intValue();
}
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 {}", this.toStringWithProfiles());
}
// 1st dump all profiles for this RegionAdvisor
super.dumpProfiles(infoMsg);
// 2nd dump all profiles for each BucketAdvisor
ProxyBucketRegion[] pbrs = this.buckets;
if (pbrs == null) {
return;
}
for (int i = 0; i < pbrs.length; i++) {
pbrs[i].getBucketAdvisor().dumpProfiles(infoMsg);
BucketPersistenceAdvisor persistentAdvisor = pbrs[i].getPersistenceAdvisor();
if (persistentAdvisor != null) {
persistentAdvisor.dump(infoMsg);
}
}
}
public void notPrimary(int bucketId, InternalDistributedMember wasPrimary) {
Assert.assertTrue(this.buckets != null);
ProxyBucketRegion b = this.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(this.buckets != null);
ProxyBucketRegion[] bucs = this.buckets;
HashSet 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 static 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 = this.buckets;
Assert.assertTrue(bucs != null);
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
*/
public void putBucketProfile(int bucketId, BucketProfile profile) {
synchronized (this.preInitQueueMonitor) {
if (this.preInitQueue != null) {
// Queue profile during pre-initialization
QueuedBucketProfile qbf = new QueuedBucketProfile(bucketId, profile);
this.preInitQueue.add(qbf);
return;
}
}
// Directly process profile post-initialization
getBucket(bucketId).getBucketAdvisor().putProfile(profile);
}
static class QueuedBucketProfile {
protected final int bucketId;
protected 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 */
protected final boolean isRemoval;
/** true means that the peer crashed */
protected final boolean crashed;
/**
* true means that this QueuedBucketProfile was created because of MembershipListener invocation
*/
protected final boolean fromMembershipListener;
protected final boolean destroyed;
protected final InternalDistributedMember memberId;
protected final int serials[];
/**
* Queue up an addition
*
* @param bId the bucket being added
* @param p the profile to add
*/
public QueuedBucketProfile(int bId, BucketProfile p) {
this.bucketId = bId;
this.bucketProfile = p;
this.isRemoval = false;
this.crashed = false;
this.memberDeparted = false;
this.memberId = null;
this.serials = null;
this.destroyed = false;
this.fromMembershipListener = false;
}
/**
* Queue up a removal due to member leaving the view
*
* @param mbr the member being removed
*/
public QueuedBucketProfile(InternalDistributedMember mbr, boolean crashed, boolean destroyed,
boolean fromMembershipListener) {
this.bucketId = 0;
this.bucketProfile = null;
this.isRemoval = true;
this.crashed = crashed;
this.memberDeparted = true;
this.memberId = mbr;
this.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
*/
public QueuedBucketProfile(InternalDistributedMember mbr, int serials[], boolean destroyed) {
this.bucketId = 0;
this.bucketProfile = null;
this.isRemoval = true;
this.crashed = false;
this.memberDeparted = false;
this.memberId = mbr;
this.serials = serials;
this.destroyed = destroyed;
this.fromMembershipListener = false;
}
}
public Set adviseBucketProfileExchange() {
return adviseDataStore();
}
public long adviseTotalMemoryAllocation() {
final AtomicLong total = new AtomicLong();
adviseFilter(new Filter() {
public boolean include(Profile 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();
}
public long adviseTotalMemoryAllocationForFPR() {
final AtomicLong total = new AtomicLong();
adviseFilter(new Filter() {
public boolean include(Profile 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) {
total.addAndGet(p.localMaxMemory);
}
}
return false;
}
});
return total.get();
}
/**
* Returns true if there are any buckets created anywhere in the distributed system for this
* partitioned region.
*/
public boolean hasCreatedBuckets() {
final ProxyBucketRegion[] bucs = this.buckets;
if (bucs != null) {
for (int i = 0; i < bucs.length; i++) {
if (bucs[i].getBucketOwnersCount() > 0) {
return true;
}
}
}
return false;
}
/**
* 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 = this.buckets;
if (bucs == null) {
return 0;
}
int createdBucketsCount = 0;
for (int i = 0; i < bucs.length; i++) {
if (bucs[i].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 = this.buckets;
if (bucs == null) {
return null;
}
ArrayList 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 l) {
int size = l.size();
for (int i = 0; i < size; i++) {
BucketProfileAndId bp = (BucketProfileAndId) l.get(i);
int id = bp.getId();
getBucket(id).getBucketAdvisor().putProfile(bp.getBucketProfile());
}
}
@Override
protected void profileRemoved(Profile profile) {
if (logger.isDebugEnabled()) {
logger.debug("RA: removing profile {}", profile);
}
if (getAdvisee() instanceof PartitionedRegion) {
((PartitionedRegion) getAdvisee()).removeMemberFromCriticalList(profile.peerMemberId);
}
if (this.buckets != null) {
for (int i = 0; i < this.buckets.length; i++) {
this.buckets[i].getBucketAdvisor().checkForLostPrimaryElector(profile);
}
}
}
public void addPersistenceListener(PersistentStateListener listener) {
for (int i = 0; i < buckets.length; i++) {
PersistenceAdvisor advisor = buckets[i].getPersistenceAdvisor();
if (advisor != null) {
advisor.addListener(listener);
}
}
}
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 this.id;
}
public BucketProfile getBucketProfile() {
return this.bp;
}
public void fromData(DataInput in) throws IOException, ClassNotFoundException {
this.id = in.readInt();
this.isServerBucketProfile = in.readBoolean();
if (this.isServerBucketProfile)
this.bp = new ServerBucketProfile();
else
this.bp = new BucketProfile();
InternalDataSerializer.invokeFromData(this.bp, in);
}
public void toData(DataOutput out) throws IOException {
out.writeInt(this.id);
out.writeBoolean(this.isServerBucketProfile);
InternalDataSerializer.invokeToData(this.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;
public void waitForChange() {
Region pr = getPartitionedRegion();
synchronized (this) {
while (!profileChanged && pr != null && !pr.isDestroyed()) {
// the advisee might have been destroyed due to initialization failure
try {
this.wait(1000);
} catch (InterruptedException e) {
}
}
this.profileChanged = false;
}
}
public void profileCreated(Profile profile) {
profileUpdated(profile);
}
public void profileRemoved(Profile profile, boolean regionDestroyed) {
// if a profile is gone, notify
synchronized (this) {
this.profileChanged = true;
this.notifyAll();
}
}
public void profileUpdated(Profile profile) {
// when updated, notify the loop in GFC to check the list again
synchronized (this) {
this.profileChanged = true;
this.notifyAll();
}
}
}
}