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

com.gemstone.gemfire.internal.cache.PartitionRegionConfigValidator Maven / Gradle / Ivy

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

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import com.gemstone.gemfire.cache.AttributesFactory;
import com.gemstone.gemfire.cache.DataPolicy;
import com.gemstone.gemfire.cache.DuplicatePrimaryPartitionException;
import com.gemstone.gemfire.cache.EvictionAttributes;
import com.gemstone.gemfire.cache.FixedPartitionAttributes;
import com.gemstone.gemfire.cache.PartitionAttributes;
import com.gemstone.gemfire.cache.RegionAttributes;
import com.gemstone.gemfire.cache.Scope;
import com.gemstone.gemfire.cache.asyncqueue.internal.AsyncEventQueueImpl;
import com.gemstone.gemfire.cache.hdfs.internal.HDFSStoreFactoryImpl;
import com.gemstone.gemfire.cache.partition.PartitionNotAvailableException;
import com.gemstone.gemfire.internal.Assert;
import com.gemstone.gemfire.internal.i18n.LocalizedStrings;

/**
 * 
 * @author ymahajan
 * 
 */
public class PartitionRegionConfigValidator {

  private final PartitionedRegion pr;

  // Incompatible LRU memory eviction attributes maximum message fragment, for
  // tests
  public static final String EVICTION_ATTRIBUTE_MAXIMUM_MEMORY_MESSAGE = " the Eviction Attribute for maximum memory, ";

  // Incompatible LRU entry eviction attributes maximum message fragment, for
  // tests
  public static final String EVICTION_ATTRIBUTE_MAXIMUM_ENTRIES_MESSAGE = " the Eviction Attribute for maximum entries, ";

  // Incompatible eviction attributes exception message fragment, for tests
  public static final String EVICTION_ATTRIBUTES_ARE_INCOMPATIBLE_MESSAGE = " is incompatible with other VMs which have EvictionAttributes ";

  public PartitionRegionConfigValidator(PartitionedRegion pr) {
    this.pr = pr;
  }

  /**
   * This method validates the PartitionedAttributes that user provided PR
   * Attributes with PR Attributes set in PR Config obtained from global
   * meta-data allPartitionedRegion region
   */
  void validatePartitionAttrsFromPRConfig(PartitionRegionConfig prconf) {
    final PartitionAttributes prconfPA = prconf.getPartitionAttrs();
    final PartitionAttributes userPA = pr.getAttributes()
        .getPartitionAttributes();

    if (userPA.getTotalSize() != prconfPA.getTotalSize()) {
      throw new IllegalStateException(
          LocalizedStrings.PartitionedRegion_TOTAL_SIZE_IN_PARTITIONATTRIBUTES_IS_INCOMPATIBLE_WITH_GLOBALLY_SET_TOTAL_SIZE_SET_THE_TOTAL_SIZE_TO_0MB
              .toLocalizedString(Long.valueOf(prconfPA.getTotalSize())));
    }
    if (userPA.getRedundantCopies() != prconfPA.getRedundantCopies()) {
      throw new IllegalStateException(
          LocalizedStrings.PartitionedRegion_REQUESTED_REDUNDANCY_0_IS_INCOMPATIBLE_WITH_EXISTING_REDUNDANCY_1
              .toLocalizedString(new Object[] {
                  Integer.valueOf(userPA.getRedundantCopies()),
                  Integer.valueOf(prconfPA.getRedundantCopies()) }));
    }

    if (prconf.isFirstDataStoreCreated() && pr.isDataStore()) {
      validateDistributedEvictionAttributes(prconf.getEvictionAttributes());
    }

    Scope prconfScope = prconf.getScope();
    Scope myScope = pr.getScope();
    if (!myScope.equals(prconfScope)) {
      throw new IllegalStateException(
          LocalizedStrings.PartitionedRegion_SCOPE_IN_PARTITIONATTRIBUTES_IS_INCOMPATIBLE_WITH_ALREADY_SET_SCOPESET_THE_SCOPE_TO_0
              .toLocalizedString(prconfScope));
    }

    final int prconfTotalNumBuckets = prconfPA.getTotalNumBuckets();
    if (userPA.getTotalNumBuckets() != prconfTotalNumBuckets) {
      throw new IllegalStateException(
          LocalizedStrings.PartitionedRegion_THE_TOTAL_NUMBER_OF_BUCKETS_FOUND_IN_PARTITIONATTRIBUTES_0_IS_INCOMPATIBLE_WITH_THE_TOTAL_NUMBER_OF_BUCKETS_USED_BY_OTHER_DISTRIBUTED_MEMBERS_SET_THE_NUMBER_OF_BUCKETS_TO_1
              .toLocalizedString(new Object[] {
                  Integer.valueOf(userPA.getTotalNumBuckets()),
                  Integer.valueOf(prconfTotalNumBuckets) }));
    }
     validatePartitionListeners(prconf, userPA);
     validatePartitionResolver(prconf, userPA);
     validateColocatedWith(prconf, userPA);
     validateExpirationAttributes(pr.getAttributes(), prconf);
  }
  
