This is an automated email from the ASF dual-hosted git repository.
JackieTien97 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/master by this push:
new 2fd1fe3fda4 support the retry for the execution the insert path in
statemachine with DN metadata fenced and retry for coordinator (#18301)
2fd1fe3fda4 is described below
commit 2fd1fe3fda40012c68b51938cdff2405079aec3a
Author: CYB <[email protected]>
AuthorDate: Mon Jul 27 17:25:34 2026 +0800
support the retry for the execution the insert path in statemachine with DN
metadata fenced and retry for coordinator (#18301)
---
.../java/org/apache/iotdb/rpc/TSStatusCode.java | 4 +-
.../ratis/ApplicationStateMachineProxy.java | 32 ++++-
.../dataregion/DataExecutionVisitor.java | 11 ++
.../dataregion/DataRegionStateMachine.java | 14 +-
.../analyze/cache/partition/PartitionCache.java | 4 +-
.../cache/TreeDeviceSchemaCacheManager.java | 4 +-
.../schemaengine/lease/MetadataLeaseManager.java | 6 +-
.../db/schemaengine/table/DataNodeTableCache.java | 59 ++++++---
.../iotdb/db/schemaengine/table/ITableCache.java | 7 +
.../db/storageengine/dataregion/DataRegion.java | 5 +-
.../dataregion/DataRegionStateMachineTest.java | 143 +++++++++++++++++++++
.../schemaengine/lease/MetadataLeaseTestUtils.java | 9 +-
.../table/DataNodeTableCacheLeaseTest.java | 24 +++-
.../exception/MetadataLeaseFencedException.java | 21 ++-
.../org/apache/iotdb/commons/utils/RetryUtils.java | 1 +
.../apache/iotdb/commons/utils/StatusUtils.java | 1 +
.../iotdb/commons/utils/RetryStatusUtilsTest.java} | 27 +++-
17 files changed, 322 insertions(+), 50 deletions(-)
diff --git
a/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/TSStatusCode.java
b/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/TSStatusCode.java
index 384f38028e8..8f28c916056 100644
---
a/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/TSStatusCode.java
+++
b/iotdb-client/service-rpc/src/main/java/org/apache/iotdb/rpc/TSStatusCode.java
@@ -90,7 +90,9 @@ public enum TSStatusCode {
TYPE_NOT_FOUND(528),
DATABASE_CONFLICT(529),
DATABASE_MODEL(530),
- METADATA_LEASE_FENCED(531),
+ // the range [531, 534] has been occupied
+ METADATA_LEASE_FENCED(535),
+ METADATA_LEASE_FENCED_RETRY_REQUIRED(536),
TABLE_NOT_EXISTS(550),
TABLE_ALREADY_EXISTS(551),
diff --git
a/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/ratis/ApplicationStateMachineProxy.java
b/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/ratis/ApplicationStateMachineProxy.java
index d7900cf2388..6c0366c546f 100644
---
a/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/ratis/ApplicationStateMachineProxy.java
+++
b/iotdb-core/consensus/src/main/java/org/apache/iotdb/consensus/ratis/ApplicationStateMachineProxy.java
@@ -57,6 +57,7 @@ import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.TimeUnit;
import java.util.function.BiConsumer;
public class ApplicationStateMachineProxy extends BaseStateMachine {
@@ -152,6 +153,10 @@ public class ApplicationStateMachineProxy extends
BaseStateMachine {
deserializedRequest.markAsGeneratedByRemoteConsensusLeader();
}
final TSStatus result =
applicationStateMachine.write(deserializedRequest);
+ if (result.getCode() ==
TSStatusCode.METADATA_LEASE_FENCED_RETRY_REQUIRED.getStatusCode()
+ && waitBeforeRetry()) {
+ continue;
+ }
ret = new ResponseMessage(result);
break;
} catch (Throwable rte) {
@@ -160,13 +165,11 @@ public class ApplicationStateMachineProxy extends
BaseStateMachine {
new ResponseMessage(
new
TSStatus(TSStatusCode.INTERNAL_SERVER_ERROR.getStatusCode())
.setMessage(RatisMessages.INTERNAL_ERROR_STATEMACHINE_RUNTIME_EXCEPTION + rte));
- if (Utils.stallApply(consensusGroupType)) {
- waitUntilSystemAllowApply();
- } else {
+ if (!Utils.stallApply(consensusGroupType) ||
!waitUntilSystemAllowApply()) {
break;
}
}
- } while (Utils.stallApply(consensusGroupType));
+ } while (true);
if (isLeader) {
// only record time cost for data region in Performance Overview
Dashboard
@@ -182,16 +185,35 @@ public class ApplicationStateMachineProxy extends
BaseStateMachine {
return CompletableFuture.completedFuture(ret);
}
- private void waitUntilSystemAllowApply() {
+ /**
+ * @return true if the wait completed normally, false if interrupted
+ */
+ private boolean waitUntilSystemAllowApply() {
try {
Retriable.attemptUntilTrue(
() -> !Utils.stallApply(consensusGroupType),
TimeDuration.ONE_MINUTE,
"waitUntilSystemAllowApply",
logger);
+ return true;
+ } catch (InterruptedException e) {
+ logger.warn(RatisMessages.INTERRUPTED_WAITING_SYSTEM_READY, this, e);
+ Thread.currentThread().interrupt();
+ return false;
+ }
+ }
+
+ /**
+ * @return true if the sleep completed normally, false if interrupted
+ */
+ private boolean waitBeforeRetry() {
+ try {
+ TimeUnit.MINUTES.sleep(1);
+ return true;
} catch (InterruptedException e) {
logger.warn(RatisMessages.INTERRUPTED_WAITING_SYSTEM_READY, this, e);
Thread.currentThread().interrupt();
+ return false;
}
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/consensus/statemachine/dataregion/DataExecutionVisitor.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/consensus/statemachine/dataregion/DataExecutionVisitor.java
index bd48ed25ae7..96bbb6ccc16 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/consensus/statemachine/dataregion/DataExecutionVisitor.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/consensus/statemachine/dataregion/DataExecutionVisitor.java
@@ -21,6 +21,7 @@ package org.apache.iotdb.db.consensus.statemachine.dataregion;
import org.apache.iotdb.common.rpc.thrift.TSStatus;
import org.apache.iotdb.commons.exception.IllegalPathException;
+import org.apache.iotdb.commons.exception.MetadataLeaseFencedException;
import org.apache.iotdb.commons.exception.SemanticException;
import org.apache.iotdb.commons.path.MeasurementPath;
import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNode;
@@ -88,6 +89,8 @@ public class DataExecutionVisitor implements
PlanVisitor<TSStatus, DataRegion> {
} catch (WriteProcessException e) {
LOGGER.error(DataNodeMiscMessages.ERROR_EXECUTING_PLAN_NODE, node, e);
return RpcUtils.getStatus(e.getErrorCode(), e.getMessage());
+ } catch (MetadataLeaseFencedException e) {
+ return RpcUtils.getStatus(e.getErrorCode(), e.getMessage());
}
}
@@ -132,6 +135,8 @@ public class DataExecutionVisitor implements
PlanVisitor<TSStatus, DataRegion> {
}
}
return firstStatus;
+ } catch (final MetadataLeaseFencedException e) {
+ return RpcUtils.getStatus(e.getErrorCode(), e.getMessage());
}
}
@@ -170,6 +175,8 @@ public class DataExecutionVisitor implements
PlanVisitor<TSStatus, DataRegion> {
} catch (SemanticException | TableLostRuntimeException e) {
LOGGER.error(DataNodeMiscMessages.ERROR_EXECUTING_PLAN_NODE_CAUSED,
node, e.getMessage());
return RpcUtils.getStatus(e.getErrorCode(), e.getMessage());
+ } catch (MetadataLeaseFencedException e) {
+ return RpcUtils.getStatus(e.getErrorCode(), e.getMessage());
}
}
@@ -205,6 +212,8 @@ public class DataExecutionVisitor implements
PlanVisitor<TSStatus, DataRegion> {
}
}
return firstStatus;
+ } catch (MetadataLeaseFencedException e) {
+ return RpcUtils.getStatus(e.getErrorCode(), e.getMessage());
}
}
@@ -243,6 +252,8 @@ public class DataExecutionVisitor implements
PlanVisitor<TSStatus, DataRegion> {
}
}
return firstStatus;
+ } catch (MetadataLeaseFencedException e) {
+ return RpcUtils.getStatus(e.getErrorCode(), e.getMessage());
}
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/consensus/statemachine/dataregion/DataRegionStateMachine.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/consensus/statemachine/dataregion/DataRegionStateMachine.java
index 8887054e2f2..051dcbb2771 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/consensus/statemachine/dataregion/DataRegionStateMachine.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/consensus/statemachine/dataregion/DataRegionStateMachine.java
@@ -253,7 +253,7 @@ public class DataRegionStateMachine extends
BaseStateMachine {
while (retryTime < MAX_WRITE_RETRY_TIMES) {
result = planNode.accept(new DataExecutionVisitor(), region);
// Let pipe retry with the original event instead of retrying a possibly
mutated node here.
- if (needRetry(result.getCode()) && !planNode.isGeneratedByPipe()) {
+ if (needRetryForSpecificCases(result.getCode(), planNode)) {
retryTime++;
logger.debug(
DataNodePipeMessages.PIPE_LOG_WRITE_OPERATION_FAILED_BECAUSE_RETRYTIME_34EFBE99,
@@ -330,10 +330,16 @@ public class DataRegionStateMachine extends
BaseStateMachine {
}
}
- public static boolean needRetry(int statusCode) {
- // To fix the atomicity problem, we only need to add retry for system
reject.
+ public static boolean needRetryForSpecificCases(int statusCode, PlanNode
planNode) {
+ // To fix the atomicity problem, retry system rejection and a fenced
metadata lease that
+ // explicitly requires retry.
// In other cases, such as readonly, we can return directly because there
are retries at the
// consensus layer.
- return statusCode == TSStatusCode.WRITE_PROCESS_REJECT.getStatusCode();
+ if (statusCode == TSStatusCode.WRITE_PROCESS_REJECT.getStatusCode()
+ && !planNode.isGeneratedByPipe()) {
+ return true;
+ }
+ return statusCode ==
TSStatusCode.METADATA_LEASE_FENCED_RETRY_REQUIRED.getStatusCode()
+ && !planNode.isGeneratedByPipe();
}
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/partition/PartitionCache.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/partition/PartitionCache.java
index a79eb652cf3..4b8ee72ffd4 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/partition/PartitionCache.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/partition/PartitionCache.java
@@ -30,6 +30,7 @@ import
org.apache.iotdb.commons.client.exception.ClientManagerException;
import org.apache.iotdb.commons.consensus.ConfigRegionId;
import org.apache.iotdb.commons.exception.IoTDBRuntimeException;
import org.apache.iotdb.commons.exception.MetadataException;
+import
org.apache.iotdb.commons.exception.MetadataLeaseFencedException.LeaseFencedRetryPolicy;
import org.apache.iotdb.commons.memory.IMemoryBlock;
import org.apache.iotdb.commons.memory.MemoryBlockType;
import org.apache.iotdb.commons.partition.DataPartition;
@@ -145,7 +146,8 @@ public class PartitionCache {
}
protected void failIfMetadataLeaseFenced() {
- MetadataLeaseManager.getInstance().failIfMetadataLeaseFenced();
+ MetadataLeaseManager.getInstance()
+ .failIfMetadataLeaseFenced(LeaseFencedRetryPolicy.RETRY_UNTIL_SUCCESS);
}
// region database cache
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/fetcher/cache/TreeDeviceSchemaCacheManager.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/fetcher/cache/TreeDeviceSchemaCacheManager.java
index 799f938edd4..29a66bdcdf0 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/fetcher/cache/TreeDeviceSchemaCacheManager.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/metadata/fetcher/cache/TreeDeviceSchemaCacheManager.java
@@ -20,6 +20,7 @@
package org.apache.iotdb.db.queryengine.plan.relational.metadata.fetcher.cache;
import org.apache.iotdb.commons.conf.CommonDescriptor;
+import
org.apache.iotdb.commons.exception.MetadataLeaseFencedException.LeaseFencedRetryPolicy;
import org.apache.iotdb.commons.path.MeasurementPath;
import org.apache.iotdb.commons.path.PartialPath;
import org.apache.iotdb.commons.path.PathPatternUtil;
@@ -74,7 +75,8 @@ public class TreeDeviceSchemaCacheManager {
}
void failIfMetadataLeaseFenced() {
- MetadataLeaseManager.getInstance().failIfMetadataLeaseFenced();
+ MetadataLeaseManager.getInstance()
+ .failIfMetadataLeaseFenced(LeaseFencedRetryPolicy.RETRY_UNTIL_SUCCESS);
}
/** singleton pattern. */
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/lease/MetadataLeaseManager.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/lease/MetadataLeaseManager.java
index 9f00901da56..da198225243 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/lease/MetadataLeaseManager.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/lease/MetadataLeaseManager.java
@@ -22,6 +22,7 @@ package org.apache.iotdb.db.schemaengine.lease;
import org.apache.iotdb.commons.concurrent.IoTDBThreadPoolFactory;
import org.apache.iotdb.commons.concurrent.threadpool.ScheduledExecutorUtil;
import org.apache.iotdb.commons.exception.MetadataLeaseFencedException;
+import
org.apache.iotdb.commons.exception.MetadataLeaseFencedException.LeaseFencedRetryPolicy;
import org.apache.iotdb.commons.utils.TestOnly;
import org.apache.iotdb.db.conf.IoTDBDescriptor;
import org.apache.iotdb.db.i18n.DataNodeSchemaMessages;
@@ -278,9 +279,10 @@ public class MetadataLeaseManager {
* refuse to serve it rather than risk validating writes/queries against
stale schema and
* producing dirty data.
*/
- public void failIfMetadataLeaseFenced() {
+ public void failIfMetadataLeaseFenced(final LeaseFencedRetryPolicy
leaseFencedRetryPolicy) {
if (isFenced()) {
- throw new
MetadataLeaseFencedException(DataNodeSchemaMessages.METADATA_LEASE_IS_FENCED);
+ throw new MetadataLeaseFencedException(
+ DataNodeSchemaMessages.METADATA_LEASE_IS_FENCED,
leaseFencedRetryPolicy);
}
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/table/DataNodeTableCache.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/table/DataNodeTableCache.java
index 5b90104de83..c1bb53a8a90 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/table/DataNodeTableCache.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/table/DataNodeTableCache.java
@@ -24,6 +24,7 @@ import org.apache.iotdb.commons.client.IClientManager;
import org.apache.iotdb.commons.client.exception.ClientManagerException;
import org.apache.iotdb.commons.consensus.ConfigRegionId;
import org.apache.iotdb.commons.exception.IoTDBRuntimeException;
+import
org.apache.iotdb.commons.exception.MetadataLeaseFencedException.LeaseFencedRetryPolicy;
import org.apache.iotdb.commons.exception.SemanticException;
import org.apache.iotdb.commons.schema.table.NonCommittableTsTable;
import org.apache.iotdb.commons.schema.table.PreDeleteTsTable;
@@ -102,8 +103,8 @@ public class DataNodeTableCache implements ITableCache {
return DataNodeTableCacheHolder.INSTANCE;
}
- void failIfMetadataLeaseFenced() {
- MetadataLeaseManager.getInstance().failIfMetadataLeaseFenced();
+ void failIfMetadataLeaseFenced(final LeaseFencedRetryPolicy
leaseFencedRetryPolicy) {
+
MetadataLeaseManager.getInstance().failIfMetadataLeaseFenced(leaseFencedRetryPolicy);
}
@Override
@@ -180,7 +181,7 @@ public class DataNodeTableCache implements ITableCache {
database = PathUtils.unQualifyDatabaseName(database);
readWriteLock.writeLock().lock();
try {
- failIfMetadataLeaseFenced();
+ failIfMetadataLeaseFenced(LeaseFencedRetryPolicy.RETRY_UNTIL_SUCCESS);
specialStatusMap
.computeIfAbsent(database, k -> new ConcurrentHashMap<>())
.compute(
@@ -228,7 +229,7 @@ public class DataNodeTableCache implements ITableCache {
database = PathUtils.unQualifyDatabaseName(database);
readWriteLock.writeLock().lock();
try {
- failIfMetadataLeaseFenced();
+ failIfMetadataLeaseFenced(LeaseFencedRetryPolicy.RETRY_UNTIL_SUCCESS);
// if rollback the drop table procedure, do nothing,
// wait for triggering the action of pull table from CN
final TsTable table = getTableFromSpecialStatusMap(database, tableName);
@@ -299,7 +300,7 @@ public class DataNodeTableCache implements ITableCache {
database = PathUtils.unQualifyDatabaseName(database);
readWriteLock.writeLock().lock();
try {
- failIfMetadataLeaseFenced();
+ failIfMetadataLeaseFenced(LeaseFencedRetryPolicy.RETRY_UNTIL_SUCCESS);
final TsTable newTable = getTableFromSpecialStatusMap(database,
tableName);
if (Objects.isNull(newTable)) {
LOGGER.info(
@@ -422,7 +423,7 @@ public class DataNodeTableCache implements ITableCache {
public Map<String, Map<String, TsTable>> getTableSnapshot() {
readWriteLock.readLock().lock();
try {
- failIfMetadataLeaseFenced();
+ failIfMetadataLeaseFenced(LeaseFencedRetryPolicy.RETRY_UNTIL_SUCCESS);
return databaseTableMap.entrySet().stream()
.collect(
Collectors.toMap(
@@ -444,7 +445,8 @@ public class DataNodeTableCache implements ITableCache {
@Override
public TsTable getTableInWrite(final String database, final String
tableName) {
- final TsTable result = getTableInCache(database, tableName);
+ final TsTable result =
+ getTableInCache(database, tableName,
LeaseFencedRetryPolicy.RETRY_UNTIL_SUCCESS);
return Objects.nonNull(result) ? result : getTable(database, tableName,
false);
}
@@ -459,21 +461,30 @@ public class DataNodeTableCache implements ITableCache {
*/
@Override
public TsTable getTable(String database, final String tableName, final
boolean force) {
+ return getTable(database, tableName, force,
LeaseFencedRetryPolicy.RETRY_UNTIL_SUCCESS);
+ }
+
+ @Override
+ public TsTable getTable(
+ String database,
+ final String tableName,
+ final boolean force,
+ final LeaseFencedRetryPolicy leaseFencedRetryPolicy) {
database = PathUtils.unQualifyDatabaseName(database);
final AtomicReference<TableNodeStatus> tableStatusRef = new
AtomicReference<>();
final Map<String, Map<String, Long>> specialStatusMap =
- mayGetTableInSpecialStatusMap(database, tableName, tableStatusRef);
+ mayGetTableInSpecialStatusMap(database, tableName, tableStatusRef,
leaseFencedRetryPolicy);
if (Objects.nonNull(specialStatusMap) && !specialStatusMap.isEmpty()) {
Map<String, Map<String, TsTable>> fetchedTables =
getTablesInConfigNode(specialStatusMap, tableStatusRef.get());
if (tableStatusRef.get() == TableNodeStatus.USING) {
- updateUsingTable(fetchedTables, specialStatusMap);
+ updateUsingTable(fetchedTables, specialStatusMap,
leaseFencedRetryPolicy);
} else {
- updateDeleteTable(fetchedTables, database, tableName);
+ updateDeleteTable(fetchedTables, database, tableName,
leaseFencedRetryPolicy);
}
}
- final TsTable table = getTableInCache(database, tableName);
+ final TsTable table = getTableInCache(database, tableName,
leaseFencedRetryPolicy);
if (Objects.isNull(table) && force) {
CommonMetadataUtils.throwTableNotExistsException(database, tableName);
}
@@ -483,10 +494,11 @@ public class DataNodeTableCache implements ITableCache {
private Map<String, Map<String, Long>> mayGetTableInSpecialStatusMap(
final String database,
final String tableName,
- final AtomicReference<TableNodeStatus> tableNodeStatus) {
+ final AtomicReference<TableNodeStatus> tableNodeStatus,
+ final LeaseFencedRetryPolicy leaseFencedRetryPolicy) {
readWriteLock.readLock().lock();
try {
- failIfMetadataLeaseFenced();
+ failIfMetadataLeaseFenced(leaseFencedRetryPolicy);
final Map<String, Pair<TsTable, Long>> targetDatabaseMap =
specialStatusMap.get(database);
if (Objects.isNull(targetDatabaseMap)) {
return null;
@@ -560,10 +572,11 @@ public class DataNodeTableCache implements ITableCache {
private void updateUsingTable(
final Map<String, Map<String, TsTable>> fetchedTables,
- final Map<String, Map<String, Long>> previousVersions) {
+ final Map<String, Map<String, Long>> previousVersions,
+ final LeaseFencedRetryPolicy leaseFencedRetryPolicy) {
readWriteLock.writeLock().lock();
try {
- failIfMetadataLeaseFenced();
+ failIfMetadataLeaseFenced(leaseFencedRetryPolicy);
final AtomicBoolean isUpdated = new AtomicBoolean(false);
fetchedTables.forEach(
(qualifiedDatabase, tableInfoMap) -> {
@@ -618,10 +631,11 @@ public class DataNodeTableCache implements ITableCache {
private void updateDeleteTable(
Map<String, Map<String, TsTable>> fetchedTables,
String targetDatabase,
- final String targetTable) {
+ final String targetTable,
+ final LeaseFencedRetryPolicy leaseFencedRetryPolicy) {
readWriteLock.writeLock().lock();
try {
- failIfMetadataLeaseFenced();
+ failIfMetadataLeaseFenced(leaseFencedRetryPolicy);
boolean isUpdated = false;
boolean targetTableIsStillDeleting = false;
@@ -762,10 +776,13 @@ public class DataNodeTableCache implements ITableCache {
return modified ? builder.toString() :
DataNodeSchemaMessages.COMPARE_TABLE_NOT_MODIFIED;
}
- private TsTable getTableInCache(final String database, final String
tableName) {
+ private TsTable getTableInCache(
+ final String database,
+ final String tableName,
+ final LeaseFencedRetryPolicy leaseFencedRetryPolicy) {
readWriteLock.readLock().lock();
try {
- failIfMetadataLeaseFenced();
+ failIfMetadataLeaseFenced(leaseFencedRetryPolicy);
final TsTable result =
databaseTableMap.containsKey(database)
? databaseTableMap.get(database).get(tableName)
@@ -779,7 +796,7 @@ public class DataNodeTableCache implements ITableCache {
}
public boolean isDatabaseExist(final String database) {
- failIfMetadataLeaseFenced();
+ failIfMetadataLeaseFenced(LeaseFencedRetryPolicy.RETRY_UNTIL_SUCCESS);
if (databaseTableMap.containsKey(database)) {
return true;
}
@@ -788,7 +805,7 @@ public class DataNodeTableCache implements ITableCache {
.containsKey(database)) {
readWriteLock.readLock().lock();
try {
- failIfMetadataLeaseFenced();
+ failIfMetadataLeaseFenced(LeaseFencedRetryPolicy.RETRY_UNTIL_SUCCESS);
databaseTableMap.computeIfAbsent(database, k -> new
ConcurrentHashMap<>());
return true;
} finally {
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/table/ITableCache.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/table/ITableCache.java
index 26f67fb1129..13b05058955 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/table/ITableCache.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/table/ITableCache.java
@@ -19,6 +19,7 @@
package org.apache.iotdb.db.schemaengine.table;
+import
org.apache.iotdb.commons.exception.MetadataLeaseFencedException.LeaseFencedRetryPolicy;
import org.apache.iotdb.commons.schema.table.TsTable;
import javax.annotation.Nonnull;
@@ -53,6 +54,12 @@ public interface ITableCache {
TsTable getTable(String database, final String tableName, final boolean
force);
+ TsTable getTable(
+ String database,
+ final String tableName,
+ final boolean force,
+ final LeaseFencedRetryPolicy leaseFencedRetryPolicy);
+
Map<String, Map<String, TsTable>> getTableSnapshot();
String tryGetInternColumnName(
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/DataRegion.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/DataRegion.java
index d2619a9de65..0d14d5b770d 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/DataRegion.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/storageengine/dataregion/DataRegion.java
@@ -31,6 +31,7 @@ import org.apache.iotdb.commons.conf.IoTDBConstant;
import org.apache.iotdb.commons.consensus.DataRegionId;
import org.apache.iotdb.commons.exception.DiskSpaceInsufficientException;
import org.apache.iotdb.commons.exception.MetadataException;
+import
org.apache.iotdb.commons.exception.MetadataLeaseFencedException.LeaseFencedRetryPolicy;
import org.apache.iotdb.commons.file.SystemFileFactory;
import org.apache.iotdb.commons.path.IFullPath;
import org.apache.iotdb.commons.path.MeasurementPath;
@@ -1740,7 +1741,9 @@ public class DataRegion implements IDataRegionForQuery {
t -> {
final String database = getDatabaseName();
- TsTable tsTable =
DataNodeTableCache.getInstance().getTable(database, t, false);
+ TsTable tsTable =
+ DataNodeTableCache.getInstance()
+ .getTable(database, t, false,
LeaseFencedRetryPolicy.RETRY_UNTIL_SUCCESS);
if (tsTable == null) {
// There is a high probability that the leader node has been
executed and is currently
// located in the follower node.
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/consensus/statemachine/dataregion/DataRegionStateMachineTest.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/consensus/statemachine/dataregion/DataRegionStateMachineTest.java
index c6ab83d7b6f..a1373c94603 100644
---
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/consensus/statemachine/dataregion/DataRegionStateMachineTest.java
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/consensus/statemachine/dataregion/DataRegionStateMachineTest.java
@@ -150,6 +150,149 @@ public class DataRegionStateMachineTest {
Assert.assertEquals(2, planNode.getAcceptCount());
}
+ @Test
+ public void testMetadataLeaseFencedNoRetry() {
+ final DataRegionStateMachine stateMachine = new
DataRegionStateMachine(null);
+ final FixedStatusPlanNode planNode =
+ new
FixedStatusPlanNode(TSStatusCode.METADATA_LEASE_FENCED.getStatusCode());
+
+ final TSStatus status = stateMachine.write(planNode);
+
+ Assert.assertEquals(TSStatusCode.METADATA_LEASE_FENCED.getStatusCode(),
status.getCode());
+ Assert.assertEquals(1, planNode.getAcceptCount());
+ }
+
+ @Test
+ public void testMetadataLeaseFencedRetryRequiredRetriesAndFails() {
+ final DataRegionStateMachine stateMachine = new
DataRegionStateMachine(null);
+ final FixedStatusPlanNode planNode =
+ new
FixedStatusPlanNode(TSStatusCode.METADATA_LEASE_FENCED_RETRY_REQUIRED.getStatusCode());
+
+ final TSStatus status = stateMachine.write(planNode);
+
+ Assert.assertEquals(
+ TSStatusCode.METADATA_LEASE_FENCED_RETRY_REQUIRED.getStatusCode(),
status.getCode());
+ Assert.assertEquals(5, planNode.getAcceptCount());
+ }
+
+ @Test
+ public void testMetadataLeaseFencedRetryRequiredRetriesAndSucceeds() {
+ final DataRegionStateMachine stateMachine = new
DataRegionStateMachine(null);
+ final SequenceStatusPlanNode planNode =
+ new SequenceStatusPlanNode(
+ TSStatusCode.METADATA_LEASE_FENCED_RETRY_REQUIRED.getStatusCode(),
+ TSStatusCode.METADATA_LEASE_FENCED_RETRY_REQUIRED.getStatusCode(),
+ TSStatusCode.SUCCESS_STATUS.getStatusCode());
+
+ final TSStatus status = stateMachine.write(planNode);
+
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(),
status.getCode());
+ Assert.assertEquals(3, planNode.getAcceptCount());
+ }
+
+ private static class FixedStatusPlanNode extends PlanNode {
+
+ private final int statusCode;
+ private int acceptCount = 0;
+
+ private FixedStatusPlanNode(final int statusCode) {
+ super(new PlanNodeId("test"));
+ this.statusCode = statusCode;
+ }
+
+ private int getAcceptCount() {
+ return acceptCount;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <R, C> R accept(final IPlanVisitor<R, C> visitor, final C context) {
+ ++acceptCount;
+ return (R) new TSStatus(statusCode);
+ }
+
+ @Override
+ public List<PlanNode> getChildren() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public void addChild(final PlanNode child) {}
+
+ @Override
+ public PlanNode clone() {
+ return new FixedStatusPlanNode(statusCode);
+ }
+
+ @Override
+ public int allowedChildCount() {
+ return NO_CHILD_ALLOWED;
+ }
+
+ @Override
+ public List<String> getOutputColumnNames() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ protected void serializeAttributes(final ByteBuffer byteBuffer) {}
+
+ @Override
+ protected void serializeAttributes(final DataOutputStream stream) throws
IOException {}
+ }
+
+ private static class SequenceStatusPlanNode extends PlanNode {
+
+ private final int[] statusCodes;
+ private int acceptCount = 0;
+
+ private SequenceStatusPlanNode(final int... statusCodes) {
+ super(new PlanNodeId("test"));
+ this.statusCodes = statusCodes;
+ }
+
+ private int getAcceptCount() {
+ return acceptCount;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public <R, C> R accept(final IPlanVisitor<R, C> visitor, final C context) {
+ final int code = statusCodes[Math.min(acceptCount, statusCodes.length -
1)];
+ ++acceptCount;
+ return (R) new TSStatus(code);
+ }
+
+ @Override
+ public List<PlanNode> getChildren() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ public void addChild(final PlanNode child) {}
+
+ @Override
+ public PlanNode clone() {
+ return new SequenceStatusPlanNode(statusCodes);
+ }
+
+ @Override
+ public int allowedChildCount() {
+ return NO_CHILD_ALLOWED;
+ }
+
+ @Override
+ public List<String> getOutputColumnNames() {
+ return Collections.emptyList();
+ }
+
+ @Override
+ protected void serializeAttributes(final ByteBuffer byteBuffer) {}
+
+ @Override
+ protected void serializeAttributes(final DataOutputStream stream) throws
IOException {}
+ }
+
private static class RetryControlledPlanNode extends PlanNode {
private final boolean markAsPipeOnFirstAccept;
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/schemaengine/lease/MetadataLeaseTestUtils.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/schemaengine/lease/MetadataLeaseTestUtils.java
index e59873bf36a..7507d604838 100644
---
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/schemaengine/lease/MetadataLeaseTestUtils.java
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/schemaengine/lease/MetadataLeaseTestUtils.java
@@ -20,6 +20,7 @@
package org.apache.iotdb.db.schemaengine.lease;
import org.apache.iotdb.commons.exception.MetadataLeaseFencedException;
+import
org.apache.iotdb.commons.exception.MetadataLeaseFencedException.LeaseFencedRetryPolicy;
import com.google.common.util.concurrent.MoreExecutors;
@@ -47,10 +48,16 @@ public final class MetadataLeaseTestUtils {
}
public static void failIfMetadataLeaseFenced(final MetadataLeaseManager
manager) {
+ failIfMetadataLeaseFenced(manager, LeaseFencedRetryPolicy.NONE);
+ }
+
+ public static void failIfMetadataLeaseFenced(
+ final MetadataLeaseManager manager, final LeaseFencedRetryPolicy
leaseFencedRetryPolicy) {
manager.checkLeaseStatus();
if (manager.isFenced()) {
throw new MetadataLeaseFencedException(
- "Metadata lease is fenced. The local metadata cache is
unavailable.");
+ "Metadata lease is fenced. The local metadata cache is unavailable.",
+ leaseFencedRetryPolicy);
}
}
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/schemaengine/table/DataNodeTableCacheLeaseTest.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/schemaengine/table/DataNodeTableCacheLeaseTest.java
index 5325f6e349c..00c5388e747 100644
---
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/schemaengine/table/DataNodeTableCacheLeaseTest.java
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/schemaengine/table/DataNodeTableCacheLeaseTest.java
@@ -20,6 +20,7 @@
package org.apache.iotdb.db.schemaengine.table;
import org.apache.iotdb.commons.exception.MetadataLeaseFencedException;
+import
org.apache.iotdb.commons.exception.MetadataLeaseFencedException.LeaseFencedRetryPolicy;
import org.apache.iotdb.commons.schema.table.TsTable;
import org.apache.iotdb.db.schemaengine.lease.MetadataLeaseManager;
import org.apache.iotdb.db.schemaengine.lease.MetadataLeaseTestUtils;
@@ -51,11 +52,12 @@ public class DataNodeTableCacheLeaseTest {
tableCache.invalidateAll();
Mockito.doAnswer(
invocation -> {
- MetadataLeaseTestUtils.failIfMetadataLeaseFenced(leaseManager);
+ MetadataLeaseTestUtils.failIfMetadataLeaseFenced(
+ leaseManager, invocation.getArgument(0));
return null;
})
.when(tableCache)
- .failIfMetadataLeaseFenced();
+ .failIfMetadataLeaseFenced(Mockito.any());
}
@After
@@ -82,9 +84,25 @@ public class DataNodeTableCacheLeaseTest {
assertLeaseFenced(() -> tableCache.commitUpdateTable("root.db", "t",
null));
}
+ @Test
+ public void stateMachineReadCarriesRetryPolicy() {
+ nowNanos.addAndGet((T_FENCE_MS + 1) * 1_000_000L);
+
+ final MetadataLeaseFencedException e =
+ assertThrows(
+ MetadataLeaseFencedException.class,
+ () ->
+ tableCache.getTable(
+ "root.db", "t", false,
LeaseFencedRetryPolicy.RETRY_UNTIL_SUCCESS));
+
+ assertEquals(
+ TSStatusCode.METADATA_LEASE_FENCED_RETRY_REQUIRED.getStatusCode(),
e.getErrorCode());
+ }
+
private static void assertLeaseFenced(final Runnable runnable) {
final MetadataLeaseFencedException e =
assertThrows(MetadataLeaseFencedException.class, runnable::run);
- assertEquals(TSStatusCode.METADATA_LEASE_FENCED.getStatusCode(),
e.getErrorCode());
+ assertEquals(
+ TSStatusCode.METADATA_LEASE_FENCED_RETRY_REQUIRED.getStatusCode(),
e.getErrorCode());
}
}
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/exception/MetadataLeaseFencedException.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/exception/MetadataLeaseFencedException.java
index b87f76bb18f..cc393f2f468 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/exception/MetadataLeaseFencedException.java
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/exception/MetadataLeaseFencedException.java
@@ -23,11 +23,24 @@ import org.apache.iotdb.rpc.TSStatusCode;
public class MetadataLeaseFencedException extends IoTDBRuntimeException {
- public MetadataLeaseFencedException(String message) {
- super(message, TSStatusCode.METADATA_LEASE_FENCED.getStatusCode());
+ public enum LeaseFencedRetryPolicy {
+ NONE,
+ RETRY_UNTIL_SUCCESS
}
- public MetadataLeaseFencedException(Throwable cause) {
- super(cause, TSStatusCode.METADATA_LEASE_FENCED.getStatusCode());
+ public MetadataLeaseFencedException(
+ String message, LeaseFencedRetryPolicy leaseFencedRetryPolicy) {
+ super(message, getStatusCode(leaseFencedRetryPolicy));
+ }
+
+ public MetadataLeaseFencedException(
+ Throwable cause, LeaseFencedRetryPolicy leaseFencedRetryPolicy) {
+ super(cause, getStatusCode(leaseFencedRetryPolicy));
+ }
+
+ private static int getStatusCode(LeaseFencedRetryPolicy
leaseFencedRetryPolicy) {
+ return leaseFencedRetryPolicy == LeaseFencedRetryPolicy.RETRY_UNTIL_SUCCESS
+ ? TSStatusCode.METADATA_LEASE_FENCED_RETRY_REQUIRED.getStatusCode()
+ : TSStatusCode.METADATA_LEASE_FENCED.getStatusCode();
}
}
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/RetryUtils.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/RetryUtils.java
index 56e9d7f401f..b07a95045c7 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/RetryUtils.java
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/RetryUtils.java
@@ -40,6 +40,7 @@ public class RetryUtils {
return statusCode == TSStatusCode.INTERNAL_SERVER_ERROR.getStatusCode()
|| statusCode == TSStatusCode.SYSTEM_READ_ONLY.getStatusCode()
|| statusCode == TSStatusCode.WRITE_PROCESS_REJECT.getStatusCode()
+ || statusCode ==
TSStatusCode.METADATA_LEASE_FENCED_RETRY_REQUIRED.getStatusCode()
|| statusCode == TSStatusCode.EXECUTE_STATEMENT_ERROR.getStatusCode();
}
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/StatusUtils.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/StatusUtils.java
index 246cf6d0ea1..f9cdb35b118 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/StatusUtils.java
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/StatusUtils.java
@@ -68,6 +68,7 @@ public class StatusUtils {
NEED_RETRY.add(TSStatusCode.TOO_MANY_CONCURRENT_QUERIES_ERROR.getStatusCode());
NEED_RETRY.add(TSStatusCode.SYNC_CONNECTION_ERROR.getStatusCode());
NEED_RETRY.add(TSStatusCode.PLAN_FAILED_NETWORK_PARTITION.getStatusCode());
+
NEED_RETRY.add(TSStatusCode.METADATA_LEASE_FENCED_RETRY_REQUIRED.getStatusCode());
}
/**
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/exception/MetadataLeaseFencedException.java
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/RetryStatusUtilsTest.java
similarity index 50%
copy from
iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/exception/MetadataLeaseFencedException.java
copy to
iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/RetryStatusUtilsTest.java
index b87f76bb18f..6ca93ed1bac 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/exception/MetadataLeaseFencedException.java
+++
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/RetryStatusUtilsTest.java
@@ -17,17 +17,32 @@
* under the License.
*/
-package org.apache.iotdb.commons.exception;
+package org.apache.iotdb.commons.utils;
+import org.apache.iotdb.common.rpc.thrift.TSStatus;
import org.apache.iotdb.rpc.TSStatusCode;
-public class MetadataLeaseFencedException extends IoTDBRuntimeException {
+import org.junit.Assert;
+import org.junit.Test;
- public MetadataLeaseFencedException(String message) {
- super(message, TSStatusCode.METADATA_LEASE_FENCED.getStatusCode());
+public class RetryStatusUtilsTest {
+
+ @Test
+ public void testNeedRetryForMetadataLeaseFencedRetryRequired() {
+ Assert.assertTrue(
+ StatusUtils.needRetryHelper(
+ new
TSStatus(TSStatusCode.METADATA_LEASE_FENCED_RETRY_REQUIRED.getStatusCode())));
+ Assert.assertTrue(
+ RetryUtils.needRetryForWrite(
+
TSStatusCode.METADATA_LEASE_FENCED_RETRY_REQUIRED.getStatusCode()));
}
- public MetadataLeaseFencedException(Throwable cause) {
- super(cause, TSStatusCode.METADATA_LEASE_FENCED.getStatusCode());
+ @Test
+ public void testNeedRetryForMetadataLeaseFenced() {
+ Assert.assertFalse(
+ StatusUtils.needRetryHelper(
+ new TSStatus(TSStatusCode.METADATA_LEASE_FENCED.getStatusCode())));
+ Assert.assertFalse(
+
RetryUtils.needRetryForWrite(TSStatusCode.METADATA_LEASE_FENCED.getStatusCode()));
}
}