diff --git a/modules/ducktests/README.md b/modules/ducktests/README.md
index 3d56ca33f292d..53a5d062badbe 100644
--- a/modules/ducktests/README.md
+++ b/modules/ducktests/README.md
@@ -198,6 +198,7 @@ You can modify test environments at execution time using global flags injected t
| **AppSpec** | Specifies the class to use for application specifications in Ignite applications. Controls how Ignite applications are configured and started. | ```{"AppSpec": "myapp.services.MyAppSpec"}``` |
| **IgniteTestContext** | Class name for the test context implementation. Allows customization of test context behavior. | ```{"IgniteTestContext": "myapp.context.CustomTestContext"}``` |
| **project** | Project/fork name for version handling (e.g., "ignite", "fork"). Used to distinguish between different Ignite variants. Default is "ignite". | ```{"project": "fork"}``` |
+| **mdc_cache_topology_validator** | Whether the MDC tests create their caches with the cache level `MdcTopologyValidator`. Default is True. | ```{"mdc_cache_topology_validator": false}``` |
#### Paths & Directories
diff --git a/modules/ducktests/src/main/java/org/apache/ignite/internal/ducktest/tests/mdc/MdcCacheAwareApplication.java b/modules/ducktests/src/main/java/org/apache/ignite/internal/ducktest/tests/mdc/MdcCacheAwareApplication.java
index 447486faa9b75..5a3a3fc885543 100644
--- a/modules/ducktests/src/main/java/org/apache/ignite/internal/ducktest/tests/mdc/MdcCacheAwareApplication.java
+++ b/modules/ducktests/src/main/java/org/apache/ignite/internal/ducktest/tests/mdc/MdcCacheAwareApplication.java
@@ -50,8 +50,10 @@
*
* - {@code cacheName} - cache name;
* - {@code backups} - number of backups; {@code (backups + 1)} must be divisible by {@code dcsNum};
+ * - {@code topologyValidator} - whether to set the cache level {@link MdcTopologyValidator}, default
+ * {@code true};
* - {@code mainDc} - main data center for the topology validator (2 DC mode); required, and must be
- * non-empty, unless {@code datacenters} is given;
+ * non-empty, unless {@code datacenters} is given or the cache level validator is disabled;
* - {@code datacenters} - full DC set for majority-based validation (odd DC count mode),
* takes precedence over {@code mainDc};
* - {@code dcsNum} - number of data centers, default 2;
@@ -82,6 +84,9 @@ public abstract class MdcCacheAwareApplication extends IgniteAwareApplication {
/** */
protected static final int DFLT_PARTITIONS = 512;
+ /** The cache level topology validator is set unless the parameters say otherwise. */
+ protected static final boolean DFLT_CACHE_TOP_VALIDATOR = true;
+
/** */
protected static final CacheAtomicityMode DFLT_ATOMICITY_MODE = ATOMIC;
@@ -129,6 +134,30 @@ protected CacheConfiguration mdcCacheConfiguration(JsonNode jNod
int dcsNum = jNode.path("dcsNum").asInt(DFLT_DCS_NUM);
+ CacheConfiguration cacheCfg = new CacheConfiguration()
+ .setName(cacheName)
+ .setCacheMode(cacheMode)
+ .setAtomicityMode(atomicity)
+ .setWriteSynchronizationMode(writeSync)
+ .setBackups(backups)
+ .setReadFromBackup(readFromBackup)
+ .setAffinity(new RendezvousAffinityFunction()
+ .setPartitions(partitions)
+ .setAffinityBackupFilter(new MdcAffinityBackupFilter(dcsNum, backups)));
+
+ if (jNode.path("topologyValidator").asBoolean(DFLT_CACHE_TOP_VALIDATOR))
+ cacheCfg.setTopologyValidator(mdcTopologyValidator(jNode));
+ else
+ log.info("Cache level topology validator is disabled [cache=" + cacheName + "]");
+
+ return cacheCfg;
+ }
+
+ /**
+ * @param jNode Parameters.
+ * @return Cache level topology validator compiled from the application parameters.
+ */
+ private MdcTopologyValidator mdcTopologyValidator(JsonNode jNode) {
MdcTopologyValidator topValidator = new MdcTopologyValidator();
if (jNode.hasNonNull("datacenters")) {
@@ -147,17 +176,7 @@ protected CacheConfiguration mdcCacheConfiguration(JsonNode jNod
topValidator.setMainDatacenter(mainDc);
}
- return new CacheConfiguration()
- .setName(cacheName)
- .setTopologyValidator(topValidator)
- .setCacheMode(cacheMode)
- .setAtomicityMode(atomicity)
- .setWriteSynchronizationMode(writeSync)
- .setBackups(backups)
- .setReadFromBackup(readFromBackup)
- .setAffinity(new RendezvousAffinityFunction()
- .setPartitions(partitions)
- .setAffinityBackupFilter(new MdcAffinityBackupFilter(dcsNum, backups)));
+ return topValidator;
}
/**
diff --git a/modules/ducktests/tests/ignitetest/services/mdc/mdc_cluster.py b/modules/ducktests/tests/ignitetest/services/mdc/mdc_cluster.py
index d37b9c045fa1c..84f2f1cd2610a 100644
--- a/modules/ducktests/tests/ignitetest/services/mdc/mdc_cluster.py
+++ b/modules/ducktests/tests/ignitetest/services/mdc/mdc_cluster.py
@@ -25,6 +25,11 @@
net.enable_network_partition(DC_1, DC_2)
...
+
+Globals:
+
+ mdc_cache_topology_validator - whether the MDC caches are created with the cache level
+ MdcTopologyValidator, default true.
"""
from typing import Dict, List, Optional, Union
@@ -45,6 +50,9 @@
IGNITE_STARTUP_TIMEOUT_SEC = 90
+# Global: set to false to create the MDC caches without the cache level topology validator.
+CACHE_TOP_VALIDATOR_GLOBAL = "mdc_cache_topology_validator"
+
DATA_CENTER_ATTR = "IGNITE_DATA_CENTER_ID"
IGNITE_SQL_RETRY_TIMEOUT_ATTR = "IGNITE_SQL_RETRY_TIMEOUT"
@@ -148,6 +156,13 @@ def __init__(self, test, ignite_version: str, srv_per_dc: Union[int, Dict[str, i
# Admissibility checks run on reusable services, so each check needs a unique result prefix.
self._adm_checks = 0
+ # Cache parameters applied to every cache this fixture creates, unless a call overrides them.
+ self.cache_defaults = {
+ "topologyValidator": self.test_context.globals.get(CACHE_TOP_VALIDATOR_GLOBAL, True)
+ }
+
+ self.logger.info(f"MDC cache defaults [{self.cache_defaults}]")
+
def sync_service_discovery(self):
"""
Points every server service at a discovery SPI covering all DCs.
@@ -258,12 +273,13 @@ def start_loader(self, dc: str, params: dict, loader: int = 0,
java_class: str = LOAD_APP) -> IgniteApplicationService:
"""
Starts a background load application (runs until stopped). Any exception raised
- by the application surfaces in :meth:`stop_loader`.
+ by the application surfaces in :meth:`stop_loader`. :attr:`cache_defaults` are
+ merged in.
"""
svc = self.loaders[dc][loader]
svc.java_class_name = java_class
- svc.params = params
+ svc.params = {**self.cache_defaults, **params}
svc.start(clean=self._first_start(svc))
@@ -292,10 +308,12 @@ def generate_data(self, dc: str, cache_name: str, from_idx: int, to_idx: int, ba
"""
Creates the MDC cache (if absent) and populates keys ``[from_idx, to_idx)``.
Extra cache parameters (``atomicity``, ``writeSync``, ``readFromBackup``,
- ``partitions``, ...) are passed through to the cache configuration builder.
+ ``partitions``, ...) are passed through to the cache configuration builder, on top
+ of :attr:`cache_defaults`.
"""
params = {"cacheName": cache_name, "backups": backups, "mainDc": main_dc,
- "from": from_idx, "to": to_idx, "sqlMode": sql_mode, **cache_params}
+ "from": from_idx, "to": to_idx, "sqlMode": sql_mode,
+ **self.cache_defaults, **cache_params}
return self.run_app(dc, GENERATOR_APP, params)
@@ -336,8 +354,10 @@ def run_load(self, dc: str, mode: str, cache_name: str, result_prefix: str,
"""
Runs a load burst (see ``MdcContinuousLoadApplication``) and returns the service.
``result_prefix`` must be unique per burst because runner services are reused.
+ :attr:`cache_defaults` are merged in.
"""
- load_params = {"mode": mode, "cacheName": cache_name, "resultPrefix": result_prefix, **params}
+ load_params = {"mode": mode, "cacheName": cache_name, "resultPrefix": result_prefix,
+ **self.cache_defaults, **params}
return self.run_app(dc, LOAD_APP, load_params, runner=runner)