  private void validatePartitionListeners(final PartitionRegionConfig prconf,
      final PartitionAttributes userPA) {

    ArrayList prconfList = prconf.getPartitionListenerClassNames();

    if (userPA.getPartitionListeners() == null
        && userPA.getPartitionListeners().length == 0 && prconfList != null) {
      throw new IllegalStateException(
          LocalizedStrings.PartitionRegionConfigValidator_INCOMPATIBLE_PARTITION_LISTENER
              .toLocalizedString(new Object[] { null, prconfList }));
    }
    if (userPA.getPartitionListeners() != null && prconfList != null) {
      ArrayList userPRList = new ArrayList();
      for (int i = 0; i < userPA.getPartitionListeners().length; i++) {
        userPRList.add(userPA.getPartitionListeners()[i].getClass().getName());
      }

      if (userPA.getPartitionListeners().length != prconfList.size()) {
        throw new IllegalStateException(
            LocalizedStrings.PartitionRegionConfigValidator_INCOMPATIBLE_PARTITION_LISTENER
                .toLocalizedString(new Object[] { userPRList, prconfList }));
      }

      for (String listener : prconfList) {
        if (!(userPRList.contains(listener))) {
          throw new IllegalStateException(
              LocalizedStrings.PartitionRegionConfigValidator_INCOMPATIBLE_PARTITION_LISTENER
                  .toLocalizedString(new Object[] { userPRList, prconfList }));
        }
      }
    }
  }

  private void validatePartitionResolver(final PartitionRegionConfig prconf,
      final PartitionAttributes userPA) {
    /* if (userPA.getPartitionResolver() == null
        && prconf.getPartitionResolverClassName() != null) {
      throw new IllegalStateException(
          LocalizedStrings.PartitionRegionConfigValidator_INCOMPATIBLE_PARTITION_RESOLVER
              .toLocalizedString(new Object[] { "null",
                  prconf.getPartitionResolverClassName() }));
    }*/ 
	if (userPA.getPartitionResolver() != null
        && prconf.getResolverClassName() != null) {
      if (!(prconf.getResolverClassName().equals(userPA
          .getPartitionResolver().getClass().getName()))) {
        throw new IllegalStateException(
            LocalizedStrings.PartitionRegionConfigValidator_INCOMPATIBLE_PARTITION_RESOLVER
                .toLocalizedString(new Object[] {
                    userPA.getPartitionResolver().getClass().getName(),
                    prconf.getResolverClassName() }));
      }
    }
  }

  private void validateColocatedWith(final PartitionRegionConfig prconf,
      final PartitionAttributes userPA) {
    if (userPA.getColocatedWith() == null && prconf.getColocatedWith() != null) {
      throw new IllegalStateException(
          LocalizedStrings.PartitionRegionConfigValidator_INCOMPATIBLE_COLOCATED_WITH
              .toLocalizedString(new Object[] { "null",
                  prconf.getColocatedWith() }));

    }
    if (userPA.getColocatedWith() != null && prconf.getColocatedWith() != null) {
      if (!(prconf.getColocatedWith().equals(userPA.getColocatedWith()))) {
        throw new IllegalStateException(
            LocalizedStrings.PartitionRegionConfigValidator_INCOMPATIBLE_COLOCATED_WITH
                .toLocalizedString(new Object[] { userPA.getColocatedWith(),
                    prconf.getColocatedWith() }));
      }
    }
  }

