You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ag...@apache.org on 2019/07/30 11:53:09 UTC
[ignite] branch master updated: IGNITE-7883 Cluster can have
inconsistent affinity configuration.
This is an automated email from the ASF dual-hosted git repository.
agura pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git
The following commit(s) were added to refs/heads/master by this push:
new 1ea1505 IGNITE-7883 Cluster can have inconsistent affinity configuration.
1ea1505 is described below
commit 1ea1505872b0dce8386ca3975a346fbfd2482dad
Author: a-polyakov <po...@gmail.com>
AuthorDate: Tue Jul 30 14:52:34 2019 +0300
IGNITE-7883 Cluster can have inconsistent affinity configuration.
Signed-off-by: Andrey Gura <ag...@apache.org>
---
.../apache/ignite/cache/CacheKeyConfiguration.java | 26 ++
.../processors/cache/ClusterCachesInfo.java | 3 +
.../internal/processors/cache/GridCacheUtils.java | 152 +++++++++--
.../CacheAffinityKeyConfigurationMismatchTest.java | 287 +++++++++++++++++++++
.../ignite/testsuites/IgniteCacheTestSuite.java | 2 +
5 files changed, 451 insertions(+), 19 deletions(-)
diff --git a/modules/core/src/main/java/org/apache/ignite/cache/CacheKeyConfiguration.java b/modules/core/src/main/java/org/apache/ignite/cache/CacheKeyConfiguration.java
index 33e5881..2fcf232 100644
--- a/modules/core/src/main/java/org/apache/ignite/cache/CacheKeyConfiguration.java
+++ b/modules/core/src/main/java/org/apache/ignite/cache/CacheKeyConfiguration.java
@@ -19,6 +19,7 @@ package org.apache.ignite.cache;
import java.io.Serializable;
import java.lang.reflect.Field;
+import java.util.Objects;
import org.apache.ignite.cache.affinity.AffinityKeyMapped;
import org.apache.ignite.internal.util.typedef.internal.S;
@@ -111,6 +112,31 @@ public class CacheKeyConfiguration implements Serializable {
}
/** {@inheritDoc} */
+ @Override public boolean equals(Object o) {
+ if (this == o)
+ return true;
+
+ if (o == null || getClass() != o.getClass())
+ return false;
+
+ CacheKeyConfiguration that = (CacheKeyConfiguration)o;
+
+ if (!Objects.equals(typeName, that.typeName))
+ return false;
+
+ return Objects.equals(affKeyFieldName, that.affKeyFieldName);
+ }
+
+ /** {@inheritDoc} */
+ @Override public int hashCode() {
+ int result = typeName != null ? typeName.hashCode() : 0;
+
+ result = 31 * result + (affKeyFieldName != null ? affKeyFieldName.hashCode() : 0);
+
+ return result;
+ }
+
+ /** {@inheritDoc} */
@Override public String toString() {
return S.toString(CacheKeyConfiguration.class, this);
}
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClusterCachesInfo.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClusterCachesInfo.java
index 72ac6be..3df638d 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClusterCachesInfo.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/ClusterCachesInfo.java
@@ -381,6 +381,9 @@ class ClusterCachesInfo {
"Affinity partitions count", locAttr.affinityPartitionsCount(),
rmtAttr.affinityPartitionsCount(), true);
+ CU.validateKeyConfigiration(rmtAttr.groupName(), rmtAttr.cacheName(), rmt, rmtAttr.configuration().getKeyConfiguration(),
+ locAttr.configuration().getKeyConfiguration(), log, true);
+
CU.checkAttributeMismatch(log, rmtAttr.cacheName(), rmt, "evictionFilter", "Eviction filter",
locAttr.evictionFilterClassName(), rmtAttr.evictionFilterClassName(), true);
diff --git a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java
index cc5dd42..f6ea2ba 100644
--- a/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java
+++ b/modules/core/src/main/java/org/apache/ignite/internal/processors/cache/GridCacheUtils.java
@@ -47,6 +47,7 @@ import org.apache.ignite.IgniteCheckedException;
import org.apache.ignite.IgniteException;
import org.apache.ignite.IgniteLogger;
import org.apache.ignite.IgniteSystemProperties;
+import org.apache.ignite.cache.CacheKeyConfiguration;
import org.apache.ignite.cache.CachePartialUpdateException;
import org.apache.ignite.cache.CacheServerNotFoundException;
import org.apache.ignite.cache.QueryEntity;
@@ -984,25 +985,13 @@ public class GridCacheUtils {
assert attrName != null;
assert attrMsg != null;
- if (!F.eq(locVal, rmtVal)) {
- if (fail) {
- throw new IgniteCheckedException(attrMsg + " mismatch (fix " + attrMsg.toLowerCase() + " in cache " +
- "configuration or set -D" + IGNITE_SKIP_CONFIGURATION_CONSISTENCY_CHECK + "=true " +
- "system property) [cacheName=" + cfgName +
- ", local" + capitalize(attrName) + "=" + locVal +
- ", remote" + capitalize(attrName) + "=" + rmtVal +
- ", rmtNodeId=" + rmtNodeId + ']');
- }
- else {
- assert log != null;
-
- U.warn(log, attrMsg + " mismatch (fix " + attrMsg.toLowerCase() + " in cache " +
- "configuration) [cacheName=" + cfgName +
- ", local" + capitalize(attrName) + "=" + locVal +
- ", remote" + capitalize(attrName) + "=" + rmtVal +
- ", rmtNodeId=" + rmtNodeId + ']');
- }
- }
+ if (!F.eq(locVal, rmtVal))
+ throwIgniteCheckedException(log, fail,
+ attrMsg + " mismatch [" +
+ "cacheName=" + cfgName + ", " +
+ "local" + capitalize(attrName) + "=" + locVal + ", " +
+ "remote" + capitalize(attrName) + "=" + rmtVal + ", " +
+ "rmtNodeId=" + rmtNodeId + ']');
}
/**
@@ -1627,6 +1616,129 @@ public class GridCacheUtils {
}
/**
+ * Validate affinity key configurations.
+ * All fields are initialized and not empty (typeName and affKeyFieldName is defined).
+ * Definition for the type does not repeat.
+ *
+ * @param groupName Cache group name.
+ * @param cacheName Cache name.
+ * @param cacheKeyCfgs keyConfiguration to validate.
+ * @param log Logger used to log warning message (used only if fail flag is not set).
+ * @param fail If true throws IgniteCheckedException in case of attribute values mismatch, otherwise logs warning.
+ * @return Affinity key maps (typeName -> fieldName)
+ * @throws IgniteCheckedException In case the affinity key configurations is not valid.
+ */
+ public static Map<String, String> validateKeyConfigiration(
+ String groupName, String cacheName, CacheKeyConfiguration[] cacheKeyCfgs, IgniteLogger log,
+ boolean fail
+ ) throws IgniteCheckedException {
+ Map<String, String> keyConfigurations = new HashMap<>();
+
+ if (cacheKeyCfgs != null) {
+ for (CacheKeyConfiguration cacheKeyCfg : cacheKeyCfgs) {
+ String typeName = cacheKeyCfg.getTypeName();
+
+ A.notNullOrEmpty(typeName, "typeName");
+
+ String fieldName = cacheKeyCfg.getAffinityKeyFieldName();
+
+ A.notNullOrEmpty(fieldName, "affKeyFieldName");
+
+ String oldFieldName = keyConfigurations.put(typeName, fieldName);
+
+ if (oldFieldName != null && !oldFieldName.equals(fieldName)) {
+ final String msg = "Cache key configuration contains conflicting definitions: [" +
+ (groupName != null ? "cacheGroup=" + groupName + ", " : "") +
+ "cacheName=" + cacheName + ", " +
+ "typeName=" + typeName + ", " +
+ "affKeyFieldName1=" + oldFieldName + ", " +
+ "affKeyFieldName2=" + fieldName + "].";
+
+ throwIgniteCheckedException(log, fail, msg);
+ }
+ }
+ }
+
+ return keyConfigurations;
+ }
+
+ /**
+ * If -DIGNITE_SKIP_CONFIGURATION_CONSISTENCY_CHECK=true then output message to log else throw IgniteCheckedException.
+ *
+ * @param log Logger used to log warning message (used only if fail flag is not set).
+ * @param fail If true throws IgniteCheckedException in case of attribute values mismatch, otherwise logs warning.
+ * @param msg Message to output.
+ * @throws IgniteCheckedException
+ */
+ private static void throwIgniteCheckedException(IgniteLogger log,
+ boolean fail, String msg) throws IgniteCheckedException {
+ if (fail)
+ throw new IgniteCheckedException(msg + " Fix cache configuration or set system property -D" +
+ IGNITE_SKIP_CONFIGURATION_CONSISTENCY_CHECK + "=true.");
+ else {
+ assert log != null;
+
+ U.warn(log, msg);
+ }
+ }
+
+ /**
+ * Validate and compare affinity key configurations.
+ *
+ * @param groupName Cache group name.
+ * @param cacheName Cache name.
+ * @param rmtNodeId Remote node.
+ * @param rmtCacheKeyCfgs Exist affinity key configurations.
+ * @param locCacheKeyCfgs New affinity key configurations.
+ * @param log Logger used to log warning message (used only if fail flag is not set).
+ * @param fail If true throws IgniteCheckedException in case of attribute values mismatch, otherwise logs warning.
+ * @throws IgniteCheckedException In case the affinity key configurations is not valid.
+ */
+ public static void validateKeyConfigiration(
+ String groupName,
+ String cacheName,
+ UUID rmtNodeId,
+ CacheKeyConfiguration rmtCacheKeyCfgs[],
+ CacheKeyConfiguration locCacheKeyCfgs[],
+ IgniteLogger log,
+ boolean fail
+ ) throws IgniteCheckedException {
+ Map<String, String> rmtAffinityKeys = CU.validateKeyConfigiration(groupName, cacheName, rmtCacheKeyCfgs, log, fail);
+
+ Map<String, String> locAffinityKey = CU.validateKeyConfigiration(groupName, cacheName, locCacheKeyCfgs, log, fail);
+
+ if (rmtAffinityKeys.size() != locAffinityKey.size()) {
+ throwIgniteCheckedException(log, fail, "Affinity key configuration mismatch" +
+ "[" +
+ (groupName != null ? "cacheGroup=" + groupName + ", " : "") +
+ "cacheName=" + cacheName + ", " +
+ "remote keyConfiguration.length=" + rmtAffinityKeys.size() + ", " +
+ "local keyConfiguration.length=" + locAffinityKey.size() +
+ (rmtNodeId != null ? ", rmtNodeId=" + rmtNodeId : "") +
+ ']');
+ }
+
+ for (Map.Entry<String, String> rmtAffinityKey : rmtAffinityKeys.entrySet()) {
+ String rmtTypeName = rmtAffinityKey.getKey();
+
+ String rmtFieldName = rmtAffinityKey.getValue();
+
+ String locFieldName = locAffinityKey.get(rmtTypeName);
+
+ if (!rmtFieldName.equals(locFieldName)) {
+ throwIgniteCheckedException(log, fail, "Affinity key configuration mismatch [" +
+ (groupName != null ? "cacheGroup=" + groupName + ", " : "") +
+ "cacheName=" + cacheName + ", " +
+ "typeName=" + rmtTypeName + ", " +
+ "remote affinityKeyFieldName=" + rmtFieldName + ", " +
+ "local affinityKeyFieldName=" + locFieldName +
+ (rmtNodeId != null ? ", rmtNodeId=" + rmtNodeId : "") +
+ ']');
+ }
+ }
+ }
+
+ /**
* @param cfg Initializes cache configuration with proper defaults.
* @param cacheObjCtx Cache object context.
* @throws IgniteCheckedException If configuration is not valid.
@@ -1665,6 +1777,8 @@ public class GridCacheUtils {
}
}
+ validateKeyConfigiration(cfg.getGroupName(), cfg.getName(), cfg.getKeyConfiguration(), log, true);
+
if (cfg.getCacheMode() == REPLICATED)
cfg.setBackups(Integer.MAX_VALUE);
diff --git a/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheAffinityKeyConfigurationMismatchTest.java b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheAffinityKeyConfigurationMismatchTest.java
new file mode 100644
index 0000000..2df1b2a
--- /dev/null
+++ b/modules/core/src/test/java/org/apache/ignite/internal/processors/cache/CacheAffinityKeyConfigurationMismatchTest.java
@@ -0,0 +1,287 @@
+/*
+ * 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.ignite.internal.processors.cache;
+
+import java.util.concurrent.Callable;
+import org.apache.ignite.Ignite;
+import org.apache.ignite.IgniteCheckedException;
+import org.apache.ignite.cache.CacheAtomicityMode;
+import org.apache.ignite.cache.CacheKeyConfiguration;
+import org.apache.ignite.cache.CacheMode;
+import org.apache.ignite.cache.affinity.Affinity;
+import org.apache.ignite.cache.affinity.AffinityKeyMapped;
+import org.apache.ignite.configuration.CacheConfiguration;
+import org.apache.ignite.configuration.IgniteConfiguration;
+import org.apache.ignite.testframework.GridTestUtils;
+import org.apache.ignite.testframework.junits.common.GridCommonAbstractTest;
+import org.junit.Test;
+
+/**
+ * Testing cluster inconsistent affinity configuration.
+ * Detect difference property "keyConfiguration" in cache configuration and generate exception.
+ */
+public class CacheAffinityKeyConfigurationMismatchTest extends GridCommonAbstractTest {
+
+ /** {@inheritDoc} */
+ @Override protected IgniteConfiguration getConfiguration(String igniteInstanceName) throws Exception {
+ IgniteConfiguration cfg = super.getConfiguration(igniteInstanceName);
+
+ CacheKeyConfiguration cacheKeyCfg[];
+
+ if (getTestIgniteInstanceName(0).equals(igniteInstanceName)) {
+ cacheKeyCfg = new CacheKeyConfiguration[] {
+ new CacheKeyConfiguration(AKey.class)
+ };
+ }
+ else if (getTestIgniteInstanceName(1).equals(igniteInstanceName)) {
+ cacheKeyCfg = new CacheKeyConfiguration[] {
+ getCacheAKeyConfiguration("a")
+ };
+ }
+ else if (getTestIgniteInstanceName(2).equals(igniteInstanceName)) {
+ cacheKeyCfg = new CacheKeyConfiguration[] {
+ new CacheKeyConfiguration(AKey.class),
+ getCacheAKeyConfiguration("a")
+ };
+ }
+ else if (getTestIgniteInstanceName(3).equals(igniteInstanceName)) {
+ cacheKeyCfg = new CacheKeyConfiguration[] {
+ new CacheKeyConfiguration(AKey.class),
+ getCacheAKeyConfiguration("b")
+ };
+ }
+ else if (getTestIgniteInstanceName(4).equals(igniteInstanceName)) {
+ cacheKeyCfg = new CacheKeyConfiguration[] {
+ getCacheAKeyConfiguration("b")
+ };
+ }
+ else if (getTestIgniteInstanceName(5).equals(igniteInstanceName)) {
+ cacheKeyCfg = new CacheKeyConfiguration[] {
+ };
+ }
+ else if (getTestIgniteInstanceName(6).equals(igniteInstanceName)) {
+ cacheKeyCfg = new CacheKeyConfiguration[] {
+ new CacheKeyConfiguration(AKey.class),
+ new CacheKeyConfiguration(BKey.class)
+ };
+ }
+ else if (getTestIgniteInstanceName(7).equals(igniteInstanceName)) {
+ cacheKeyCfg = new CacheKeyConfiguration[] {
+ new CacheKeyConfiguration(BKey.class)
+ };
+ }
+ else {
+ cacheKeyCfg = null;
+ }
+
+ if (cacheKeyCfg != null) {
+ CacheConfiguration cacheCfg = new CacheConfiguration(DEFAULT_CACHE_NAME);
+
+ cacheCfg.setAtomicityMode(CacheAtomicityMode.TRANSACTIONAL);
+ cacheCfg.setCacheMode(CacheMode.PARTITIONED);
+ cacheCfg.setKeyConfiguration(cacheKeyCfg);
+
+ cfg.setCacheConfiguration(cacheCfg);
+ }
+
+ return cfg;
+ }
+
+ /**
+ * Test for matching "keyConfiguration" property.
+ *
+ * @throws Exception If test fails.
+ */
+ @Test
+ public void testKeyConfigurationMatch() throws Exception {
+ try (Ignite ignite0 = startGrid(0)) {
+ try (Ignite ignite1 = startGrid(1)) {
+ Affinity<Object> affinity0 = ignite0.affinity(DEFAULT_CACHE_NAME);
+ Affinity<Object> affinity1 = ignite1.affinity(DEFAULT_CACHE_NAME);
+
+ for (int i = 0; i < Integer.MAX_VALUE; i = i << 1 | 1) {
+ AKey aKey = new AKey(i);
+
+ assertEquals("different affinity partition for key=" + i,
+ affinity0.partition(aKey),
+ affinity1.partition(aKey)
+ );
+ }
+ }
+ }
+ }
+
+ /**
+ * Test for checking "keyConfiguration" when property has other field name for a specified name type
+ *
+ * @throws Exception If test fails.
+ */
+ @Test
+ public void testKeyConfigurationDuplicateTypeName() throws Exception {
+ try (Ignite ignite0 = startGrid(2)) {
+ try (Ignite ignite1 = startGrid(0)) {
+ }
+ }
+
+ GridTestUtils.assertThrowsAnyCause(
+ log,
+ new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ try (Ignite ignite = startGrid(3)) {
+ }
+ return null;
+ }
+ },
+ IgniteCheckedException.class, null);
+
+ GridTestUtils.assertThrowsAnyCause(
+ log,
+ new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ try (Ignite ignite0 = startGrid(0)) {
+ try (Ignite ignite1 = startGrid(4)) {
+ }
+ }
+ return null;
+ }
+ },
+ IgniteCheckedException.class, null);
+ }
+
+ /**
+ * Test when property "keyConfiguration" differs by array size.
+ *
+ * @throws Exception If test fails.
+ */
+ @Test
+ public void testKeyConfigurationLengthMismatch() throws Exception {
+ GridTestUtils.assertThrowsAnyCause(
+ log,
+ new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ try (Ignite ignite0 = startGrid(0)) {
+ try (Ignite ignite1 = startGrid(5)) {
+ }
+ }
+
+ return null;
+ }
+ },
+ IgniteCheckedException.class,
+ "Affinity key configuration mismatch"
+ );
+
+ GridTestUtils.assertThrowsAnyCause(
+ log,
+ new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ try (Ignite ignite0 = startGrid(0)) {
+ try (Ignite ignite1 = startGrid(6)) {
+ }
+ }
+
+ return null;
+ }
+ },
+ IgniteCheckedException.class,
+ "Affinity key configuration mismatch"
+ );
+ }
+
+ /**
+ * Test for checking "keyConfiguration" when property has other field name for a specified name type
+ *
+ * @throws Exception If test fails.
+ */
+ @Test
+ public void testKeyConfigurationMismatch() throws Exception {
+ GridTestUtils.assertThrowsAnyCause(
+ log,
+ new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ try (Ignite ignite0 = startGrid(0)) {
+ try (Ignite ignite1 = startGrid(4)) {
+ }
+ }
+ return null;
+ }
+ },
+ IgniteCheckedException.class,
+ "Affinity key configuration mismatch"
+ );
+
+ GridTestUtils.assertThrowsAnyCause(
+ log,
+ new Callable<Void>() {
+ @Override public Void call() throws Exception {
+ try (Ignite ignite0 = startGrid(0)) {
+ try (Ignite ignite1 = startGrid(7)) {
+ }
+ }
+ return null;
+ }
+ },
+ IgniteCheckedException.class,
+ "Affinity key configuration mismatch"
+ );
+ }
+
+ /**
+ * Creating test cache key configuration
+ *
+ * @param affKeyFieldName Affinity field name.
+ * @return Configuration.
+ */
+ private CacheKeyConfiguration getCacheAKeyConfiguration(String affKeyFieldName) {
+ return new CacheKeyConfiguration(AKey.class.getName(), affKeyFieldName);
+ }
+
+ /**
+ * Value structure for test
+ */
+ private static class AKey {
+ /** */
+ @AffinityKeyMapped
+ int a;
+
+ public AKey(int a) {
+ this.a = a;
+ }
+
+ @Override public String toString() {
+ return "AKey{a=" + a + '}';
+ }
+ }
+
+ /**
+ * Value structure for test
+ */
+ private static class BKey {
+ /** */
+ @AffinityKeyMapped
+ int b;
+
+ public BKey(int b) {
+ this.b = b;
+ }
+
+ @Override public String toString() {
+ return "BKey{b=" + b + '}';
+ }
+ }
+}
diff --git a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite.java b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite.java
index 86a4f74..2683184 100755
--- a/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite.java
+++ b/modules/core/src/test/java/org/apache/ignite/testsuites/IgniteCacheTestSuite.java
@@ -47,6 +47,7 @@ import org.apache.ignite.internal.managers.communication.IgniteIoTestMessagesTes
import org.apache.ignite.internal.managers.communication.IgniteVariousConnectionNumberTest;
import org.apache.ignite.internal.processors.cache.BinaryMetadataRegistrationInsideEntryProcessorTest;
import org.apache.ignite.internal.processors.cache.CacheAffinityCallSelfTest;
+import org.apache.ignite.internal.processors.cache.CacheAffinityKeyConfigurationMismatchTest;
import org.apache.ignite.internal.processors.cache.CacheDeferredDeleteQueueTest;
import org.apache.ignite.internal.processors.cache.CacheDeferredDeleteSanitySelfTest;
import org.apache.ignite.internal.processors.cache.CacheFutureExceptionSelfTest;
@@ -230,6 +231,7 @@ public class IgniteCacheTestSuite {
// GridTestUtils.addTestIfNeeded(suite, GridCacheP2PUndeploySelfTest.class, ignoredTests);
GridTestUtils.addTestIfNeeded(suite, GridCacheConfigurationValidationSelfTest.class, ignoredTests);
GridTestUtils.addTestIfNeeded(suite, GridCacheConfigurationConsistencySelfTest.class, ignoredTests);
+ GridTestUtils.addTestIfNeeded(suite, CacheAffinityKeyConfigurationMismatchTest.class, ignoredTests);
GridTestUtils.addTestIfNeeded(suite, GridDataStorageConfigurationConsistencySelfTest.class, ignoredTests);
GridTestUtils.addTestIfNeeded(suite, DataStorageConfigurationValidationTest.class, ignoredTests);
GridTestUtils.addTestIfNeeded(suite, CacheWithDifferentDataRegionConfigurationTest.class, ignoredTests);