This is an automated email from the ASF dual-hosted git repository.
lokiore pushed a commit to branch PHOENIX-7562-feature-new
in repository https://gitbox.apache.org/repos/asf/phoenix.git
The following commit(s) were added to refs/heads/PHOENIX-7562-feature-new by
this push:
new 123f94c6c6 PHOENIX-8038 :- Remove
CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED and always block mutations in the
ACTIVE_TO_STANDBY role (#2649)
123f94c6c6 is described below
commit 123f94c6c634028e87675e50737160f5ebf0b4fc
Author: Lokesh Khurana <[email protected]>
AuthorDate: Thu Nov 5 09:51:25 2026 -0800
PHOENIX-8038 :- Remove CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED and always
block mutations in the ACTIVE_TO_STANDBY role (#2649)
* PHOENIX-8038 :- Remove CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED and
always block mutations in the ACTIVE_TO_STANDBY role
Consistent failover is not correct without the ACTIVE_TO_STANDBY mutation
block, so remove
the property and make HAGroupStoreManager#isMutationBlocked always check
the cluster role.
The block stays gated on SYNCHRONOUS_REPLICATION_ENABLED and applies only
to HA client
batches. Drop the property from ITs and remove testMutationBlockDisabled.
Co-authored-by: Claude Opus 5.5 (1M context) <[email protected]>
---
.../exception/MutationBlockedIOException.java | 4 +-
.../apache/phoenix/jdbc/HAGroupStoreClient.java | 42 ++++-----
.../apache/phoenix/jdbc/HAGroupStoreManager.java | 101 ++++++++++++---------
.../org/apache/phoenix/query/QueryServices.java | 3 -
.../apache/phoenix/query/QueryServicesOptions.java | 4 -
.../org/apache/phoenix/schema/MetaDataClient.java | 6 +-
.../phoenix/hbase/index/IndexRegionObserver.java | 19 +---
...cReplicationRolloutFeatureOnMissingGroupIT.java | 4 +-
.../end2end/SyncReplicationRolloutStep1IT.java | 14 ++-
.../index/BypassedMutationBlockMetricsIT.java | 5 -
.../IndexRegionObserverMutationBlockingIT.java | 45 ++++-----
.../org/apache/phoenix/jdbc/HAGroupMetricsIT.java | 5 -
.../apache/phoenix/jdbc/HAGroupStoreManagerIT.java | 74 ++++++---------
.../phoenix/jdbc/ParallelPhoenixConnectionIT.java | 5 -
14 files changed, 141 insertions(+), 190 deletions(-)
diff --git
a/phoenix-core-client/src/main/java/org/apache/phoenix/exception/MutationBlockedIOException.java
b/phoenix-core-client/src/main/java/org/apache/phoenix/exception/MutationBlockedIOException.java
index 4c2663cefa..9f37225245 100644
---
a/phoenix-core-client/src/main/java/org/apache/phoenix/exception/MutationBlockedIOException.java
+++
b/phoenix-core-client/src/main/java/org/apache/phoenix/exception/MutationBlockedIOException.java
@@ -20,8 +20,8 @@ package org.apache.phoenix.exception;
import org.apache.hadoop.hbase.DoNotRetryIOException;
/**
- * Exception thrown when CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED is set and
the current cluster
- * role is ACTIVE_TO_STANDBY.
+ * Exception thrown when an HA client mutation reaches a cluster whose role
for the mutation's HA
+ * group is ACTIVE_TO_STANDBY.
* <p>
* Extends {@link DoNotRetryIOException} so HBase's RPC retry layers
* ({@code AsyncRequestFutureImpl}, {@code RpcRetryingCallerImpl}) fail-fast
on the first hit
diff --git
a/phoenix-core-client/src/main/java/org/apache/phoenix/jdbc/HAGroupStoreClient.java
b/phoenix-core-client/src/main/java/org/apache/phoenix/jdbc/HAGroupStoreClient.java
index 79f6409eba..e3e722a2b2 100644
---
a/phoenix-core-client/src/main/java/org/apache/phoenix/jdbc/HAGroupStoreClient.java
+++
b/phoenix-core-client/src/main/java/org/apache/phoenix/jdbc/HAGroupStoreClient.java
@@ -370,34 +370,32 @@ public class HAGroupStoreClient implements Closeable {
}
/**
- * Returns whether the underlying ZooKeeper connection for this client is
currently live.
- * When {@code false}, the Curator connection has been lost/suspended and
any call that
- * reads shared state ({@link #getHAGroupStoreRecord()}, {@link
#subscribeToTargetState},
- * etc.) will throw an {@link IOException}.
+ * Returns whether the underlying ZooKeeper connection for this client is
currently live. When
+ * {@code false}, the Curator connection has been lost/suspended and any
call that reads shared
+ * state ({@link #getHAGroupStoreRecord()}, {@link #subscribeToTargetState},
etc.) will throw an
+ * {@link IOException}.
* <p>
- * Consumers that subscribe an {@link HAGroupStateListener} and cache the
resulting events
- * in memory (i.e. treat the listener view as the source of truth) should
consult this
- * before trusting their cached snapshot — a {@code false} return
means the client
- * has stopped receiving events and the cached view may be arbitrarily stale.
+ * Consumers that subscribe an {@link HAGroupStateListener} and cache the
resulting events in
+ * memory (i.e. treat the listener view as the source of truth) should
consult this before
+ * trusting their cached snapshot — a {@code false} return means the
client has stopped
+ * receiving events and the cached view may be arbitrarily stale.
* <p>
* Flag transitions:
* <ul>
- * <li>Set to {@code true} on successful initial construction, after the
local
- * {@code PathChildrenCache} has been started and warmed.</li>
- * <li>Set to {@code false} by the Curator connection-state listener on
- * {@code CONNECTION_LOST} / {@code CONNECTION_SUSPENDED}.</li>
- * <li>Set back to {@code true} on {@code CONNECTION_RECONNECTED}. This
tracks the
- * Curator reconnect event, <em>not</em> cache resynchronization
— Curator
- * resyncs the {@code PathChildrenCache} asynchronously after
reconnect, so a brief
- * window exists where {@code isHealthy()} returns {@code true} but
the cache has
- * not yet observed post-outage state changes. Consumers requiring
absolute
- * freshness after a reconnect should re-read via {@link
#getHAGroupStoreRecord()}
- * rather than trust a listener-cached snapshot.</li>
+ * <li>Set to {@code true} on successful initial construction, after the
local
+ * {@code PathChildrenCache} has been started and warmed.</li>
+ * <li>Set to {@code false} by the Curator connection-state listener on
{@code CONNECTION_LOST} /
+ * {@code CONNECTION_SUSPENDED}.</li>
+ * <li>Set back to {@code true} on {@code CONNECTION_RECONNECTED}. This
tracks the Curator
+ * reconnect event, <em>not</em> cache resynchronization — Curator
resyncs the
+ * {@code PathChildrenCache} asynchronously after reconnect, so a brief
window exists where
+ * {@code isHealthy()} returns {@code true} but the cache has not yet
observed post-outage state
+ * changes. Consumers requiring absolute freshness after a reconnect should
re-read via
+ * {@link #getHAGroupStoreRecord()} rather than trust a listener-cached
snapshot.</li>
* </ul>
* The field is {@code volatile}; cross-thread reads are safe.
- *
- * @return {@code true} if the ZK connection is live; {@code false} if the
connection has
- * been lost or suspended
+ * @return {@code true} if the ZK connection is live; {@code false} if the
connection has been
+ * lost or suspended
*/
public boolean isHealthy() {
return isHealthy;
diff --git
a/phoenix-core-client/src/main/java/org/apache/phoenix/jdbc/HAGroupStoreManager.java
b/phoenix-core-client/src/main/java/org/apache/phoenix/jdbc/HAGroupStoreManager.java
index 6a4d5aeb30..13f6f400ad 100644
---
a/phoenix-core-client/src/main/java/org/apache/phoenix/jdbc/HAGroupStoreManager.java
+++
b/phoenix-core-client/src/main/java/org/apache/phoenix/jdbc/HAGroupStoreManager.java
@@ -23,9 +23,7 @@ import static
org.apache.phoenix.jdbc.HAGroupStoreRecord.HAGroupState.ACTIVE_NOT
import static org.apache.phoenix.jdbc.HAGroupStoreRecord.HAGroupState.STANDBY;
import static
org.apache.phoenix.jdbc.HAGroupStoreRecord.HAGroupState.STANDBY_TO_ACTIVE;
import static org.apache.phoenix.jdbc.PhoenixHAAdmin.getLocalZkUrl;
-import static
org.apache.phoenix.query.QueryServices.CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED;
import static
org.apache.phoenix.query.QueryServices.HA_GROUP_STALE_FOR_MUTATION_CHECK_ENABLED;
-import static
org.apache.phoenix.query.QueryServicesOptions.DEFAULT_CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED;
import static
org.apache.phoenix.query.QueryServicesOptions.DEFAULT_HA_GROUP_STALE_FOR_MUTATION_CHECK_ENABLED;
import java.io.IOException;
@@ -40,8 +38,10 @@ import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.hadoop.conf.Configuration;
import org.apache.phoenix.exception.InvalidClusterRoleTransitionException;
+import org.apache.phoenix.exception.MutationBlockedIOException;
import org.apache.phoenix.exception.SQLExceptionCode;
import org.apache.phoenix.exception.SQLExceptionInfo;
+import org.apache.phoenix.exception.StaleClusterRoleRecordException;
import org.apache.phoenix.exception.StaleHAGroupStoreRecordVersionException;
import org.apache.phoenix.jdbc.HAGroupStoreRecord.HAGroupState;
import org.slf4j.Logger;
@@ -61,7 +61,6 @@ public class HAGroupStoreManager {
// Can revert this but will fail for tests with one cluster down
private static Map<String, HAGroupStoreManager> instances = new
ConcurrentHashMap<>();
- private final boolean mutationBlockEnabled;
private final boolean checkStaleCRRForEveryMutation;
private final String zkUrl;
private final Configuration conf;
@@ -182,8 +181,6 @@ public class HAGroupStoreManager {
}
private HAGroupStoreManager(final Configuration conf, final String zkUrl) {
- this.mutationBlockEnabled =
conf.getBoolean(CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED,
- DEFAULT_CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED);
this.checkStaleCRRForEveryMutation =
conf.getBoolean(HA_GROUP_STALE_FOR_MUTATION_CHECK_ENABLED,
DEFAULT_HA_GROUP_STALE_FOR_MUTATION_CHECK_ENABLED);
this.zkUrl = zkUrl;
@@ -202,40 +199,47 @@ public class HAGroupStoreManager {
}
/**
- * Checks whether mutation is blocked or not for a specific HA group.
- * @param haGroupName name of the HA group, null for default HA group which
tracks all HA groups.
- * @return true if mutation is blocked, false otherwise.
- * @throws IOException when HAGroupStoreClient is not healthy.
+ * Checks an HA client mutation for {@code haGroupName} with one read of the
HA group record.
+ * Throws {@link StaleClusterRoleRecordException} if the record is stale for
the client (see
+ * {@link #isHAGroupOnClientStale}), then {@link MutationBlockedIOException}
if the local cluster
+ * role is ACTIVE_TO_STANDBY. A missing record passes both checks.
+ * @param haGroupName name of the HA group
+ * @throws IOException when the mutation is rejected or HAGroupStoreClient
is not healthy
*/
- public boolean isMutationBlocked(String haGroupName) throws IOException {
- if (mutationBlockEnabled) {
- HAGroupStoreClient haGroupStoreClient =
- getHAGroupStoreClientAndSetupFailoverManagement(haGroupName);
- HAGroupStoreRecord recordWithMetadata =
haGroupStoreClient.getHAGroupStoreRecord();
- return recordWithMetadata != null && recordWithMetadata.getClusterRole()
!= null
- && recordWithMetadata.getClusterRole().isMutationBlocked();
+ public void checkMutationAllowed(String haGroupName) throws IOException {
+ HAGroupStoreRecord record =
+
getHAGroupStoreClientAndSetupFailoverManagement(haGroupName).getHAGroupStoreRecord();
+ if (!hasClusterRole(record)) {
+ return;
+ }
+ if (checkStaleCRRForEveryMutation && isStaleOnClient(record)) {
+ throw new StaleClusterRoleRecordException(
+ String.format("HAGroupStoreRecord is stale for haGroup %s on client",
haGroupName));
+ }
+ if (record.getClusterRole().isMutationBlocked()) {
+ throw new MutationBlockedIOException(String.format(
+ "Blocking mutation because the cluster role for HA group %s is
ACTIVE_TO_STANDBY",
+ haGroupName));
}
- return false;
}
/**
- * Returns whether the {@link HAGroupStoreClient} backing {@code
haGroupName} has a live
- * ZooKeeper connection. See {@link HAGroupStoreClient#isHealthy()} for the
full state
- * transitions (including the brief post-reconnect window where the flag
returns
- * {@code true} before Curator has resynchronized the local cache).
+ * Returns whether the {@link HAGroupStoreClient} backing {@code
haGroupName} has a live ZooKeeper
+ * connection. See {@link HAGroupStoreClient#isHealthy()} for the full state
transitions
+ * (including the brief post-reconnect window where the flag returns {@code
true} before Curator
+ * has resynchronized the local cache).
* <p>
* Consumers that subscribe via {@link #subscribeToTargetState} and cache
the resulting
- * {@link HAGroupStateListener} events in memory should consult this before
trusting the
- * cached snapshot: a {@code false} return means the client stopped
receiving events and
- * the cached view may be stale until Curator reconnects. This gives
listener-based
- * consumers a first-class alternative to matching on the {@code
"HAGroupStoreClient is
+ * {@link HAGroupStateListener} events in memory should consult this before
trusting the cached
+ * snapshot: a {@code false} return means the client stopped receiving
events and the cached view
+ * may be stale until Curator reconnects. This gives listener-based
consumers a first-class
+ * alternative to matching on the {@code "HAGroupStoreClient is
* not healthy"} {@link IOException} thrown by the state-reading methods.
- *
* @param haGroupName name of the HA group
- * @return {@code true} if the underlying ZK connection is live; {@code
false} if it has
- * been lost or suspended
- * @throws IOException when the {@link HAGroupStoreClient} for {@code
haGroupName} cannot
- * be resolved or set up
+ * @return {@code true} if the underlying ZK connection is live; {@code
false} if it has been lost
+ * or suspended
+ * @throws IOException when the {@link HAGroupStoreClient} for {@code
haGroupName} cannot be
+ * resolved or set up
*/
public boolean isHealthy(String haGroupName) throws IOException {
HAGroupStoreClient haGroupStoreClient =
@@ -245,25 +249,32 @@ public class HAGroupStoreManager {
public boolean isHAGroupOnClientStale(String haGroupName) throws IOException
{
if (checkStaleCRRForEveryMutation) {
- HAGroupStoreClient haGroupStoreClient =
- getHAGroupStoreClientAndSetupFailoverManagement(haGroupName);
- // If local cluster is not ACTIVE/ACTIVE_TO_STANDBY, it means the
Failover CRR is stale
- // on client as they are trying to write/read from a STANDBY cluster.
- // If we plan to use Standby clusters for scan operations, depends on
whether the
- // connection is an SCN connection or not, If a connection is not an SCN
connection then
- // Standby cluster can't be used as most recent data is only available
through ACTIVE
- // cluster. For SCN connections, future client functionality might allow
attempts to
- // open connections to the standby cluster, which would then accept or
reject the query
- // based on whether the SCN falls within its consistency point, and will
require a change
- // in the logic her, with much bigger change.
- HAGroupStoreRecord hagroupStoreRecord =
haGroupStoreClient.getHAGroupStoreRecord();
- return HighAvailabilityPolicy.valueOf(hagroupStoreRecord.getPolicy())
- == HighAvailabilityPolicy.FAILOVER
- && !(hagroupStoreRecord.getHAGroupState().getClusterRole().isActive());
+ HAGroupStoreRecord record =
+
getHAGroupStoreClientAndSetupFailoverManagement(haGroupName).getHAGroupStoreRecord();
+ return hasClusterRole(record) && isStaleOnClient(record);
}
return false;
}
+ private static boolean hasClusterRole(HAGroupStoreRecord record) {
+ return record != null && record.getClusterRole() != null;
+ }
+
+ /** Caller must check {@link #hasClusterRole} first. */
+ private static boolean isStaleOnClient(HAGroupStoreRecord record) {
+ // If local cluster is not ACTIVE/ACTIVE_TO_STANDBY, it means the Failover
CRR is stale
+ // on client as they are trying to write/read from a STANDBY cluster.
+ // If we plan to use Standby clusters for scan operations, depends on
whether the
+ // connection is an SCN connection or not, If a connection is not an SCN
connection then
+ // Standby cluster can't be used as most recent data is only available
through ACTIVE
+ // cluster. For SCN connections, future client functionality might allow
attempts to
+ // open connections to the standby cluster, which would then accept or
reject the query
+ // based on whether the SCN falls within its consistency point, and will
require a change
+ // in the logic her, with much bigger change.
+ return HighAvailabilityPolicy.valueOf(record.getPolicy()) ==
HighAvailabilityPolicy.FAILOVER
+ && !record.getClusterRole().isActive();
+ }
+
/**
* Force rebuilds the HAGroupStoreClient instance for all HA groups. If any
HAGroupStoreClient
* instance is not created, it will be created.
diff --git
a/phoenix-core-client/src/main/java/org/apache/phoenix/query/QueryServices.java
b/phoenix-core-client/src/main/java/org/apache/phoenix/query/QueryServices.java
index 93eda74fd4..82b0244a8f 100644
---
a/phoenix-core-client/src/main/java/org/apache/phoenix/query/QueryServices.java
+++
b/phoenix-core-client/src/main/java/org/apache/phoenix/query/QueryServices.java
@@ -452,9 +452,6 @@ public interface QueryServices extends SQLCloseable {
public static final String PHOENIX_VIEW_TTL_ENABLED =
"phoenix.view.ttl.enabled";
public static final String PHOENIX_VIEW_TTL_TENANT_VIEWS_PER_SCAN_LIMIT =
"phoenix.view.ttl.tenant_views_per_scan.limit";
- // Block mutations based on cluster role record
- public static final String CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED =
- "phoenix.cluster.role.based.mutation.block.enabled";
// Check HAGroup is Stale for mutations
public static final String HA_GROUP_STALE_FOR_MUTATION_CHECK_ENABLED =
"phoenix.ha.group.stale.for.mutation.check.enabled";
diff --git
a/phoenix-core-client/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
b/phoenix-core-client/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
index 8cca59aabc..940e9b4c14 100644
---
a/phoenix-core-client/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
+++
b/phoenix-core-client/src/main/java/org/apache/phoenix/query/QueryServicesOptions.java
@@ -30,7 +30,6 @@ import static
org.apache.phoenix.query.QueryServices.CDC_TTL_SHARED_CACHE_EXPIRY
import static
org.apache.phoenix.query.QueryServices.CLIENT_INDEX_ASYNC_THRESHOLD;
import static org.apache.phoenix.query.QueryServices.CLIENT_METRICS_TAG;
import static
org.apache.phoenix.query.QueryServices.CLIENT_SPOOL_THRESHOLD_BYTES_ATTRIB;
-import static
org.apache.phoenix.query.QueryServices.CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED;
import static
org.apache.phoenix.query.QueryServices.COLLECT_REQUEST_LEVEL_METRICS;
import static org.apache.phoenix.query.QueryServices.COMMIT_STATS_ASYNC;
import static
org.apache.phoenix.query.QueryServices.CONNECTION_ACTIVITY_LOGGING_ENABLED;
@@ -502,7 +501,6 @@ public class QueryServicesOptions {
public static final int DEFAULT_PHOENIX_STREAMS_GET_TABLE_REGIONS_TIMEOUT =
300000; // 5 minutes
public static final boolean DEFAULT_SYSTEM_CATALOG_INDEXES_ENABLED = false;
- public static final Boolean
DEFAULT_CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED = false;
public static final Boolean
DEFAULT_HA_GROUP_STALE_FOR_MUTATION_CHECK_ENABLED = true;
public static final Boolean DEFAULT_HA_GROUP_STORE_CLIENT_PREWARM_ENABLED =
false;
public static final Boolean DEFAULT_CQSI_THREAD_POOL_ENABLED = false;
@@ -647,8 +645,6 @@ public class QueryServicesOptions {
DEFAULT_CONNECTION_EXPLAIN_PLAN_LOGGING_ENABLED)
.setIfUnset(CONNECTION_ACTIVITY_LOGGING_INTERVAL,
DEFAULT_CONNECTION_ACTIVITY_LOGGING_INTERVAL_IN_MINS)
- .setIfUnset(CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED,
- DEFAULT_CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED)
.setIfUnset(CQSI_THREAD_POOL_ENABLED, DEFAULT_CQSI_THREAD_POOL_ENABLED)
.setIfUnset(CQSI_THREAD_POOL_KEEP_ALIVE_SECONDS,
DEFAULT_CQSI_THREAD_POOL_KEEP_ALIVE_SECONDS)
.setIfUnset(CQSI_THREAD_POOL_CORE_POOL_SIZE,
DEFAULT_CQSI_THREAD_POOL_CORE_POOL_SIZE)
diff --git
a/phoenix-core-client/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
b/phoenix-core-client/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
index b9d917cd3b..4b6e656268 100644
---
a/phoenix-core-client/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
+++
b/phoenix-core-client/src/main/java/org/apache/phoenix/schema/MetaDataClient.java
@@ -4926,9 +4926,9 @@ public class MetaDataClient {
/**
* To check if TTL is defined at any of the child below we are
checking it at
* {@link
org.apache.phoenix.coprocessor.MetaDataEndpointImpl#mutateColumn(List,
ColumnMutator, int, PTable, PTable, boolean)}
- * level where in function
- * {@link org.apache.phoenix.coprocessor.MetaDataEndpointImpl#
validateIfMutationAllowedOnParent(PTable, List, PTableType, long, byte[],
byte[], byte[], List, int)}
- * we are already traversing through allDescendantViews.
+ * level where in function {@link
org.apache.phoenix.coprocessor.MetaDataEndpointImpl#
+ * validateIfMutationAllowedOnParent(PTable, List, PTableType, long,
byte[], byte[],
+ * byte[], List, int)} we are already traversing through
allDescendantViews.
*/
}
diff --git
a/phoenix-core-server/src/main/java/org/apache/phoenix/hbase/index/IndexRegionObserver.java
b/phoenix-core-server/src/main/java/org/apache/phoenix/hbase/index/IndexRegionObserver.java
index 6cbd8e7a54..0109d2f77e 100644
---
a/phoenix-core-server/src/main/java/org/apache/phoenix/hbase/index/IndexRegionObserver.java
+++
b/phoenix-core-server/src/main/java/org/apache/phoenix/hbase/index/IndexRegionObserver.java
@@ -105,8 +105,6 @@ import
org.apache.phoenix.coprocessor.generated.IndexMutationsProtos;
import org.apache.phoenix.coprocessor.generated.PTableProtos;
import org.apache.phoenix.coprocessorclient.BaseScannerRegionObserverConstants;
import org.apache.phoenix.exception.DataExceedsCapacityException;
-import org.apache.phoenix.exception.MutationBlockedIOException;
-import org.apache.phoenix.exception.StaleClusterRoleRecordException;
import org.apache.phoenix.execute.MutationState;
import org.apache.phoenix.expression.CaseExpression;
import org.apache.phoenix.expression.Expression;
@@ -912,7 +910,8 @@ public class IndexRegionObserver implements
RegionCoprocessor, RegionObserver {
}
/*
- * Also checks for mutationBlockEnabled if
CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED is enabled.
+ * When sync replication is enabled, also rejects HA client batches whose HA
group is stale on the
+ * client or whose local cluster role is ACTIVE_TO_STANDBY.
*/
@Override
public void preBatchMutate(ObserverContext<RegionCoprocessorEnvironment> c,
@@ -972,17 +971,9 @@ public class IndexRegionObserver implements
RegionCoprocessor, RegionObserver {
// roleRecord (I tried it and still it's slow due to haGroupStoreClient
// initialization) and caching will give us old result in case one
cluster
// is unreachable instead of UNKNOWN.
- // Check if mutation's haGroup is stale
- boolean isHAGroupOnClientStale =
haGroupStoreManager.isHAGroupOnClientStale(name);
- if (StringUtils.isNotBlank(name) && isHAGroupOnClientStale) {
- throw new StaleClusterRoleRecordException(
- String.format("HAGroupStoreRecord is stale for haGroup %s on " +
"client", name));
- }
-
- if (StringUtils.isNotBlank(name) &&
haGroupStoreManager.isMutationBlocked(name)) {
- throw new MutationBlockedIOException(
- "Blocking Mutation as Some CRRs " + "are in ACTIVE_TO_STANDBY
state and "
- + "CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED is true");
+ // Throws StaleClusterRoleRecordException, then
MutationBlockedIOException, from one read.
+ if (StringUtils.isNotBlank(name)) {
+ haGroupStoreManager.checkMutationAllowed(name);
}
}
diff --git
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SyncReplicationRolloutFeatureOnMissingGroupIT.java
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SyncReplicationRolloutFeatureOnMissingGroupIT.java
index 78944df564..aac9608288 100644
---
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SyncReplicationRolloutFeatureOnMissingGroupIT.java
+++
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SyncReplicationRolloutFeatureOnMissingGroupIT.java
@@ -21,7 +21,6 @@ import static
org.apache.phoenix.coprocessorclient.BaseScannerRegionObserverCons
import static
org.apache.phoenix.query.QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES;
import static org.apache.phoenix.query.QueryConstants.EMPTY_COLUMN_BYTES;
import static org.apache.phoenix.query.QueryConstants.EMPTY_COLUMN_VALUE_BYTES;
-import static
org.apache.phoenix.query.QueryServices.CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED;
import static
org.apache.phoenix.query.QueryServices.HA_GROUP_STALE_FOR_MUTATION_CHECK_ENABLED;
import static
org.apache.phoenix.query.QueryServices.SYNCHRONOUS_REPLICATION_ENABLED;
import static org.junit.Assert.assertFalse;
@@ -68,10 +67,9 @@ public class SyncReplicationRolloutFeatureOnMissingGroupIT
extends BaseTest {
@BeforeClass
public static synchronized void doSetup() throws Exception {
- Map<String, String> props = Maps.newHashMapWithExpectedSize(3);
+ Map<String, String> props = Maps.newHashMapWithExpectedSize(2);
// Feature on, but the HA group does not exist yet;
production-representative HA guard flags on.
props.put(SYNCHRONOUS_REPLICATION_ENABLED, Boolean.TRUE.toString());
- props.put(CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED,
Boolean.TRUE.toString());
props.put(HA_GROUP_STALE_FOR_MUTATION_CHECK_ENABLED,
Boolean.TRUE.toString());
setUpTestDriver(new ReadOnlyProps(props.entrySet().iterator()));
}
diff --git
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SyncReplicationRolloutStep1IT.java
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SyncReplicationRolloutStep1IT.java
index b6c50b9249..3a7c49a0bc 100644
---
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/SyncReplicationRolloutStep1IT.java
+++
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/SyncReplicationRolloutStep1IT.java
@@ -21,7 +21,6 @@ import static
org.apache.phoenix.coprocessorclient.BaseScannerRegionObserverCons
import static
org.apache.phoenix.query.QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES;
import static org.apache.phoenix.query.QueryConstants.EMPTY_COLUMN_BYTES;
import static org.apache.phoenix.query.QueryConstants.EMPTY_COLUMN_VALUE_BYTES;
-import static
org.apache.phoenix.query.QueryServices.CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED;
import static
org.apache.phoenix.query.QueryServices.HA_GROUP_STALE_FOR_MUTATION_CHECK_ENABLED;
import static
org.apache.phoenix.query.QueryServices.SYNCHRONOUS_REPLICATION_ENABLED;
import static org.junit.Assert.assertEquals;
@@ -51,24 +50,23 @@ import
org.apache.phoenix.thirdparty.com.google.common.collect.Maps;
* = false}) and the HA group has not been created yet, while clients —
upgraded first — already
* attach the {@code _HAGroupName} attribute to every mutation.
* <p>
- * Production runs {@code phoenix.cluster.role.based.mutation.block.enabled =
true} and the
- * stale-CRR check defaults on, so in this window {@code
IndexRegionObserver.preBatchMutate} reaches
- * the cluster-role-based mutation-block / staleness gate — which keys off
{@code _HAGroupName}
- * presence, not the feature flag — and resolves the (absent) HA group,
failing the write.
+ * The mutation block is always on and the stale-CRR check defaults on, so
without the master switch
+ * {@code IndexRegionObserver.preBatchMutate} would reach the
cluster-role-based mutation-block /
+ * staleness gate in this window — which keys off {@code _HAGroupName}
presence — and resolve the
+ * (absent) HA group, failing the write.
* <p>
* The contract this test asserts (design decision D1): {@code
SYNCHRONOUS_REPLICATION_ENABLED} is
* the master switch for all HA semantics, so with the feature off the gate
must not run and the
- * write must succeed regardless of the block/stale sub-flags or whether the
HA group exists.
+ * write must succeed regardless of the stale-check sub-flag or whether the HA
group exists.
*/
@Category(NeedsOwnMiniClusterTest.class)
public class SyncReplicationRolloutStep1IT extends BaseTest {
@BeforeClass
public static synchronized void doSetup() throws Exception {
- Map<String, String> props = Maps.newHashMapWithExpectedSize(3);
+ Map<String, String> props = Maps.newHashMapWithExpectedSize(2);
// Step-1 state: feature dark, but production-representative HA guard
flags on.
props.put(SYNCHRONOUS_REPLICATION_ENABLED, Boolean.FALSE.toString());
- props.put(CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED,
Boolean.TRUE.toString());
props.put(HA_GROUP_STALE_FOR_MUTATION_CHECK_ENABLED,
Boolean.TRUE.toString());
setUpTestDriver(new ReadOnlyProps(props.entrySet().iterator()));
}
diff --git
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/BypassedMutationBlockMetricsIT.java
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/BypassedMutationBlockMetricsIT.java
index aad94f4d40..e6230fd997 100644
---
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/BypassedMutationBlockMetricsIT.java
+++
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/BypassedMutationBlockMetricsIT.java
@@ -18,7 +18,6 @@
package org.apache.phoenix.end2end.index;
import static org.apache.phoenix.query.BaseTest.generateUniqueName;
-import static
org.apache.phoenix.query.QueryServices.CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED;
import static org.junit.Assert.assertTrue;
import java.sql.Connection;
@@ -47,10 +46,6 @@ public class BypassedMutationBlockMetricsIT extends HABaseIT
{
@BeforeClass
public static synchronized void doSetup() throws Exception {
- CLUSTERS.getHBaseCluster1().getConfiguration()
- .setBoolean(CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED, true);
- CLUSTERS.getHBaseCluster2().getConfiguration()
- .setBoolean(CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED, true);
CLUSTERS.start();
DriverManager.registerDriver(PhoenixDriver.INSTANCE);
}
diff --git
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexRegionObserverMutationBlockingIT.java
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexRegionObserverMutationBlockingIT.java
index f1966e9342..406c13acdc 100644
---
a/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexRegionObserverMutationBlockingIT.java
+++
b/phoenix-core/src/it/java/org/apache/phoenix/end2end/index/IndexRegionObserverMutationBlockingIT.java
@@ -20,7 +20,6 @@ package org.apache.phoenix.end2end.index;
import static
org.apache.phoenix.jdbc.HighAvailabilityGroup.PHOENIX_HA_GROUP_ATTR;
import static
org.apache.phoenix.jdbc.PhoenixDatabaseMetaData.SYSTEM_HA_GROUP_NAME;
import static org.apache.phoenix.query.BaseTest.generateUniqueName;
-import static
org.apache.phoenix.query.QueryServices.CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -35,6 +34,7 @@ import
org.apache.phoenix.exception.MutationBlockedIOException;
import org.apache.phoenix.execute.CommitException;
import org.apache.phoenix.jdbc.FailoverPhoenixConnection;
import org.apache.phoenix.jdbc.HABaseIT;
+import org.apache.phoenix.jdbc.HAGroupStoreManager;
import org.apache.phoenix.jdbc.HAGroupStoreRecord;
import org.apache.phoenix.jdbc.HighAvailabilityPolicy;
import org.apache.phoenix.jdbc.HighAvailabilityTestingUtility;
@@ -49,8 +49,8 @@ import org.junit.rules.TestName;
/**
* Integration test for mutation blocking functionality in
IndexRegionObserver.preBatchMutate()
- * Tests that MutationBlockedIOException is properly thrown when cluster
role-based mutation
- * blocking is enabled and CRRs are in ACTIVE_TO_STANDBY state.
+ * Tests that MutationBlockedIOException is properly thrown when CRRs are in
ACTIVE_TO_STANDBY
+ * state.
*/
@Category(NeedsOwnMiniClusterTest.class)
public class IndexRegionObserverMutationBlockingIT extends HABaseIT {
@@ -67,10 +67,6 @@ public class IndexRegionObserverMutationBlockingIT extends
HABaseIT {
@BeforeClass
public static synchronized void doSetup() throws Exception {
- CLUSTERS.getHBaseCluster1().getConfiguration()
- .setBoolean(CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED, true);
- CLUSTERS.getHBaseCluster2().getConfiguration()
- .setBoolean(CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED, true);
CLUSTERS.start();
DriverManager.registerDriver(PhoenixDriver.INSTANCE);
}
@@ -348,7 +344,8 @@ public class IndexRegionObserverMutationBlockingIT extends
HABaseIT {
HighAvailabilityPolicy.FAILOVER.toString(), this.peerZkUrl,
CLUSTERS.getMasterAddress1(),
CLUSTERS.getMasterAddress2(), CLUSTERS.getHdfsUrl1(),
CLUSTERS.getHdfsUrl2(), 0L);
haAdmin.updateHAGroupStoreRecordInZooKeeper(haGroupName,
haGroupStoreRecord, -1);
- awaitZkPropagation(haGroupName,
HAGroupStoreRecord.HAGroupState.ACTIVE_IN_SYNC_TO_STANDBY);
+ awaitServerCachePropagation(haGroupName,
+ HAGroupStoreRecord.HAGroupState.ACTIVE_IN_SYNC_TO_STANDBY);
long startMs = System.currentTimeMillis();
try {
@@ -431,7 +428,8 @@ public class IndexRegionObserverMutationBlockingIT extends
HABaseIT {
HighAvailabilityPolicy.FAILOVER.toString(), this.peerZkUrl,
CLUSTERS.getMasterAddress1(),
CLUSTERS.getMasterAddress2(), CLUSTERS.getHdfsUrl1(),
CLUSTERS.getHdfsUrl2(), 0L);
haAdmin.updateHAGroupStoreRecordInZooKeeper(haGroupName,
haGroupStoreRecord, -1);
- awaitZkPropagation(haGroupName,
HAGroupStoreRecord.HAGroupState.ACTIVE_IN_SYNC_TO_STANDBY);
+ awaitServerCachePropagation(haGroupName,
+ HAGroupStoreRecord.HAGroupState.ACTIVE_IN_SYNC_TO_STANDBY);
long startMs = System.currentTimeMillis();
try {
@@ -456,36 +454,29 @@ public class IndexRegionObserverMutationBlockingIT
extends HABaseIT {
}
/**
- * Polls ZooKeeper for the HAGroupStoreRecord to reflect the {@code
expectedState} after a
- * {@code haAdmin.updateHAGroupStoreRecordInZooKeeper} write. Returns as
soon as the cached state
- * matches the expected, or after a timeout. Replaces the unconditional
- * {@code Thread.sleep(ZK_CURATOR_EVENT_PROPAGATION_TIMEOUT_MS)} pattern
with a bounded polling
- * loop (with a short between-poll park) so the wait is condition-driven
rather than
- * fixed-duration.
- * <p>
- * The poll interval is short ({@link #ZK_PROPAGATION_POLL_INTERVAL_MS} ms)
so the typical wait is
- * well under the propagation budget; the deadline gives generous CI
headroom. Throws
- * {@code AssertionError} via {@code fail()} if the state never converges,
which surfaces the same
- * failure shape a stale read would (the subsequent UPSERT would observe the
old state).
- * @param haGroupName the HA group whose state to await
- * @param expectedState the {@link HAGroupStoreRecord.HAGroupState} the
cached record must reach
- * @throws Exception if reading the cached record throws
+ * Waits until the cluster 1 RegionServer's cached HAGroupStoreRecord (the
one
+ * {@code preBatchMutate} reads) reaches {@code expectedState}. A direct ZK
read can return the
+ * new state before the Curator cache event arrives, so polling ZK alone
lets the next UPSERT race
+ * the cache. The mini-cluster shares this JVM, so {@link
HAGroupStoreManager#getInstance} returns
+ * the RegionServer's instance.
*/
// java:S2925 (Thread.sleep in tests) suppressed: the sleep here is bounded
between condition
// checks inside a deadline-bounded polling loop, not an unconditional wait.
The rule's flake
// concern (fixed-duration sleeps) does not apply.
@SuppressWarnings("java:S2925")
- private void awaitZkPropagation(String haGroupName,
HAGroupStoreRecord.HAGroupState expectedState)
- throws Exception {
+ private void awaitServerCachePropagation(String haGroupName,
+ HAGroupStoreRecord.HAGroupState expectedState) throws Exception {
+ HAGroupStoreManager manager =
+
HAGroupStoreManager.getInstance(CLUSTERS.getHBaseCluster1().getConfiguration());
long deadlineMs = System.currentTimeMillis() +
ZK_PROPAGATION_AWAIT_DEADLINE_MS;
while (System.currentTimeMillis() < deadlineMs) {
- HAGroupStoreRecord cached =
haAdmin.getHAGroupStoreRecordInZooKeeper(haGroupName).getLeft();
+ HAGroupStoreRecord cached =
manager.getHAGroupStoreRecord(haGroupName).orElse(null);
if (cached != null && cached.getHAGroupState() == expectedState) {
return;
}
Thread.sleep(ZK_PROPAGATION_POLL_INTERVAL_MS);
}
- fail("Timed out waiting for ZK to propagate HAGroupStoreRecord state " +
expectedState
+ fail("Timed out waiting for the RegionServer HA group cache to reach state
" + expectedState
+ " for HA group " + haGroupName + " within " +
ZK_PROPAGATION_AWAIT_DEADLINE_MS + "ms");
}
diff --git
a/phoenix-core/src/it/java/org/apache/phoenix/jdbc/HAGroupMetricsIT.java
b/phoenix-core/src/it/java/org/apache/phoenix/jdbc/HAGroupMetricsIT.java
index efe9371972..f4ae3c7817 100644
--- a/phoenix-core/src/it/java/org/apache/phoenix/jdbc/HAGroupMetricsIT.java
+++ b/phoenix-core/src/it/java/org/apache/phoenix/jdbc/HAGroupMetricsIT.java
@@ -27,7 +27,6 @@ import static
org.apache.phoenix.monitoring.GlobalClientMetrics.GLOBAL_HA_MUTATI
import static
org.apache.phoenix.monitoring.GlobalClientMetrics.GLOBAL_HA_POLLER_TICK_COUNT;
import static
org.apache.phoenix.monitoring.GlobalClientMetrics.GLOBAL_HA_POLLER_TICK_FAILURES;
import static
org.apache.phoenix.monitoring.GlobalClientMetrics.GLOBAL_HA_STALE_CRR_DETECTED_COUNT;
-import static
org.apache.phoenix.query.QueryServices.CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
@@ -71,10 +70,6 @@ public class HAGroupMetricsIT extends HABaseIT {
@BeforeClass
public static void setUpBeforeClass() throws Exception {
- CLUSTERS.getHBaseCluster1().getConfiguration()
- .setBoolean(CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED, true);
- CLUSTERS.getHBaseCluster2().getConfiguration()
- .setBoolean(CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED, true);
CLUSTERS.start();
DriverManager.registerDriver(PhoenixDriver.INSTANCE);
}
diff --git
a/phoenix-core/src/it/java/org/apache/phoenix/jdbc/HAGroupStoreManagerIT.java
b/phoenix-core/src/it/java/org/apache/phoenix/jdbc/HAGroupStoreManagerIT.java
index 85d29ed872..b3a08ba72d 100644
---
a/phoenix-core/src/it/java/org/apache/phoenix/jdbc/HAGroupStoreManagerIT.java
+++
b/phoenix-core/src/it/java/org/apache/phoenix/jdbc/HAGroupStoreManagerIT.java
@@ -22,7 +22,6 @@ import static org.apache.hadoop.test.GenericTestUtils.waitFor;
import static
org.apache.phoenix.jdbc.HAGroupStoreClient.ZK_CONSISTENT_HA_GROUP_RECORD_NAMESPACE;
import static org.apache.phoenix.jdbc.PhoenixHAAdmin.getLocalZkUrl;
import static org.apache.phoenix.jdbc.PhoenixHAAdmin.toPath;
-import static
org.apache.phoenix.query.QueryServices.CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED;
import static
org.apache.phoenix.query.QueryServices.HA_GROUP_STORE_CLIENT_PREWARM_ENABLED;
import static
org.apache.phoenix.replication.ReplicationShardDirectoryManager.DEFAULT_REPLICATION_ROUND_DURATION_SECONDS;
import static
org.apache.phoenix.replication.ReplicationShardDirectoryManager.PHOENIX_REPLICATION_ROUND_DURATION_SECONDS_KEY;
@@ -31,21 +30,21 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
-import java.lang.reflect.Field;
import java.util.List;
import java.util.Optional;
-import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.hbase.HConstants;
import org.apache.hadoop.hbase.regionserver.HRegionServer;
import org.apache.phoenix.end2end.NeedsOwnMiniClusterTest;
import org.apache.phoenix.exception.InvalidClusterRoleTransitionException;
+import org.apache.phoenix.exception.MutationBlockedIOException;
+import org.apache.phoenix.exception.StaleClusterRoleRecordException;
import org.apache.phoenix.replication.ReplicationLogGroup;
import org.apache.phoenix.replication.reader.ReplicationLogReplay;
import org.apache.phoenix.util.HAGroupStoreTestUtil;
@@ -76,9 +75,7 @@ public class HAGroupStoreManagerIT extends HABaseIT {
@BeforeClass
public static synchronized void doSetup() throws Exception {
- conf1.setBoolean(CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED, true);
conf1.setLong(ZK_SESSION_TIMEOUT, 30 * 1000);
- conf2.setBoolean(CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED, true);
conf2.setLong(ZK_SESSION_TIMEOUT, 30 * 1000);
// Both mini-clusters run in one JVM. Keep background initialization from
claiming shared
// replay and log-group singletons before each test binds them to the
intended cluster.
@@ -121,7 +118,7 @@ public class HAGroupStoreManagerIT extends HABaseIT {
HAGroupStoreManager haGroupStoreManager =
HAGroupStoreManager.getInstance(conf1);
// Initially no mutation should be blocked
- assertFalse(haGroupStoreManager.isMutationBlocked(haGroupName));
+ haGroupStoreManager.checkMutationAllowed(haGroupName);
// Update to ACTIVE_TO_STANDBY role (should block mutations)
HAGroupStoreRecord transitionRecord = new HAGroupStoreRecord("1.0",
haGroupName,
@@ -132,7 +129,27 @@ public class HAGroupStoreManagerIT extends HABaseIT {
Thread.sleep(ZK_CURATOR_EVENT_PROPAGATION_TIMEOUT_MS);
// Now mutations should be blocked
- assertTrue(haGroupStoreManager.isMutationBlocked(haGroupName));
+ assertThrows(MutationBlockedIOException.class,
+ () -> haGroupStoreManager.checkMutationAllowed(haGroupName));
+ }
+
+ @Test
+ public void testMutationRejectedAsStaleOnStandby() throws Exception {
+ String haGroupName = testName.getMethodName();
+ HAGroupStoreManager haGroupStoreManager =
HAGroupStoreManager.getInstance(conf1);
+ haGroupStoreManager.checkMutationAllowed(haGroupName);
+
+ // A FAILOVER group whose local role is STANDBY means the client CRR is
stale.
+ HAGroupStoreRecord standbyRecord =
+ new HAGroupStoreRecord("1.0", haGroupName,
HAGroupStoreRecord.HAGroupState.STANDBY, 0L,
+ HighAvailabilityPolicy.FAILOVER.toString(), this.peerZKUrl,
CLUSTERS.getMasterAddress1(),
+ CLUSTERS.getMasterAddress2(), CLUSTERS.getHdfsUrl1(),
CLUSTERS.getHdfsUrl2(), 0L);
+ haAdmin.updateHAGroupStoreRecordInZooKeeper(haGroupName, standbyRecord, 0);
+ Thread.sleep(ZK_CURATOR_EVENT_PROPAGATION_TIMEOUT_MS);
+
+ assertTrue(haGroupStoreManager.isHAGroupOnClientStale(haGroupName));
+ assertThrows(StaleClusterRoleRecordException.class,
+ () -> haGroupStoreManager.checkMutationAllowed(haGroupName));
}
@Test
@@ -158,8 +175,8 @@ public class HAGroupStoreManagerIT extends HABaseIT {
CLUSTERS.getHdfsUrl1(), CLUSTERS.getHdfsUrl2());
// No mutations should be blocked
- assertFalse(haGroupStoreManager.isMutationBlocked(haGroupName1));
- assertFalse(haGroupStoreManager.isMutationBlocked(haGroupName2));
+ haGroupStoreManager.checkMutationAllowed(haGroupName1);
+ haGroupStoreManager.checkMutationAllowed(haGroupName2);
// Move only the second group into a mutation-blocking drain state
// (ACTIVE_IN_SYNC_TO_STANDBY maps to ACTIVE_TO_STANDBY, which blocks
mutations).
@@ -172,8 +189,9 @@ public class HAGroupStoreManagerIT extends HABaseIT {
Thread.sleep(ZK_CURATOR_EVENT_PROPAGATION_TIMEOUT_MS);
// Blocking is per-group: only the second group is blocked; the first
stays writable.
- assertFalse(haGroupStoreManager.isMutationBlocked(haGroupName1));
- assertTrue(haGroupStoreManager.isMutationBlocked(haGroupName2));
+ haGroupStoreManager.checkMutationAllowed(haGroupName1);
+ assertThrows(MutationBlockedIOException.class,
+ () -> haGroupStoreManager.checkMutationAllowed(haGroupName2));
}
@Test
@@ -336,38 +354,6 @@ public class HAGroupStoreManagerIT extends HABaseIT {
assertTrue(recordOpt.isPresent());
}
- @Test
- public void testMutationBlockDisabled() throws Exception {
- String haGroupName = testName.getMethodName();
-
- // Create configuration with mutation block disabled
- Configuration conf = new Configuration();
- conf.set(CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED, "false");
- conf.set(HConstants.ZOOKEEPER_QUORUM, getLocalZkUrl(conf1));
-
- // Set the HAGroupStoreManager instance to null via reflection to force
recreation
- Field field = HAGroupStoreManager.class.getDeclaredField("instances");
- field.setAccessible(true);
- field.set(null, new ConcurrentHashMap<>());
-
- HAGroupStoreManager haGroupStoreManager =
HAGroupStoreManager.getInstance(conf);
- // Create HAGroupStoreRecord with ACTIVE_IN_SYNC_TO_STANDBY role
- HAGroupStoreRecord transitionRecord = new HAGroupStoreRecord("1.0",
haGroupName,
- HAGroupStoreRecord.HAGroupState.ACTIVE_IN_SYNC_TO_STANDBY, 0L,
- HighAvailabilityPolicy.FAILOVER.toString(), this.peerZKUrl,
CLUSTERS.getMasterAddress1(),
- CLUSTERS.getMasterAddress2(), CLUSTERS.getHdfsUrl1(),
CLUSTERS.getHdfsUrl2(), 0L);
-
- haAdmin.updateHAGroupStoreRecordInZooKeeper(haGroupName, transitionRecord,
-1);
- Thread.sleep(ZK_CURATOR_EVENT_PROPAGATION_TIMEOUT_MS);
-
- // Mutations should not be blocked even with ACTIVE_TO_STANDBY role
- assertFalse(haGroupStoreManager.isMutationBlocked(haGroupName));
-
- // Set the HAGroupStoreManager instance back to null via reflection to
force recreation for
- // other tests
- field.set(null, new ConcurrentHashMap<>());
- }
-
@Test
public void testSetHAGroupStatusToStoreAndForward() throws Exception {
String haGroupName = testName.getMethodName();
diff --git
a/phoenix-core/src/it/java/org/apache/phoenix/jdbc/ParallelPhoenixConnectionIT.java
b/phoenix-core/src/it/java/org/apache/phoenix/jdbc/ParallelPhoenixConnectionIT.java
index 84ff5199d4..e4d29c1160 100644
---
a/phoenix-core/src/it/java/org/apache/phoenix/jdbc/ParallelPhoenixConnectionIT.java
+++
b/phoenix-core/src/it/java/org/apache/phoenix/jdbc/ParallelPhoenixConnectionIT.java
@@ -35,7 +35,6 @@ import static
org.apache.phoenix.monitoring.GlobalClientMetrics.GLOBAL_HA_PARALL
import static
org.apache.phoenix.monitoring.GlobalClientMetrics.GLOBAL_HA_PARALLEL_POOL2_TASK_REJECTED_COUNTER;
import static
org.apache.phoenix.query.BaseTest.extractThreadPoolExecutorFromCQSI;
import static org.apache.phoenix.query.QueryServices.AUTO_COMMIT_ATTRIB;
-import static
org.apache.phoenix.query.QueryServices.CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED;
import static
org.apache.phoenix.query.QueryServices.CQSI_THREAD_POOL_ALLOW_CORE_THREAD_TIMEOUT;
import static
org.apache.phoenix.query.QueryServices.CQSI_THREAD_POOL_CORE_POOL_SIZE;
import static org.apache.phoenix.query.QueryServices.CQSI_THREAD_POOL_ENABLED;
@@ -126,10 +125,6 @@ public class ParallelPhoenixConnectionIT extends HABaseIT {
@BeforeClass
public static void setUpBeforeClass() throws Exception {
- CLUSTERS.getHBaseCluster1().getConfiguration()
- .setBoolean(CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED, true);
- CLUSTERS.getHBaseCluster2().getConfiguration()
- .setBoolean(CLUSTER_ROLE_BASED_MUTATION_BLOCK_ENABLED, true);
CLUSTERS.start();
DriverManager.registerDriver(PhoenixDriver.INSTANCE);
DriverManager.registerDriver(new PhoenixTestDriver());