  private void validateExpirationAttributes(final RegionAttributes userRA,
      final PartitionRegionConfig prconf) {
    //GemFireXD allows changing expiration attributes with ALTER TABLE. This will mismatch
    //with the configuration on other members during DDLReplay of Create Table on restart 
    //of a node. defect #49728 has more details. 
    if (!GemFireCacheImpl.gfxdSystem()) {
      if (!userRA.getRegionIdleTimeout().equals(prconf.getRegionIdleTimeout())) {
        throw new IllegalStateException(
            LocalizedStrings.PartitionRegionConfigValidator_INCOMPATIBLE_EXPIRATION_ATTRIBUETS
                .toLocalizedString(new Object[] { " region idle timout " }));
      }
      if (!userRA.getRegionTimeToLive().equals(prconf.getRegionTimeToLive())) {
        throw new IllegalStateException(
            LocalizedStrings.PartitionRegionConfigValidator_INCOMPATIBLE_EXPIRATION_ATTRIBUETS
                .toLocalizedString(new Object[] { " region time to live " }));
      }
      if (!userRA.getEntryIdleTimeout().equals(prconf.getEntryIdleTimeout())) {
        throw new IllegalStateException(
            LocalizedStrings.PartitionRegionConfigValidator_INCOMPATIBLE_EXPIRATION_ATTRIBUETS
                .toLocalizedString(new Object[] { " entry idle timout " }));
      }
      if (!userRA.getEntryTimeToLive().equals(prconf.getEntryTimeToLive())) {
        throw new IllegalStateException(
            LocalizedStrings.PartitionRegionConfigValidator_INCOMPATIBLE_EXPIRATION_ATTRIBUETS
                .toLocalizedString(new Object[] { " entry time to live " }));
      }
    }
  }
  
  /**
   * The 2nd step of Eviction Attributes validation to ensure that all VMs are
   * reasonably similar to prevent weird config. issues.
   * 
   * @param prconfEa
   *                the eviction attributes currently used by other VMs
   * @see AttributesFactory#validateAttributes(RegionAttributes)
   * @see #validateEvictionAttributesAgainstLocalMaxMemory()
   */
  private void validateDistributedEvictionAttributes(
      final EvictionAttributes prconfEa) {
    final EvictionAttributes ea = pr.getAttributes().getEvictionAttributes();
    // there is no such thing as null EvictionAttributes, assert that is true
    Assert.assertTrue(ea != null);
    Assert.assertTrue(prconfEa != null);

    // Enforce that all VMs with this PR have the same Eviction Attributes
    // Even an accessor should do this to stay consistent with all other
    // accessor validation/enforcement
    // *and* because an accessor can set the first/global eviction attributes
    // If this is an accessor with Evicitonttributes, log an info message
    // indicating that no eviction will
    // occur in this VM (duh)
    // Further validation should occur for datastores to ensure consistent
    // behavior wrt local max memory and
    // total number of buckets
    final boolean equivAlgoAndAction = ea.getAlgorithm().equals(
        prconfEa.getAlgorithm())
        && ea.getAction().equals(prconfEa.getAction());

    if (!equivAlgoAndAction) {
      throw new IllegalStateException("For Partitioned Region "
          + pr.getFullPath() + " the configured EvictionAttributes " + ea
          + EVICTION_ATTRIBUTES_ARE_INCOMPATIBLE_MESSAGE + prconfEa);
    }
    else {
      // Same algo, action...
      // It is ok to have disparate heap or memory sizes since different
      // VMs may have different heap or memory sizes, particularly if
      // the action is overflow, but...
      // It *is* dangerous to locally destroy entries if all VMs don't have the
      // same maximum
      // basically the VM with the smallest maximum may cause erroneous misses
      // to occur. Warn the user,
      // but allow the configuration.
      if (ea.getAction().isLocalDestroy()) {
        // LRUHeap doesn't support maximum, but other eviction algos do
        if (! ea.getAlgorithm().isLRUHeap() && ea.getMaximum() != prconfEa.getMaximum()) {
          pr.logger.warning(LocalizedStrings.
              PartitionedRegion_0_EVICTIONATTRIBUTES_1_DO_NOT_MATCH_WITH_OTHER_2,
              new Object[] {pr.getFullPath(), ea, prconfEa});
        }
      }
    } // end Same algo, action...
  }

