| /* |
| * 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; |
| |
| import java.io.DataInput; |
| import java.io.DataOutput; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Properties; |
| import java.util.Set; |
| |
| import org.apache.logging.log4j.Logger; |
| |
| import org.apache.geode.DataSerializable; |
| import org.apache.geode.DataSerializer; |
| import org.apache.geode.InternalGemFireError; |
| import org.apache.geode.annotations.Immutable; |
| import org.apache.geode.annotations.internal.MutableForTesting; |
| import org.apache.geode.cache.Cache; |
| import org.apache.geode.cache.FixedPartitionAttributes; |
| import org.apache.geode.cache.PartitionAttributes; |
| import org.apache.geode.cache.PartitionAttributesFactory; |
| import org.apache.geode.cache.PartitionResolver; |
| import org.apache.geode.cache.Region; |
| import org.apache.geode.cache.partition.PartitionListener; |
| import org.apache.geode.distributed.internal.InternalDistributedSystem; |
| import org.apache.geode.internal.InternalDataSerializer; |
| import org.apache.geode.internal.offheap.OffHeapStorage; |
| import org.apache.geode.logging.internal.log4j.api.LogService; |
| |
| /** |
| * Internal implementation of PartitionAttributes. New attributes existing only in this class and |
| * not in {@link PartitionAttributes} are for internal use only. |
| * |
| * @since GemFire 5.5 |
| */ |
| public class PartitionAttributesImpl implements PartitionAttributes, Cloneable, DataSerializable { |
| private static final Logger logger = LogService.getLogger(); |
| private static final long serialVersionUID = -7120239286748961954L; |
| |
| private static final int OFF_HEAP_LOCAL_MAX_MEMORY_PLACEHOLDER = 1; |
| |
| /** Partition resolver. */ |
| private transient PartitionResolver partitionResolver; |
| private transient boolean hasPartitionResolver; |
| |
| /** |
| * the number of redundant copies to keep of each datum |
| */ |
| private int redundancy = 0; |
| private transient boolean hasRedundancy; |
| |
| /** |
| * maximum global size of the partitioned region, in megabytes |
| */ |
| private long totalMaxMemory = PartitionAttributesFactory.GLOBAL_MAX_MEMORY_DEFAULT; |
| private transient boolean hasTotalMaxMemory; |
| |
| /** |
| * local settings GLOBAL_MAX_MEMORY_PROPERTY - deprecated, use setTotalMaxMemory |
| * GLOBAL_MAX_BUCKETS_PROPERTY - deprecated, use setTotalNumBuckets |
| */ |
| private Properties localProperties = new Properties(); |
| |
| /** |
| * non-local settings LOCAL_MAX_MEMORY_PROPERTY - deprecated, use setLocalMaxMemory |
| */ |
| private Properties globalProperties = new Properties(); |
| |
| /* |
| * This is used to artificially set the amount of available off-heap memory when no distributed |
| * system is available. This value works the same way as specifying off-heap as a GemFire |
| * property, so "100m" = 100 megabytes, "100g" = 100 gigabytes, etc. |
| */ |
| @MutableForTesting |
| private static String testAvailableOffHeapMemory = null; |
| |
| /** the amount of local memory to use, in megabytes */ |
| private int localMaxMemory = PartitionAttributesFactory.LOCAL_MAX_MEMORY_DEFAULT; |
| private transient boolean hasLocalMaxMemory; |
| private transient boolean localMaxMemoryExists; |
| |
| /** |
| * Used to determine how to calculate the default local max memory. This was made transient since |
| * we do not support p2p backwards compat changes to values stored in a region and our PR |
| * implementation stores this object in the internal PRRoot internal region. |
| */ |
| private transient boolean offHeap = false; |
| private transient boolean hasOffHeap; |
| |
| /** placeholder for javadoc for this variable */ |
| private int totalNumBuckets = PartitionAttributesFactory.GLOBAL_MAX_BUCKETS_DEFAULT; |
| private transient boolean hasTotalNumBuckets; |
| |
| /** |
| * Specifies the partition region name with which this newly created partitione region is |
| * colocated |
| */ |
| private String colocatedRegionName; |
| private transient boolean hasColocatedRegionName; |
| |
| /** |
| * Specifies how long existing members will wait before recoverying redundancy |
| */ |
| private long recoveryDelay = PartitionAttributesFactory.RECOVERY_DELAY_DEFAULT; |
| private transient boolean hasRecoveryDelay; |
| /** |
| * Specifies how new members will wait before recoverying redundancy |
| */ |
| private long startupRecoveryDelay = PartitionAttributesFactory.STARTUP_RECOVERY_DELAY_DEFAULT; |
| private transient boolean hasStartupRecoveryDelay; |
| |
| private ArrayList<PartitionListener> partitionListeners; |
| private transient boolean hasPartitionListeners; |
| |
| /** |
| * the set of the static partitions defined for the region |
| */ |
| private List<FixedPartitionAttributesImpl> fixedPAttrs; |
| private transient boolean hasFixedPAttrs; |
| |
| public void setTotalNumBuckets(int maxNumberOfBuckets) { |
| this.totalNumBuckets = maxNumberOfBuckets; |
| this.globalProperties.setProperty(PartitionAttributesFactory.GLOBAL_MAX_BUCKETS_PROPERTY, |
| String.valueOf(this.totalNumBuckets)); |
| this.hasTotalNumBuckets = true; |
| } |
| |
| public void setTotalMaxMemory(long maximumMB) { |
| this.totalMaxMemory = maximumMB; |
| this.globalProperties.setProperty(PartitionAttributesFactory.GLOBAL_MAX_MEMORY_PROPERTY, |
| String.valueOf(maximumMB)); |
| this.hasTotalMaxMemory = true; |
| } |
| |
| public void setLocalMaxMemory(int maximumMB) { |
| this.localMaxMemory = maximumMB; |
| this.localProperties.setProperty(PartitionAttributesFactory.LOCAL_MAX_MEMORY_PROPERTY, |
| String.valueOf(this.localMaxMemory)); |
| this.hasLocalMaxMemory = true; |
| this.localMaxMemoryExists = true; |
| } |
| |
| public void setOffHeap(final boolean offHeap) { |
| this.offHeap = offHeap; |
| this.hasOffHeap = true; |
| if (this.offHeap && !this.hasLocalMaxMemory) { |
| this.localMaxMemory = computeOffHeapLocalMaxMemory(); |
| } |
| } |
| |
| public void setColocatedWith(String colocatedRegionFullPath) { |
| this.colocatedRegionName = colocatedRegionFullPath; |
| this.hasColocatedRegionName = true; |
| } |
| |
| public void setRecoveryDelay(long recoveryDelay) { |
| this.recoveryDelay = recoveryDelay; |
| this.hasRecoveryDelay = true; |
| } |
| |
| public void setStartupRecoveryDelay(long startupRecoveryDelay) { |
| this.startupRecoveryDelay = startupRecoveryDelay; |
| this.hasStartupRecoveryDelay = true; |
| } |
| |
| /** |
| * Constructs an instance of {@code PartitionAttributes} with default settings. |
| * |
| * @see PartitionAttributesFactory |
| */ |
| public PartitionAttributesImpl() { |
| // do nothing |
| } |
| |
| @Override |
| public PartitionResolver getPartitionResolver() { |
| return this.partitionResolver; |
| } |
| |
| public void addPartitionListener(PartitionListener listener) { |
| ArrayList<PartitionListener> listeners = this.partitionListeners; |
| if (listeners == null) { |
| ArrayList<PartitionListener> al = new ArrayList<PartitionListener>(1); |
| al.add(listener); |
| addPartitionListeners(al); |
| } else { |
| synchronized (listeners) { |
| listeners.add(listener); |
| } |
| } |
| } |
| |
| private void addPartitionListeners(ArrayList<PartitionListener> listeners) { |
| this.partitionListeners = listeners; |
| this.hasPartitionListeners = true; |
| } |
| |
| @Override |
| public int getRedundantCopies() { |
| return this.redundancy; |
| } |
| |
| @Override |
| public int getTotalNumBuckets() { |
| return this.totalNumBuckets; |
| } |
| |
| @Override |
| public long getTotalSize() { |
| return this.getTotalMaxMemory(); |
| } |
| |
| @Override |
| public long getTotalMaxMemory() { |
| return this.totalMaxMemory; |
| } |
| |
| public boolean getOffHeap() { |
| return this.offHeap; |
| } |
| |
| /** |
| * Returns localMaxMemory that must not be a temporary placeholder for offHeapLocalMaxMemory if |
| * off-heap. This must return the true final value of localMaxMemory which requires the |
| * DistributedSystem to be created if off-heap. See bug #52003. |
| * |
| * @throws IllegalStateException if off-heap and the actual value is not yet known (because the |
| * DistributedSystem has not yet been created) |
| * @see #getLocalMaxMemoryForValidation() |
| */ |
| @Override |
| public int getLocalMaxMemory() { |
| if (this.offHeap && !this.localMaxMemoryExists) { |
| int value = computeOffHeapLocalMaxMemory(); |
| if (this.localMaxMemoryExists) { |
| // real value now exists so set it and return |
| this.localMaxMemory = value; |
| } |
| } |
| checkLocalMaxMemoryExists(); |
| return this.localMaxMemory; |
| } |
| |
| /** |
| * @throws IllegalStateException if off-heap and the actual value is not yet known (because the |
| * DistributedSystem has not yet been created) |
| */ |
| private void checkLocalMaxMemoryExists() { |
| if (this.offHeap && !this.localMaxMemoryExists) { |
| // real value does NOT yet exist so throw IllegalStateException |
| throw new IllegalStateException( |
| "Attempting to use localMaxMemory for off-heap but value is not yet known (default value is equal to off-heap-memory-size)"); |
| } |
| } |
| |
| /** |
| * Returns localMaxMemory for validation of attributes before Region is created (possibly before |
| * DistributedSystem is created). Returned value may be the temporary placeholder representing |
| * offHeapLocalMaxMemory which cannot be calculated until the DistributedSystem is created. See |
| * bug #52003. |
| * |
| * @see #OFF_HEAP_LOCAL_MAX_MEMORY_PLACEHOLDER |
| * @see #getLocalMaxMemory() |
| */ |
| public int getLocalMaxMemoryForValidation() { |
| if (this.offHeap && !this.hasLocalMaxMemory && !this.localMaxMemoryExists) { |
| int value = computeOffHeapLocalMaxMemory(); |
| if (this.localMaxMemoryExists) { // real value now exists so set it and return |
| this.localMaxMemory = value; |
| } |
| } |
| return this.localMaxMemory; |
| } |
| |
| @Override |
| public String getColocatedWith() { |
| return this.colocatedRegionName; |
| } |
| |
| @Override |
| public Properties getLocalProperties() { |
| return this.localProperties; |
| } |
| |
| @Override |
| public Properties getGlobalProperties() { |
| return this.globalProperties; |
| } |
| |
| @Override |
| public long getStartupRecoveryDelay() { |
| return startupRecoveryDelay; |
| } |
| |
| @Override |
| public long getRecoveryDelay() { |
| return recoveryDelay; |
| } |
| |
| @Override |
| public List<FixedPartitionAttributesImpl> getFixedPartitionAttributes() { |
| return this.fixedPAttrs; |
| } |
| |
| @Immutable |
| private static final PartitionListener[] EMPTY_PARTITION_LISTENERS = new PartitionListener[0]; |
| |
| @Override |
| public PartitionListener[] getPartitionListeners() { |
| ArrayList<PartitionListener> listeners = this.partitionListeners; |
| if (listeners == null) { |
| return (PartitionListener[]) EMPTY_PARTITION_LISTENERS; |
| } else { |
| synchronized (listeners) { |
| if (listeners.size() == 0) { |
| return (PartitionListener[]) EMPTY_PARTITION_LISTENERS; |
| } else { |
| PartitionListener[] result = new PartitionListener[listeners.size()]; |
| listeners.toArray(result); |
| return result; |
| } |
| } |
| } |
| } |
| |
| @Override |
| public Object clone() { |
| try { |
| PartitionAttributesImpl copy = (PartitionAttributesImpl) super.clone(); |
| if (copy.fixedPAttrs != null) { |
| copy.fixedPAttrs = new ArrayList<FixedPartitionAttributesImpl>(copy.fixedPAttrs); |
| } |
| if (copy.partitionListeners != null) { |
| copy.partitionListeners = new ArrayList<PartitionListener>(copy.partitionListeners); |
| } |
| return copy; |
| } catch (CloneNotSupportedException ignore) { |
| throw new InternalGemFireError( |
| "CloneNotSupportedException thrown in class that implements cloneable"); |
| } |
| } |
| |
| public PartitionAttributesImpl copy() { |
| return (PartitionAttributesImpl) clone(); |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder(); |
| return sb.append("PartitionAttributes@").append(System.identityHashCode(this)) |
| .append("[redundantCopies=").append(getRedundantCopies()).append(";localMaxMemory=") |
| .append(getLocalMaxMemory()).append(";totalMaxMemory=").append(this.totalMaxMemory) |
| .append(";totalNumBuckets=").append(this.totalNumBuckets).append(";partitionResolver=") |
| .append(this.partitionResolver).append(";colocatedWith=").append(this.colocatedRegionName) |
| .append(";recoveryDelay=").append(this.recoveryDelay).append(";startupRecoveryDelay=") |
| .append(this.startupRecoveryDelay).append(";FixedPartitionAttributes=") |
| .append(this.fixedPAttrs).append(";partitionListeners=").append(this.partitionListeners) |
| .append("]").toString(); |
| } |
| |
| /** |
| * @throws IllegalStateException if off-heap and the actual value is not yet known (because the |
| * DistributedSystem has not yet been created) |
| */ |
| @Override |
| public void toData(DataOutput out) throws IOException { |
| checkLocalMaxMemoryExists(); |
| out.writeInt(this.redundancy); |
| out.writeLong(this.totalMaxMemory); |
| out.writeInt(getLocalMaxMemory()); // call the gettor to force it to be computed in the offheap |
| // case |
| out.writeInt(this.totalNumBuckets); |
| DataSerializer.writeString(this.colocatedRegionName, out); |
| DataSerializer.writeObject(this.localProperties, out); |
| DataSerializer.writeObject(this.globalProperties, out); |
| out.writeLong(this.recoveryDelay); |
| out.writeLong(this.startupRecoveryDelay); |
| DataSerializer.writeObject(this.fixedPAttrs, out); |
| } |
| |
| @Override |
| public void fromData(DataInput in) throws IOException, ClassNotFoundException { |
| this.redundancy = in.readInt(); |
| this.totalMaxMemory = in.readLong(); |
| this.localMaxMemory = in.readInt(); |
| this.totalNumBuckets = in.readInt(); |
| this.colocatedRegionName = DataSerializer.readString(in); |
| this.localProperties = (Properties) DataSerializer.readObject(in); |
| this.globalProperties = (Properties) DataSerializer.readObject(in); |
| this.recoveryDelay = in.readLong(); |
| this.startupRecoveryDelay = in.readLong(); |
| this.fixedPAttrs = DataSerializer.readObject(in); |
| } |
| |
| public static PartitionAttributesImpl createFromData(DataInput in) |
| throws IOException, ClassNotFoundException { |
| PartitionAttributesImpl result = new PartitionAttributesImpl(); |
| InternalDataSerializer.invokeFromData(result, in); |
| return result; |
| } |
| |
| public void setPartitionResolver(PartitionResolver partitionResolver) { |
| this.partitionResolver = partitionResolver; |
| this.hasPartitionResolver = true; |
| } |
| |
| @Override |
| public boolean equals(final Object obj) { |
| if (this == obj) { |
| return true; |
| } |
| |
| if (!(obj instanceof PartitionAttributesImpl)) { |
| return false; |
| } |
| |
| PartitionAttributesImpl other = (PartitionAttributesImpl) obj; |
| |
| if (this.redundancy != other.getRedundantCopies() |
| || getLocalMaxMemory() != other.getLocalMaxMemory() || this.offHeap != other.getOffHeap() |
| || this.totalNumBuckets != other.getTotalNumBuckets() |
| || this.totalMaxMemory != other.getTotalMaxMemory() |
| || this.startupRecoveryDelay != other.getStartupRecoveryDelay() |
| || this.recoveryDelay != other.getRecoveryDelay() |
| || ((this.partitionResolver == null) != (other.getPartitionResolver() == null)) |
| || (this.partitionResolver != null |
| && !this.partitionResolver.equals(other.getPartitionResolver())) |
| || ((this.colocatedRegionName == null) != (other.getColocatedWith() == null)) |
| || (this.colocatedRegionName != null |
| && !this.colocatedRegionName.equals(other.getColocatedWith())) |
| || ((this.fixedPAttrs == null) != (other.getFixedPartitionAttributes() == null)) |
| || (this.fixedPAttrs != null |
| && !this.fixedPAttrs.equals(other.getFixedPartitionAttributes()))) { |
| return false; |
| } |
| |
| PartitionListener[] otherPListeners = other.getPartitionListeners(); |
| PartitionListener[] thisPListeners = this.getPartitionListeners(); |
| |
| if (otherPListeners.length != thisPListeners.length) { |
| return false; |
| } |
| Set<String> otherListenerClassName = new HashSet<>(); |
| for (int i = 0; i < otherPListeners.length; i++) { |
| PartitionListener listener = otherPListeners[i]; |
| otherListenerClassName.add(listener.getClass().getName()); |
| } |
| Set<String> thisListenerClassName = new HashSet<>(); |
| for (int i = 0; i < thisPListeners.length; i++) { |
| PartitionListener listener = thisPListeners[i]; |
| thisListenerClassName.add(listener.getClass().getName()); |
| } |
| if (!thisListenerClassName.equals(otherListenerClassName)) { |
| return false; |
| } |
| |
| return true; |
| } |
| |
| @Override |
| public int hashCode() { |
| return this.getRedundantCopies(); |
| } |
| |
| public int getRedundancy() { |
| return redundancy; |
| } |
| |
| public void setRedundantCopies(int redundancy) { |
| this.redundancy = redundancy; |
| this.hasRedundancy = true; |
| } |
| |
| /** |
| * Set local properties |
| * |
| * @deprecated use {@link #setLocalMaxMemory(int)} in GemFire 5.1 and later releases |
| * @param localProps those properties for the local VM |
| */ |
| @Deprecated |
| public void setLocalProperties(Properties localProps) { |
| this.localProperties = localProps; |
| if (localProps.get(PartitionAttributesFactory.LOCAL_MAX_MEMORY_PROPERTY) != null) { |
| setLocalMaxMemory(Integer |
| .parseInt((String) localProps.get(PartitionAttributesFactory.LOCAL_MAX_MEMORY_PROPERTY))); |
| } |
| } |
| |
| /** |
| * Set global properties |
| * |
| * @deprecated use {@link #setTotalMaxMemory(long)} and {@link #setTotalNumBuckets(int)} in |
| * GemFire 5.1 and later releases |
| * @param globalProps those properties for the entire Partitioned Region |
| */ |
| @Deprecated |
| public void setGlobalProperties(Properties globalProps) { |
| this.globalProperties = globalProps; |
| String propVal = globalProps.getProperty(PartitionAttributesFactory.GLOBAL_MAX_MEMORY_PROPERTY); |
| if (propVal != null) { |
| try { |
| setTotalMaxMemory(Integer.parseInt(propVal)); |
| } catch (RuntimeException ignore) { |
| this.totalMaxMemory = PartitionAttributesFactory.GLOBAL_MAX_MEMORY_DEFAULT; |
| } |
| } |
| propVal = globalProps.getProperty(PartitionAttributesFactory.GLOBAL_MAX_BUCKETS_PROPERTY); |
| if (propVal != null) { |
| try { |
| this.setTotalNumBuckets(Integer.parseInt(propVal)); |
| } catch (RuntimeException ignore) { |
| this.totalNumBuckets = PartitionAttributesFactory.GLOBAL_MAX_BUCKETS_DEFAULT; |
| } |
| } |
| } |
| |
| public void addFixedPartitionAttributes(FixedPartitionAttributes fpa) { |
| if (this.fixedPAttrs == null) { |
| this.fixedPAttrs = new ArrayList<>(1); |
| this.fixedPAttrs.add((FixedPartitionAttributesImpl) fpa); |
| this.hasFixedPAttrs = true; |
| } else { |
| this.fixedPAttrs.add((FixedPartitionAttributesImpl) fpa); |
| } |
| } |
| |
| private void addFixedPartitionAttributes(List<FixedPartitionAttributesImpl> fpas) { |
| this.fixedPAttrs = fpas; |
| this.hasFixedPAttrs = true; |
| } |
| |
| /** |
| * Validates that the attributes are consistent with each other. The following rules are checked |
| * and enforced: |
| * <ul> |
| * <li>Redundancy should be between 1 and 4</li> |
| * <li>Scope should be either DIST_ACK or DIST_NO_ACK</li> |
| * </ul> |
| * NOTE: validation that depends on more than one attribute can not be done in this method. That |
| * validation needs to be done in validateWhenAllAttributesAreSet |
| * |
| * @throws IllegalStateException if the attributes are not consistent with each other. |
| */ |
| public void validateAttributes() { |
| if ((this.totalNumBuckets <= 0)) { |
| throw new IllegalStateException( |
| String.format( |
| "TotalNumBuckets %s is an illegal value, please choose a value greater than 0", |
| this.totalNumBuckets)); |
| } |
| if ((this.redundancy < 0) || (this.redundancy >= 4)) { |
| throw new IllegalStateException( |
| String.format( |
| "RedundantCopies %s is an illegal value, please choose a value between 0 and 3", |
| this.redundancy)); |
| } |
| for (Iterator it = this.getLocalProperties().keySet().iterator(); it.hasNext();) { |
| String propName = (String) it.next(); |
| if (!PartitionAttributesFactory.LOCAL_MAX_MEMORY_PROPERTY.equals(propName)) { |
| throw new IllegalStateException( |
| String.format("Unknown local property: '%s'", |
| propName)); |
| } |
| } |
| for (Iterator it = this.getGlobalProperties().keySet().iterator(); it.hasNext();) { |
| String propName = (String) it.next(); |
| if (!PartitionAttributesFactory.GLOBAL_MAX_BUCKETS_PROPERTY.equals(propName) |
| && !PartitionAttributesFactory.GLOBAL_MAX_MEMORY_PROPERTY.equals(propName)) { |
| throw new IllegalStateException( |
| String.format("Unknown global property: '%s'", |
| propName)); |
| } |
| } |
| if (this.recoveryDelay < -1) { |
| throw new IllegalStateException("RecoveryDelay " + this.recoveryDelay |
| + " is an illegal value, please choose a value that is greater than or equal to -1"); |
| } |
| if (this.startupRecoveryDelay < -1) { |
| throw new IllegalStateException("StartupRecoveryDelay " + this.startupRecoveryDelay |
| + " is an illegal value, please choose a value that is greater than or equal to -1"); |
| } |
| if (this.fixedPAttrs != null) { |
| List<FixedPartitionAttributesImpl> duplicateFPAattrsList = |
| new ArrayList<FixedPartitionAttributesImpl>(); |
| Set<FixedPartitionAttributes> fpAttrsSet = new HashSet<FixedPartitionAttributes>(); |
| for (FixedPartitionAttributesImpl fpa : this.fixedPAttrs) { |
| if (fpa == null || fpa.getPartitionName() == null) { |
| throw new IllegalStateException( |
| "Fixed partition name cannot be null" |
| .toString()); |
| } |
| if (fpAttrsSet.contains(fpa)) { |
| duplicateFPAattrsList.add(fpa); |
| } else { |
| fpAttrsSet.add(fpa); |
| } |
| } |
| if (duplicateFPAattrsList.size() != 0) { |
| throw new IllegalStateException( |
| String.format("Partition name %s can be added only once in FixedPartitionAttributes", |
| duplicateFPAattrsList.toString())); |
| } |
| } |
| } |
| |
| /** |
| * This validation should only be done once the region attributes that owns this pa is ready to be |
| * created. Need to do it this late because of bug 45749. |
| */ |
| public void validateWhenAllAttributesAreSet(boolean isDeclarative) { |
| if (this.colocatedRegionName != null) { |
| if (this.fixedPAttrs != null) { |
| throw new IllegalStateException( |
| String.format( |
| "FixedPartitionAttributes %s can not be specified in PartitionAttributesFactory if colocated-with is specified. ", |
| this.fixedPAttrs)); |
| } |
| } |
| if (this.fixedPAttrs != null) { |
| if (this.localMaxMemory == 0) { |
| throw new IllegalStateException( |
| String.format("FixedPartitionAttributes %s can not be defined for accessor", |
| this.fixedPAttrs)); |
| } |
| } |
| } |
| |
| /** |
| * Validates colocation of PartitionRegion <br> |
| * This method used to be called when the RegionAttributes were created. But this was too early |
| * since the region we are colocated with might not exist (yet). So it is now called when the PR |
| * using these attributes is created. See bug 47197. |
| * |
| * 1. region passed in setColocatedWith should exist.<br> |
| * 2. region passed should be of a PartitionedRegion <br> |
| * 3. Custom partitioned should be enabled for colocated regions <br> |
| * 4. totalNumBuckets should be same for colocated regions<br> |
| * 5. redundancy of colocated regions should be same<br> |
| * |
| * @since GemFire 5.8Beta |
| */ |
| void validateColocation(Cache cache) { |
| if (this.colocatedRegionName == null) { |
| return; |
| } |
| if (cache != null) { |
| Region<?, ?> region = cache.getRegion(this.colocatedRegionName); |
| if (region == null) { |
| throw new IllegalStateException( |
| "Region specified in 'colocated-with' is not present. It should be created before setting 'colocated-with' to this region."); |
| } |
| if (!(region instanceof PartitionedRegion)) { |
| throw new IllegalStateException( |
| "Setting the attribute 'colocated-with' is supported only for PartitionedRegions"); |
| } |
| PartitionedRegion colocatedRegion = (PartitionedRegion) region; |
| if (this.getTotalNumBuckets() != colocatedRegion.getPartitionAttributes() |
| .getTotalNumBuckets()) { |
| throw new IllegalStateException( |
| "Current PartitionedRegion's TotalNumBuckets should be same as TotalNumBuckets of colocated PartitionedRegion"); |
| } |
| if (this.getRedundancy() != colocatedRegion.getPartitionAttributes().getRedundantCopies()) { |
| throw new IllegalStateException( |
| "Current PartitionedRegion's redundancy should be same as the redundancy of colocated PartitionedRegion"); |
| } |
| } |
| } |
| |
| /** |
| * Added for bug 45749. The attributes in pa are merged into this. Only attributes explicitly set |
| * in pa will be merged into this. Any attribute set in pa will take precedence over an attribute |
| * in this. |
| * |
| * @param pa the attributes to merge into this. |
| * @since GemFire 7.0 |
| */ |
| public void merge(PartitionAttributesImpl pa) { |
| if (pa.hasRedundancy) { |
| setRedundantCopies(pa.getRedundantCopies()); |
| } |
| if (pa.hasLocalMaxMemory) { |
| setLocalMaxMemory(pa.getLocalMaxMemory()); |
| } |
| if (pa.hasOffHeap) { |
| setOffHeap(pa.getOffHeap()); |
| } |
| if (pa.hasTotalMaxMemory) { |
| setTotalMaxMemory(pa.getTotalMaxMemory()); |
| } |
| if (pa.hasTotalNumBuckets) { |
| setTotalNumBuckets(pa.getTotalNumBuckets()); |
| } |
| if (pa.hasPartitionResolver) { |
| setPartitionResolver(pa.getPartitionResolver()); |
| } |
| if (pa.hasColocatedRegionName) { |
| setColocatedWith(pa.getColocatedWith()); |
| } |
| if (pa.hasRecoveryDelay) { |
| setRecoveryDelay(pa.getRecoveryDelay()); |
| } |
| if (pa.hasStartupRecoveryDelay) { |
| setStartupRecoveryDelay(pa.getStartupRecoveryDelay()); |
| } |
| if (pa.hasFixedPAttrs) { |
| addFixedPartitionAttributes(pa.getFixedPartitionAttributes()); |
| } |
| if (pa.hasPartitionListeners) { |
| this.addPartitionListeners(pa.partitionListeners); |
| } |
| } |
| |
| @SuppressWarnings("unchecked") |
| public void setAll(PartitionAttributes pa) { |
| setRedundantCopies(pa.getRedundantCopies()); |
| setLocalProperties(pa.getLocalProperties()); |
| setGlobalProperties(pa.getGlobalProperties()); |
| setLocalMaxMemory(pa.getLocalMaxMemory()); |
| setTotalMaxMemory(pa.getTotalMaxMemory()); |
| setTotalNumBuckets(pa.getTotalNumBuckets()); |
| setPartitionResolver(pa.getPartitionResolver()); |
| setColocatedWith(pa.getColocatedWith()); |
| setRecoveryDelay(pa.getRecoveryDelay()); |
| setStartupRecoveryDelay(pa.getStartupRecoveryDelay()); |
| setOffHeap(((PartitionAttributesImpl) pa).getOffHeap()); |
| addFixedPartitionAttributes(pa.getFixedPartitionAttributes()); |
| } |
| |
| /** |
| * Only used for testing. Sets the amount of available off-heap memory when no distributed system |
| * is available. This method must be called before any instances of PartitionAttributesImpl are |
| * created. Specify the value the same way the off-heap memory property is specified. So, "100m" = |
| * 100 megabytes, etc. |
| * |
| * @param newTestAvailableOffHeapMemory The new test value for available off-heap memory. |
| */ |
| public static void setTestAvailableOffHeapMemory(final String newTestAvailableOffHeapMemory) { |
| testAvailableOffHeapMemory = newTestAvailableOffHeapMemory; |
| } |
| |
| /** |
| * By default the partition can use up to 100% of the allocated off-heap memory. |
| */ |
| private int computeOffHeapLocalMaxMemory() { |
| |
| long availableOffHeapMemoryInMB = 0; |
| if (testAvailableOffHeapMemory != null) { |
| availableOffHeapMemoryInMB = |
| OffHeapStorage.parseOffHeapMemorySize(testAvailableOffHeapMemory) / (1024 * 1024); |
| } else if (InternalDistributedSystem.getAnyInstance() == null) { |
| this.localMaxMemoryExists = false; |
| // fix 52033: return non-negative, non-zero temporary placeholder for offHeapLocalMaxMemory |
| return OFF_HEAP_LOCAL_MAX_MEMORY_PLACEHOLDER; |
| } else { |
| String offHeapSizeConfigValue = |
| InternalDistributedSystem.getAnyInstance().getOriginalConfig().getOffHeapMemorySize(); |
| availableOffHeapMemoryInMB = |
| OffHeapStorage.parseOffHeapMemorySize(offHeapSizeConfigValue) / (1024 * 1024); |
| } |
| |
| if (availableOffHeapMemoryInMB > Integer.MAX_VALUE) { |
| logger.warn( |
| "Reduced local max memory for partition attribute when setting from available off-heap memory size"); |
| return Integer.MAX_VALUE; |
| } |
| |
| this.localMaxMemoryExists = true; |
| return (int) availableOffHeapMemoryInMB; |
| } |
| |
| public int getLocalMaxMemoryDefault() { |
| if (!this.offHeap) { |
| return PartitionAttributesFactory.LOCAL_MAX_MEMORY_DEFAULT; |
| } |
| |
| return computeOffHeapLocalMaxMemory(); |
| } |
| } |