   /**
   * The 3rd step of EvictionAttributes validation, where mutation is acceptible
   * This should be done before buckets are created. Validate EvictionAttributes
   * with respect to localMaxMemory potentially changing the eviction
   * attributes.
   * 
   * @see AttributesFactory#validateAttributes(RegionAttributes)
   * @see #validateDistributedEvictionAttributes(EvictionAttributes)
   */
  void validateEvictionAttributesAgainstLocalMaxMemory() {
    final EvictionAttributes ea = pr.getEvictionAttributes();
    if (pr.getLocalMaxMemory()==0 && !ea.getAction().isNone()) {
      // This is an accessor which won't ever do eviction, say so
      pr.logger.info( LocalizedStrings.
          PartitionedRegion_EVICTIONATTRIBUTES_0_WILL_HAVE_NO_EFFECT_1_2,
          new Object[] { ea, pr.getFullPath(), Integer.valueOf(pr.localMaxMemory)});
    }
  }

  /**
   * validates the persistence for datastores should match
   * between members
   */
  void validatePersistentMatchBetweenDataStores(PartitionRegionConfig prconf) {
    final boolean isPersistent = pr.getAttributes().getDataPolicy() == DataPolicy.PERSISTENT_PARTITION;
    if (pr.getLocalMaxMemory()==0 || prconf==null) {
      return;
    }
    GemFireCacheImpl cache = this.pr.getCache();
    Set nodes = prconf.getNodes();
    Iterator itor = nodes.iterator();
    while (itor.hasNext()) {
      Node n = (Node)itor.next();
      if (n.getPRType() != Node.ACCESSOR_DATASTORE) {
        continue;
      }
      // skip validation for uninitialized members (#48805)
      else if (cache.isUnInitializedMember(n.getMemberId())) {
        continue;
      }
      else {
        if (n.isPersistent() != (pr.getAttributes().getDataPolicy() == DataPolicy.PERSISTENT_PARTITION)) {
          throw new IllegalStateException(
              "DataPolicy for Datastore members should all be persistent or not.");
        }
      }
    }
  }

  void validateColocation() {
    final PartitionAttributesImpl userPA = (PartitionAttributesImpl) pr.getAttributes()
        .getPartitionAttributes();
    
    userPA.validateColocation(); // do this here to fix bug 47197

    PartitionedRegion colocatedPR = ColocationHelper.getColocatedRegion(pr);
    if (colocatedPR != null) {
      if (colocatedPR.getPartitionAttributes().getTotalNumBuckets()!= userPA.getTotalNumBuckets()){
        throw new IllegalStateException(
            "Colocated regions should have same number of total-num-buckets");
      }
      if (colocatedPR.getPartitionAttributes().getRedundantCopies()!= userPA.getRedundantCopies()){
        throw new IllegalStateException(
            "Colocated regions should have same number of redundant-copies");
      }
      if ((colocatedPR.getPartitionAttributes().getLocalMaxMemory() == 0)
          && (userPA.getLocalMaxMemory() != 0)) {
        throw new IllegalStateException(
            "Colocated regions should have accessors at the same node");
      }
      if ((colocatedPR.getLocalMaxMemory() != 0)
          && (userPA.getLocalMaxMemory() == 0)) {
        throw new IllegalStateException(
            "Colocated regions should have accessors at the same node");
      }
      if (!pr.isShadowPR()) {
        if (pr.getAttributes().getDataPolicy().withPersistence()) {
          if (!colocatedPR.getDataPolicy().withPersistence()) {
            throw new IllegalStateException(
                "Cannot colocate a persistent region with a non persistent region");
          }
        }
      }
    }
  }

  public void validateCacheLoaderWriterBetweenDataStores(
      PartitionRegionConfig prconf) {
    if (pr.getLocalMaxMemory() == 0 || prconf == null) {
      return;
    }
    GemFireCacheImpl cache = this.pr.getCache();
    Set nodes = prconf.getNodes();
    Iterator itor = nodes.iterator();
    while (itor.hasNext()) {
      Node n = (Node)itor.next();
      if (n.getPRType() != Node.ACCESSOR_DATASTORE) {
        continue;
      }
      // skip validation for uninitialized members (#48805)
      else if (cache.isUnInitializedMember(n.getMemberId())) {
        continue;
      }
      else {
        if (n.isCacheLoaderAttached()
            && pr.getAttributes().getCacheLoader() == null) {
          throw new IllegalStateException(
              LocalizedStrings.PartitionRegionConfigValidator_CACHE_LOADER_IS_NOTNULL_IN_PARTITIONED_REGION_0_ON_OTHER_DATASTORE
                  .toLocalizedString(new Object[] { this.pr.getName() }));
        }
        if (!n.isCacheLoaderAttached()
            && pr.getAttributes().getCacheLoader() != null) {
          throw new IllegalStateException(
              LocalizedStrings.PartitionRegionConfigValidator_CACHE_LOADER_IS_NULL_IN_PARTITIONED_REGION_0_ON_OTHER_DATASTORE
                  .toLocalizedString(new Object[] { this.pr.getName() }));
        }
        if (n.isCacheWriterAttached()
            && pr.getAttributes().getCacheWriter() == null) {
          throw new IllegalStateException(
              LocalizedStrings.PartitionRegionConfigValidator_CACHE_WRITER_IS_NOTNULL_IN_PARTITIONED_REGION_0_ON_OTHER_DATASTORE
                  .toLocalizedString(new Object[] { this.pr.getName() }));
        }
        if (!n.isCacheWriterAttached()
            && pr.getAttributes().getCacheWriter() != null) {
          throw new IllegalStateException(
              LocalizedStrings.PartitionRegionConfigValidator_CACHE_WRITER_IS_NULL_IN_PARTITIONED_REGION_0_ON_OTHER_DATASTORE
                  .toLocalizedString(new Object[] { this.pr.getName() }));
        }
      }
    }
  }

  void validateFixedPartitionAttributes() {
    if (this.pr.getFixedPartitionAttributesImpl() != null) {
      validatePrimaryFixedPartitionAttributes();
      validateFixedPartitionAttributesAgainstRedundantCopies();
      validateFixedPartitionAttributesAgainstTotalNumberBuckets();
    }
  }

  /**
   * validate that for all partitions defined across all datastores, sum of
   * num-buckets is not more than total-num-buckets defined
   */
  private void validateFixedPartitionAttributesAgainstTotalNumberBuckets() {
    for (FixedPartitionAttributesImpl fpa : this.pr
        .getFixedPartitionAttributesImpl()) {
      int numBuckets = 0;

      Set allFPAs = new HashSet(
          this.pr.getRegionAdvisor().adviseAllFixedPartitionAttributes());
      allFPAs.add(fpa);

      for (FixedPartitionAttributes samefpa : allFPAs) {
        numBuckets = numBuckets + samefpa.getNumBuckets();
      }
      if (numBuckets > this.pr.getTotalNumberOfBuckets()) {
        Object[] prms = new Object[] { this.pr.getName(), numBuckets,
            this.pr.getTotalNumberOfBuckets() };
        throw new IllegalStateException(
            LocalizedStrings.PartitionedRegionConfigValidator_FOR_REGION_0_SUM_OF_NUM_BUCKETS_1_FOR_DIFFERENT_PRIMARY_PARTITIONS_SHOULD_NOT_BE_GREATER_THAN_TOTAL_NUM_BUCKETS_2
                .toString(prms));
      }
    }
  }
  
  /**
   * Validate that for the given partition, number if secondaries are never
   * exceed redundant copies defined Validate that the num-buckets defined for a
   * partition are same across all datastores
   */
  private void validateFixedPartitionAttributesAgainstRedundantCopies() {
    for (FixedPartitionAttributesImpl fpa : this.pr.getFixedPartitionAttributesImpl()) {
      List allSameFPAs = this.pr.getRegionAdvisor().adviseSameFPAs(fpa);
      allSameFPAs.add(fpa);
      
      if (!allSameFPAs.isEmpty()) {
        int numSecondaries = 0;
        for (FixedPartitionAttributes otherfpa : allSameFPAs) {
          if (fpa.getNumBuckets() != otherfpa.getNumBuckets()) {
            Object[] prms = new Object[] { this.pr.getName(),
                fpa.getPartitionName(), fpa.getNumBuckets(),
                otherfpa.getNumBuckets() };
            throw new IllegalStateException(
                LocalizedStrings.PartitionedRegionConfigValidator_FOR_REGION_0_FOR_PARTITION_1_NUM_BUCKETS_ARE_NOT_SAME_ACROSS_NODES
                    .toString(prms));
          }
          
          if (!otherfpa.isPrimary()) {
            if (++numSecondaries > (this.pr.getRedundantCopies())) {
              Object[] prms = new Object[] { this.pr.getName(), numSecondaries,
                  fpa.getPartitionName(), this.pr.getRedundantCopies() };
              throw new IllegalStateException(
                  LocalizedStrings.PartitionedRegionConfigValidator_FOR_REGION_0_NUMBER_OF_SECONDARY_PARTITIONS_1_OF_A_PARTITION_2_SHOULD_NEVER_EXCEED_NUMBER_OF_REDUNDANT_COPIES_3
                      .toString(prms));
            }
          }
        }
      }
    }
  }
  
  /**
   * Validate that same partition is not defined as primary on more that one
   * datastore
   */
  private void validatePrimaryFixedPartitionAttributes() {
    List remotePrimaryFPAs = this.pr.getRegionAdvisor().adviseRemotePrimaryFPAs();
    
    for (FixedPartitionAttributes fpa : this.pr.getFixedPartitionAttributesImpl()) {
      if (fpa.isPrimary() && remotePrimaryFPAs.contains(fpa)) {
        Object[] prms = new Object[]{this.pr.getName(), fpa.getPartitionName()};
        throw new DuplicatePrimaryPartitionException(
            LocalizedStrings.PartitionedRegionConfigValidator_FOR_REGION_0_SAME_PARTITION_NAME_1_CANNOT_BE_DEFINED_AS_PRIMARY_ON_MORE_THAN_ONE_NODE
                .toString(prms));
      }
    }
  }

  /**
   * @param prconf
   */
  public void validateFixedPABetweenDataStores(PartitionRegionConfig prconf) {
    boolean isDataStore = this.pr.localMaxMemory != 0;
    boolean isFixedPR = this.pr.fixedPAttrs != null;

    GemFireCacheImpl cache = this.pr.getCache();
    Set nodes = prconf.getNodes();
    Iterator itr = nodes.iterator();
    while (itr.hasNext()) {     
      Node n = itr.next();
      // skip validation for uninitialized members (#48805)
      if (cache.isUnInitializedMember(n.getMemberId())) {
        continue;
      }
      if (isFixedPR) {
        if (n.getPRType() == Node.DATASTORE
            || n.getPRType() == Node.ACCESSOR_DATASTORE) {
          // throw exception
          Object[] prms = new Object[] { pr.getName() };
          throw new IllegalStateException(
              LocalizedStrings.PartitionedRegionConfigValidator_FIXED_PARTITION_REGION_ONE_DATASTORE_IS_WITHOUTFPA
                  .toLocalizedString(prms));
        }
      } else {
        if (isDataStore) {
          if (n.getPRType() == Node.FIXED_PR_ACCESSOR
              || n.getPRType() == Node.FIXED_PR_DATASTORE) {
            // throw Exception
            Object[] prms = new Object[] { pr.getName() };
            throw new IllegalStateException(
                LocalizedStrings.PartitionedRegionConfigValidator_FIXED_PARTITION_REGION_ONE_DATASTORE_IS_WITHOUTFPA
                    .toLocalizedString(prms));
          }
        }
      }
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy