From 1722b533d1c296dd10a804dc73922112b88434ef Mon Sep 17 00:00:00 2001 From: Caideyipi <87789683+Caideyipi@users.noreply.github.com> Date: Thu, 29 Oct 2026 14:54:11 +0800 Subject: [PATCH 1/9] [Bugfix] Replay Ratis historical TsFiles without Pipe progress Treat MinimumProgressIndex as unknown for Ratis historical extraction so successful writes remain eligible for replay after a leader change. Preserve valid-index and other consensus-protocol coverage filtering. Add a regression test for initial and resumed checkpoints and the unaffected filtering behavior. --- ...icalDataRegionTsFileAndDeletionSource.java | 9 ++++ ...DataRegionTsFileAndDeletionSourceTest.java | 46 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/historical/PipeHistoricalDataRegionTsFileAndDeletionSource.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/historical/PipeHistoricalDataRegionTsFileAndDeletionSource.java index 041d33c2faffb..c47fb606017dd 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/historical/PipeHistoricalDataRegionTsFileAndDeletionSource.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/historical/PipeHistoricalDataRegionTsFileAndDeletionSource.java @@ -42,6 +42,7 @@ import org.apache.iotdb.commons.pipe.event.ProgressReportEvent; import org.apache.iotdb.commons.queryengine.utils.DateTimeUtils; import org.apache.iotdb.commons.utils.PathUtils; +import org.apache.iotdb.consensus.ConsensusFactory; import org.apache.iotdb.consensus.pipe.IoTConsensusV2; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.consensus.DataRegionConsensusImpl; @@ -919,6 +920,14 @@ private boolean mayTsFileContainUnprocessedData(final TsFileResource resource) { private boolean isProgressIndexNotCoveredByStartIndex( PersistentResource resource, ProgressIndex progressIndex) { + // Ratis writes do not carry Pipe progress indexes. Minimum therefore means unknown here, + // rather than an empty file already covered by the initial checkpoint. Replay conservatively + // after a leader change, or successful writes can disappear from the subscription backlog. + if (progressIndex instanceof MinimumProgressIndex + && ConsensusFactory.RATIS_CONSENSUS.equals( + IoTDBDescriptor.getInstance().getConfig().getDataRegionConsensusProtocolClass())) { + return true; + } final ProgressIndex innerStartIndex = getInnerProgressIndex(startIndex); if (innerStartIndex.isEqualOrAfter(progressIndex) || isProgressIndexCoveredByTimePartitionProgressIndex( diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/source/dataregion/historical/PipeHistoricalDataRegionTsFileAndDeletionSourceTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/source/dataregion/historical/PipeHistoricalDataRegionTsFileAndDeletionSourceTest.java index 991dab795f94a..090124c7fc82e 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/source/dataregion/historical/PipeHistoricalDataRegionTsFileAndDeletionSourceTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/source/dataregion/historical/PipeHistoricalDataRegionTsFileAndDeletionSourceTest.java @@ -35,6 +35,8 @@ import org.apache.iotdb.commons.pipe.datastructure.resource.PersistentResource; import org.apache.iotdb.commons.pipe.event.ProgressReportEvent; import org.apache.iotdb.commons.utils.FileUtils; +import org.apache.iotdb.consensus.ConsensusFactory; +import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.pipe.consensus.ReplicateProgressDataNodeManager; import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource; import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResourceStatus; @@ -688,6 +690,50 @@ public void testMayTsFileContainUnprocessedDataUsesEqualOrAfterCoverage() throws } } + @Test + public void testRatisFilesWithoutPipeProgressAreNotCoveredByCheckpoint() throws Exception { + final String originalConsensus = + IoTDBDescriptor.getInstance().getConfig().getDataRegionConsensusProtocolClass(); + final File tempDir = Files.createTempDirectory("pipeHistoricalRatisCoverage").toFile(); + try { + IoTDBDescriptor.getInstance() + .getConfig() + .setDataRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS); + assertMayTsFileContainUnprocessedData( + tempDir, + "unknown-initial.tsfile", + MinimumProgressIndex.INSTANCE, + MinimumProgressIndex.INSTANCE, + true); + assertMayTsFileContainUnprocessedData( + tempDir, + "unknown-recovery.tsfile", + new SimpleProgressIndex(0, 100), + MinimumProgressIndex.INSTANCE, + true); + assertMayTsFileContainUnprocessedData( + tempDir, + "indexed-covered.tsfile", + new SimpleProgressIndex(0, 100), + new SimpleProgressIndex(0, 50), + false); + IoTDBDescriptor.getInstance() + .getConfig() + .setDataRegionConsensusProtocolClass(ConsensusFactory.IOT_CONSENSUS); + assertMayTsFileContainUnprocessedData( + tempDir, + "other-consensus.tsfile", + MinimumProgressIndex.INSTANCE, + MinimumProgressIndex.INSTANCE, + false); + } finally { + IoTDBDescriptor.getInstance() + .getConfig() + .setDataRegionConsensusProtocolClass(originalConsensus); + FileUtils.deleteFileOrDirectory(tempDir); + } + } + @Test public void testMayTsFileContainUnprocessedDataUsesTimePartitionProgressCoverage() throws Exception { From 7d4912d72d1a29c98f52b415d30e79724486714d Mon Sep 17 00:00:00 2001 From: Caideyipi <87789683+Caideyipi@users.noreply.github.com> Date: Thu, 29 Oct 2026 18:32:21 +0800 Subject: [PATCH 2/9] Disable Pipe and Subscription with Ratis --- .../confignode/manager/ConfigManager.java | 29 ++++++++---- .../pipe/agent/PipeConfigNodeAgent.java | 7 +++ .../runtime/PipeConfigNodeRuntimeAgent.java | 5 ++ .../agent/task/PipeConfigNodeTaskAgent.java | 6 ++- .../runtime/PipeRuntimeCoordinator.java | 39 ++++++++++------ .../protocol/IoTDBConfigNodeReceiver.java | 10 +++- .../subscription/SubscriptionCoordinator.java | 7 ++- .../SubscriptionLeaderChangeHandler.java | 9 +++- .../db/pipe/agent/PipeDataNodeAgent.java | 7 +++ .../pipe/agent/runtime/PipeAgentLauncher.java | 4 +- .../runtime/PipeDataNodeRuntimeAgent.java | 20 ++++++-- .../agent/task/PipeDataNodeTaskAgent.java | 6 +++ .../execution/PipeSubtaskExecutorManager.java | 4 +- .../thrift/IoTDBDataNodeReceiver.java | 8 ++++ ...icalDataRegionTsFileAndDeletionSource.java | 9 ---- .../thrift/impl/ClientRPCServiceImpl.java | 29 ++++++++++-- .../impl/DataNodeInternalRPCServiceImpl.java | 31 +++++++++---- .../executor/ClusterConfigTaskExecutor.java | 31 +++++++++++++ .../org/apache/iotdb/db/service/DataNode.java | 9 ++-- .../agent/SubscriptionReceiverAgent.java | 21 ++++++++- .../agent/SubscriptionRuntimeAgent.java | 6 +++ .../ConsensusSubscriptionSetupHandler.java | 7 ++- ...usSubscriptionPrefetchExecutorManager.java | 8 +++- .../thrift/IoTDBDataNodeReceiverTest.java | 24 ++++++++++ ...DataRegionTsFileAndDeletionSourceTest.java | 46 ------------------- .../agent/SubscriptionReceiverAgentTest.java | 30 ++++++++++++ .../iotdb/commons/i18n/PipeMessages.java | 3 ++ .../iotdb/commons/i18n/PipeMessages.java | 3 ++ .../pipe/agent/task/PipeTaskAgent.java | 12 +++++ 29 files changed, 316 insertions(+), 114 deletions(-) diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java index 3e407143e46ee..2d9ee8c23ede9 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java @@ -51,6 +51,7 @@ import org.apache.iotdb.commons.enums.RepairDataPartitionTableProgressState; import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.exception.MetadataException; +import org.apache.iotdb.commons.i18n.PipeMessages; import org.apache.iotdb.commons.path.MeasurementPath; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.path.PathDeserializeUtil; @@ -1731,7 +1732,7 @@ public TGetJarInListResp getTriggerJar(TGetJarInListReq req) { @Override public TSStatus createPipePlugin(TCreatePipePluginReq req) { - TSStatus status = confirmLeader(); + TSStatus status = confirmPipeAndSubscriptionFeatureAvailability(); return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() ? pipeManager.getPipePluginCoordinator().createPipePlugin(req) : status; @@ -1769,6 +1770,18 @@ public TGetJarInListResp getPipePluginJar(TGetJarInListReq req) { : new TGetJarInListResp(status, Collections.emptyList()); } + private TSStatus confirmPipeAndSubscriptionFeatureAvailability() { + final TSStatus status = confirmLeader(); + if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode() + || PipeConfigNodeAgent.isPipeFeatureEnabled()) { + return status; + } + return RpcUtils.getStatus( + TSStatusCode.UNSUPPORTED_OPERATION, + PipeMessages + .MESSAGE_PIPE_AND_SUBSCRIPTION_ARE_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_3D8EAE6E); + } + @Override public TSStatus merge() { TSStatus status = confirmLeader(); @@ -2572,7 +2585,7 @@ public TSStatus alterTimeSeriesDataType(final TAlterTimeSeriesReq req) { @Override public TSStatus createPipe(TCreatePipeReq req) { - TSStatus status = confirmLeader(); + TSStatus status = confirmPipeAndSubscriptionFeatureAvailability(); return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() ? pipeManager.getPipeTaskCoordinator().createPipe(req) : status; @@ -2580,7 +2593,7 @@ public TSStatus createPipe(TCreatePipeReq req) { @Override public TSStatus alterPipe(TAlterPipeReq req) { - TSStatus status = confirmLeader(); + TSStatus status = confirmPipeAndSubscriptionFeatureAvailability(); return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() ? pipeManager.getPipeTaskCoordinator().alterPipe(req) : status; @@ -2588,7 +2601,7 @@ public TSStatus alterPipe(TAlterPipeReq req) { @Override public TSStatus startPipe(TStartPipeReq req) { - TSStatus status = confirmLeader(); + TSStatus status = confirmPipeAndSubscriptionFeatureAvailability(); return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() ? pipeManager.getPipeTaskCoordinator().startPipe(req) : status; @@ -2628,7 +2641,7 @@ public TGetAllPipeInfoResp getAllPipeInfo() { @Override public TSStatus createTopic(TCreateTopicReq req) { - TSStatus status = confirmLeader(); + TSStatus status = confirmPipeAndSubscriptionFeatureAvailability(); return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() ? subscriptionManager.getSubscriptionCoordinator().createTopic(req) : status; @@ -2636,7 +2649,7 @@ public TSStatus createTopic(TCreateTopicReq req) { @Override public TSStatus alterTopic(TAlterTopicReq req) { - TSStatus status = confirmLeader(); + TSStatus status = confirmPipeAndSubscriptionFeatureAvailability(); return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() ? subscriptionManager.getSubscriptionCoordinator().alterTopic(req) : status; @@ -2668,7 +2681,7 @@ public TGetAllTopicInfoResp getAllTopicInfo() { @Override public TSStatus createConsumer(TCreateConsumerReq req) { - TSStatus status = confirmLeader(); + TSStatus status = confirmPipeAndSubscriptionFeatureAvailability(); return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() ? subscriptionManager.getSubscriptionCoordinator().createConsumer(req) : status; @@ -2684,7 +2697,7 @@ public TSStatus closeConsumer(TCloseConsumerReq req) { @Override public TSStatus createSubscription(TSubscribeReq req) { - TSStatus status = confirmLeader(); + TSStatus status = confirmPipeAndSubscriptionFeatureAvailability(); return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() ? subscriptionManager.getSubscriptionCoordinator().createSubscription(req) : status; diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/PipeConfigNodeAgent.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/PipeConfigNodeAgent.java index 834b4e0ce3573..f68e259b5264f 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/PipeConfigNodeAgent.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/PipeConfigNodeAgent.java @@ -19,15 +19,22 @@ package org.apache.iotdb.confignode.manager.pipe.agent; +import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor; import org.apache.iotdb.confignode.manager.pipe.agent.plugin.PipeConfigNodePluginAgent; import org.apache.iotdb.confignode.manager.pipe.agent.receiver.IoTDBConfigNodeReceiverAgent; import org.apache.iotdb.confignode.manager.pipe.agent.runtime.PipeConfigNodeRuntimeAgent; import org.apache.iotdb.confignode.manager.pipe.agent.task.PipeConfigNodeTaskAgent; import org.apache.iotdb.confignode.service.ConfigNode; +import org.apache.iotdb.consensus.ConsensusFactory; /** {@link PipeConfigNodeAgent} is the entry point of the pipe module in {@link ConfigNode}. */ public class PipeConfigNodeAgent { + public static boolean isPipeFeatureEnabled() { + return !ConsensusFactory.RATIS_CONSENSUS.equals( + ConfigNodeDescriptor.getInstance().getConf().getDataRegionConsensusProtocolClass()); + } + private final PipeConfigNodeTaskAgent pipeConfigNodeTaskAgent; private final PipeConfigNodePluginAgent pipeConfigNodePluginAgent; private final PipeConfigNodeRuntimeAgent pipeConfigNodeRuntimeAgent; diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/runtime/PipeConfigNodeRuntimeAgent.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/runtime/PipeConfigNodeRuntimeAgent.java index 966501f06366f..172eabdbf01c5 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/runtime/PipeConfigNodeRuntimeAgent.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/runtime/PipeConfigNodeRuntimeAgent.java @@ -59,6 +59,11 @@ public class PipeConfigNodeRuntimeAgent implements IService { @Override public synchronized void start() { + if (!PipeConfigNodeAgent.isPipeFeatureEnabled()) { + isShutdown.set(true); + return; + } + PipeConfig.getInstance().printAllConfigs(); initLoggerPeriodicalLogReducer(); diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgent.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgent.java index 323fdb8ca0045..1c343222e985c 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgent.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgent.java @@ -31,7 +31,6 @@ import org.apache.iotdb.commons.pipe.agent.task.meta.PipeTemporaryMeta; import org.apache.iotdb.commons.pipe.agent.task.meta.PipeTemporaryMetaInAgent; import org.apache.iotdb.commons.pipe.config.PipeConfig; -import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor; import org.apache.iotdb.confignode.i18n.ManagerMessages; import org.apache.iotdb.confignode.manager.pipe.agent.PipeConfigNodeAgent; import org.apache.iotdb.confignode.manager.pipe.metric.overview.PipeConfigNodeRemainingTimeMetrics; @@ -68,6 +67,11 @@ protected boolean isShutdown() { return PipeConfigNodeAgent.runtime().isShutdown(); } + @Override + protected boolean isPipeFeatureEnabled() { + return PipeConfigNodeAgent.isPipeFeatureEnabled(); + } + @Override protected void thawRate(final String pipeName, final long creationTime) { PipeConfigNodeRemainingTimeMetrics.getInstance().thawRate(pipeName + "_" + creationTime); diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/runtime/PipeRuntimeCoordinator.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/runtime/PipeRuntimeCoordinator.java index cb0da3e0948cc..935db15e0de34 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/runtime/PipeRuntimeCoordinator.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/runtime/PipeRuntimeCoordinator.java @@ -25,6 +25,7 @@ import org.apache.iotdb.confignode.manager.ConfigManager; import org.apache.iotdb.confignode.manager.load.subscriber.ConsensusGroupStatisticsChangeEvent; import org.apache.iotdb.confignode.manager.load.subscriber.IClusterStatusSubscriber; +import org.apache.iotdb.confignode.manager.pipe.agent.PipeConfigNodeAgent; import org.apache.iotdb.confignode.manager.pipe.coordinator.runtime.heartbeat.PipeHeartbeat; import org.apache.iotdb.confignode.manager.pipe.coordinator.runtime.heartbeat.PipeHeartbeatScheduler; @@ -67,17 +68,23 @@ public ExecutorService getProcedureSubmitter() { } public synchronized void onConfigRegionGroupLeaderChanged() { - pipeLeaderChangeHandler.onConfigRegionGroupLeaderChanged(); + if (PipeConfigNodeAgent.isPipeFeatureEnabled()) { + pipeLeaderChangeHandler.onConfigRegionGroupLeaderChanged(); + } } @Override public synchronized void onConsensusGroupStatisticsChanged( final ConsensusGroupStatisticsChangeEvent event) { - pipeLeaderChangeHandler.onConsensusGroupStatisticsChanged(event); + if (PipeConfigNodeAgent.isPipeFeatureEnabled()) { + pipeLeaderChangeHandler.onConsensusGroupStatisticsChanged(event); + } } public void startPipeMetaSync() { - pipeMetaSyncer.start(); + if (PipeConfigNodeAgent.isPipeFeatureEnabled()) { + pipeMetaSyncer.start(); + } } public void stopPipeMetaSync() { @@ -85,7 +92,9 @@ public void stopPipeMetaSync() { } public void startPipeHeartbeat() { - pipeHeartbeatScheduler.start(); + if (PipeConfigNodeAgent.isPipeFeatureEnabled()) { + pipeHeartbeatScheduler.start(); + } } public void stopPipeHeartbeat() { @@ -101,15 +110,17 @@ public void parseHeartbeat( /* @Nullable */ final List pipeDegradedStatusListFromAgent, /* @Nullable */ final List> pipeRecentFailureListFromAgent, /* @Nullable */ final List pipeCompletedDataRegionListFromAgent) { - pipeHeartbeatScheduler.parseHeartbeat( - dataNodeId, - new PipeHeartbeat( - pipeMetaByteBufferListFromDataNode, - pipeCompletedListFromAgent, - pipeRemainingEventCountListFromAgent, - pipeRemainingTimeListFromAgent, - pipeDegradedStatusListFromAgent, - pipeRecentFailureListFromAgent, - pipeCompletedDataRegionListFromAgent)); + if (PipeConfigNodeAgent.isPipeFeatureEnabled()) { + pipeHeartbeatScheduler.parseHeartbeat( + dataNodeId, + new PipeHeartbeat( + pipeMetaByteBufferListFromDataNode, + pipeCompletedListFromAgent, + pipeRemainingEventCountListFromAgent, + pipeRemainingTimeListFromAgent, + pipeDegradedStatusListFromAgent, + pipeRecentFailureListFromAgent, + pipeCompletedDataRegionListFromAgent)); + } } } diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/receiver/protocol/IoTDBConfigNodeReceiver.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/receiver/protocol/IoTDBConfigNodeReceiver.java index 748c8db9acfdf..17b0b56c119a2 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/receiver/protocol/IoTDBConfigNodeReceiver.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/receiver/protocol/IoTDBConfigNodeReceiver.java @@ -28,6 +28,7 @@ import org.apache.iotdb.commons.conf.CommonDescriptor; import org.apache.iotdb.commons.conf.IoTDBConstant; import org.apache.iotdb.commons.exception.pipe.PipeRuntimeOutOfMemoryCriticalException; +import org.apache.iotdb.commons.i18n.PipeMessages; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.path.PathPatternTree; import org.apache.iotdb.commons.path.PathPatternTreeUtils; @@ -53,7 +54,6 @@ import org.apache.iotdb.commons.utils.PathUtils; import org.apache.iotdb.commons.utils.StatusUtils; import org.apache.iotdb.confignode.audit.CNAuditLogger; -import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor; import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlan; import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType; import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorPlan; @@ -97,6 +97,7 @@ import org.apache.iotdb.confignode.consensus.request.write.trigger.UpdateTriggerStateInTablePlan; import org.apache.iotdb.confignode.i18n.ManagerMessages; import org.apache.iotdb.confignode.manager.ConfigManager; +import org.apache.iotdb.confignode.manager.pipe.agent.PipeConfigNodeAgent; import org.apache.iotdb.confignode.manager.pipe.event.PipeConfigRegionSnapshotEvent; import org.apache.iotdb.confignode.manager.pipe.metric.receiver.PipeConfigNodeReceiverMetrics; import org.apache.iotdb.confignode.manager.pipe.receiver.visitor.PipeConfigPhysicalPlanExceptionVisitor; @@ -197,6 +198,13 @@ public TPipeTransferResp receive(final TPipeTransferReq req) { if (Objects.nonNull(authResp)) { return authResp; } + if (!PipeConfigNodeAgent.isPipeFeatureEnabled()) { + return new TPipeTransferResp( + RpcUtils.getStatus( + TSStatusCode.UNSUPPORTED_OPERATION, + PipeMessages + .MESSAGE_PIPE_AND_SUBSCRIPTION_ARE_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_3D8EAE6E)); + } TPipeTransferResp resp; final long startTime = System.nanoTime(); switch (type) { diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/subscription/SubscriptionCoordinator.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/subscription/SubscriptionCoordinator.java index 72db41afe8e70..8ea81a7d172bb 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/subscription/SubscriptionCoordinator.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/subscription/SubscriptionCoordinator.java @@ -31,6 +31,7 @@ import org.apache.iotdb.confignode.consensus.response.subscription.TopicTableResp; import org.apache.iotdb.confignode.i18n.ManagerMessages; import org.apache.iotdb.confignode.manager.ConfigManager; +import org.apache.iotdb.confignode.manager.pipe.agent.PipeConfigNodeAgent; import org.apache.iotdb.confignode.manager.pipe.coordinator.task.PipeTaskCoordinatorLock; import org.apache.iotdb.confignode.persistence.subscription.SubscriptionInfo; import org.apache.iotdb.confignode.rpc.thrift.TAlterTopicReq; @@ -187,8 +188,10 @@ public void unlockTopicAlteration(final String topicName, final boolean isTableM /////////////////////////////// Meta sync /////////////////////////////// public void startSubscriptionMetaSync() { - subscriptionMetaSyncer.start(); - subscriptionOwnerLeaseSyncer.start(); + if (PipeConfigNodeAgent.isPipeFeatureEnabled()) { + subscriptionMetaSyncer.start(); + subscriptionOwnerLeaseSyncer.start(); + } } public void stopSubscriptionMetaSync() { diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/subscription/runtime/SubscriptionLeaderChangeHandler.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/subscription/runtime/SubscriptionLeaderChangeHandler.java index 6b888e424aa9c..fb8dd08b09d1d 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/subscription/runtime/SubscriptionLeaderChangeHandler.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/subscription/runtime/SubscriptionLeaderChangeHandler.java @@ -22,6 +22,7 @@ import org.apache.iotdb.confignode.manager.load.subscriber.ConsensusGroupStatisticsChangeEvent; import org.apache.iotdb.confignode.manager.load.subscriber.IClusterStatusSubscriber; import org.apache.iotdb.confignode.manager.load.subscriber.NodeStatisticsChangeEvent; +import org.apache.iotdb.confignode.manager.pipe.agent.PipeConfigNodeAgent; public class SubscriptionLeaderChangeHandler implements IClusterStatusSubscriber { @@ -33,11 +34,15 @@ public SubscriptionLeaderChangeHandler(final SubscriptionRuntimeCoordinator runt @Override public void onNodeStatisticsChanged(final NodeStatisticsChangeEvent event) { - runtimeCoordinator.handleNodeStatisticsChange(event); + if (PipeConfigNodeAgent.isPipeFeatureEnabled()) { + runtimeCoordinator.handleNodeStatisticsChange(event); + } } @Override public void onConsensusGroupStatisticsChanged(final ConsensusGroupStatisticsChangeEvent event) { - runtimeCoordinator.handleLeaderChangeEvent(event); + if (PipeConfigNodeAgent.isPipeFeatureEnabled()) { + runtimeCoordinator.handleLeaderChangeEvent(event); + } } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/PipeDataNodeAgent.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/PipeDataNodeAgent.java index e350f0a6f4bbb..f3e2ad3b66627 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/PipeDataNodeAgent.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/PipeDataNodeAgent.java @@ -19,6 +19,8 @@ package org.apache.iotdb.db.pipe.agent; +import org.apache.iotdb.consensus.ConsensusFactory; +import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.pipe.agent.plugin.PipeDataNodePluginAgent; import org.apache.iotdb.db.pipe.agent.receiver.PipeDataNodeReceiverAgent; import org.apache.iotdb.db.pipe.agent.runtime.PipeDataNodeRuntimeAgent; @@ -28,6 +30,11 @@ /** {@link PipeDataNodeAgent} is the entry point of the pipe module in {@link DataNode}. */ public class PipeDataNodeAgent { + public static boolean isPipeFeatureEnabled() { + return !ConsensusFactory.RATIS_CONSENSUS.equals( + IoTDBDescriptor.getInstance().getConfig().getDataRegionConsensusProtocolClass()); + } + private final PipeDataNodePluginAgent pipeDataNodePluginAgent; private final PipeDataNodeTaskAgent pipeDataNodeTaskAgent; private final PipeDataNodeRuntimeAgent pipeDataNodeRuntimeAgent; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/runtime/PipeAgentLauncher.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/runtime/PipeAgentLauncher.java index 1b3e210b1e8f4..fb5a2538bc308 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/runtime/PipeAgentLauncher.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/runtime/PipeAgentLauncher.java @@ -62,7 +62,7 @@ private PipeAgentLauncher() { public static synchronized void launchPipePluginAgent( ResourcesInformationHolder resourcesInformationHolder) throws StartupException { - initPipePluginRelatedInstances(); + initializePipePluginRelatedInstances(); if (resourcesInformationHolder.getPipePluginMetaList() == null || resourcesInformationHolder.getPipePluginMetaList().isEmpty()) { @@ -106,7 +106,7 @@ public static synchronized void launchPipePluginAgent( } } - private static void initPipePluginRelatedInstances() throws StartupException { + static void initializePipePluginRelatedInstances() throws StartupException { try { PipePluginExecutableManager.setupAndGetInstance( IOTDB_CONFIG.getPipeTemporaryLibDir(), IOTDB_CONFIG.getPipeLibDir()); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/runtime/PipeDataNodeRuntimeAgent.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/runtime/PipeDataNodeRuntimeAgent.java index 8ca2ee1f52f64..77533b0009a24 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/runtime/PipeDataNodeRuntimeAgent.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/runtime/PipeDataNodeRuntimeAgent.java @@ -85,13 +85,18 @@ public class PipeDataNodeRuntimeAgent implements IService { public synchronized void preparePipeResources( final ResourcesInformationHolder resourcesInformationHolder) throws StartupException { - // Clean sender (connector) hardlink file dir and snapshot dir - PipeDataNodeHardlinkOrCopiedFileDirStartupCleaner.clean(this::registerPeriodicalJob); + if (PipeDataNodeAgent.isPipeFeatureEnabled()) { + // Clean sender (connector) hardlink file dir and snapshot dir + PipeDataNodeHardlinkOrCopiedFileDirStartupCleaner.clean(this::registerPeriodicalJob); - // Clean receiver file dir - PipeDataNodeAgent.receiver().cleanPipeReceiverDirs(); + // Clean receiver file dir + PipeDataNodeAgent.receiver().cleanPipeReceiverDirs(); + + PipeAgentLauncher.launchPipePluginAgent(resourcesInformationHolder); + } else { + PipeAgentLauncher.initializePipePluginRelatedInstances(); + } - PipeAgentLauncher.launchPipePluginAgent(resourcesInformationHolder); simpleProgressIndexAssigner.start(); IoTDBTreePattern.setDevicePathGetter(PipeDataNodeRuntimeAgent::getPath); @@ -133,6 +138,11 @@ private static PartialPath getPath(final IDeviceID device) throws IllegalPathExc @Override public synchronized void start() throws StartupException { + if (!PipeDataNodeAgent.isPipeFeatureEnabled()) { + isShutdown.set(true); + return; + } + PipeConfig.getInstance().printAllConfigs(); pipePeriodicalJobExecutor.start(); pipePeriodicalJobExecutor.runDirectly(PipeAgentLauncher::launchPipeTaskAgent); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgent.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgent.java index 5b7775c231562..ec446a19da0c3 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgent.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgent.java @@ -146,6 +146,11 @@ protected boolean isShutdown() { return PipeDataNodeAgent.runtime().isShutdown(); } + @Override + protected boolean isPipeFeatureEnabled() { + return PipeDataNodeAgent.isPipeFeatureEnabled(); + } + @Override protected Map buildPipeTasks(final PipeMeta pipeMetaFromConfigNode) throws IllegalPathException { @@ -264,6 +269,7 @@ private void restoreCompletedEmptySnapshot(final PipeMeta pipeMeta) { final PipeStaticMeta staticMeta = pipeMeta.getStaticMeta(); if (isShutdown() || !SubscriptionConfig.getInstance().getSubscriptionEnabled() + || !PipeDataNodeAgent.isPipeFeatureEnabled() || pipeMeta.getRuntimeMeta().getStatus().get() != PipeStatus.RUNNING || !PipeStaticMeta.isSubscriptionPipe(staticMeta.getPipeName()) || !staticMeta diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/execution/PipeSubtaskExecutorManager.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/execution/PipeSubtaskExecutorManager.java index f83c23871f516..9fca55c49a2f1 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/execution/PipeSubtaskExecutorManager.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/execution/PipeSubtaskExecutorManager.java @@ -21,6 +21,7 @@ import org.apache.iotdb.commons.consensus.iotv2.container.IoTV2GlobalComponentContainer; import org.apache.iotdb.commons.subscription.config.SubscriptionConfig; +import org.apache.iotdb.db.pipe.agent.PipeDataNodeAgent; import org.apache.iotdb.db.pipe.consensus.IoTConsensusV2SubtaskExecutor; import org.apache.iotdb.db.subscription.task.execution.SubscriptionSubtaskExecutor; @@ -65,7 +66,8 @@ private PipeSubtaskExecutorManager() { } public synchronized void ensureSubscriptionExecutors() { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (!SubscriptionConfig.getInstance().getSubscriptionEnabled() + || !PipeDataNodeAgent.isPipeFeatureEnabled()) { return; } if (subscriptionExecutor == null || subscriptionExecutor.isShutdown()) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java index 342fec4c70fc1..c66237eb81f7e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java @@ -29,6 +29,7 @@ import org.apache.iotdb.commons.exception.IoTDBException; import org.apache.iotdb.commons.exception.IoTDBRuntimeException; import org.apache.iotdb.commons.exception.pipe.PipeRuntimeOutOfMemoryCriticalException; +import org.apache.iotdb.commons.i18n.PipeMessages; import org.apache.iotdb.commons.log.LoggerPeriodicalLogReducer; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.pipe.config.PipeConfig; @@ -238,6 +239,13 @@ public synchronized TPipeTransferResp receive(final TPipeTransferReq req) { if (Objects.nonNull(authResp)) { return authResp; } + if (!PipeDataNodeAgent.isPipeFeatureEnabled()) { + return new TPipeTransferResp( + RpcUtils.getStatus( + TSStatusCode.UNSUPPORTED_OPERATION, + PipeMessages + .MESSAGE_PIPE_AND_SUBSCRIPTION_ARE_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_3D8EAE6E)); + } switch (requestType) { case HANDSHAKE_DATANODE_V1: { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/historical/PipeHistoricalDataRegionTsFileAndDeletionSource.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/historical/PipeHistoricalDataRegionTsFileAndDeletionSource.java index c47fb606017dd..041d33c2faffb 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/historical/PipeHistoricalDataRegionTsFileAndDeletionSource.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/historical/PipeHistoricalDataRegionTsFileAndDeletionSource.java @@ -42,7 +42,6 @@ import org.apache.iotdb.commons.pipe.event.ProgressReportEvent; import org.apache.iotdb.commons.queryengine.utils.DateTimeUtils; import org.apache.iotdb.commons.utils.PathUtils; -import org.apache.iotdb.consensus.ConsensusFactory; import org.apache.iotdb.consensus.pipe.IoTConsensusV2; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.consensus.DataRegionConsensusImpl; @@ -920,14 +919,6 @@ private boolean mayTsFileContainUnprocessedData(final TsFileResource resource) { private boolean isProgressIndexNotCoveredByStartIndex( PersistentResource resource, ProgressIndex progressIndex) { - // Ratis writes do not carry Pipe progress indexes. Minimum therefore means unknown here, - // rather than an empty file already covered by the initial checkpoint. Replay conservatively - // after a leader change, or successful writes can disappear from the subscription backlog. - if (progressIndex instanceof MinimumProgressIndex - && ConsensusFactory.RATIS_CONSENSUS.equals( - IoTDBDescriptor.getInstance().getConfig().getDataRegionConsensusProtocolClass())) { - return true; - } final ProgressIndex innerStartIndex = getInnerProgressIndex(startIndex); if (innerStartIndex.isEqualOrAfter(progressIndex) || isProgressIndexCoveredByTimePartitionProgressIndex( diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java index ad2a85db15203..987e467ec8744 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java @@ -36,6 +36,7 @@ import org.apache.iotdb.commons.conf.IoTDBConstant; import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.exception.IoTDBException; +import org.apache.iotdb.commons.i18n.PipeMessages; import org.apache.iotdb.commons.partition.DataPartition; import org.apache.iotdb.commons.partition.DataPartitionQueryParam; import org.apache.iotdb.commons.path.AlignedFullPath; @@ -3495,9 +3496,20 @@ private TSStatus checkLegacyPipeReceiverPermission() { if (!SESSION_MANAGER.checkLogin(clientSession)) { return getNotLoggedInStatus(); } - return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(clientSession.getUsername(), PrivilegeType.USE_PIPE), - PrivilegeType.USE_PIPE); + final TSStatus permissionStatus = + AuthorityChecker.getTSStatus( + AuthorityChecker.checkSystemPermission( + clientSession.getUsername(), PrivilegeType.USE_PIPE), + PrivilegeType.USE_PIPE); + if (permissionStatus.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + return permissionStatus; + } + return PipeDataNodeAgent.isPipeFeatureEnabled() + ? permissionStatus + : RpcUtils.getStatus( + TSStatusCode.UNSUPPORTED_OPERATION, + PipeMessages + .MESSAGE_PIPE_AND_SUBSCRIPTION_ARE_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_3D8EAE6E); } @Override @@ -3512,6 +3524,15 @@ public TPipeSubscribeResp pipeSubscribe(final TPipeSubscribeReq req) { if (!SESSION_MANAGER.checkLogin(clientSession)) { return getNotLoggedInPipeSubscribeResp(); } + if (!PipeDataNodeAgent.isPipeFeatureEnabled()) { + return new TPipeSubscribeResp( + RpcUtils.getStatus( + TSStatusCode.UNSUPPORTED_OPERATION, + PipeMessages + .MESSAGE_PIPE_AND_SUBSCRIPTION_ARE_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_3D8EAE6E), + PipeSubscribeResponseVersion.VERSION_1.getVersion(), + PipeSubscribeResponseType.ACK.getType()); + } return SubscriptionAgent.receiver().handle(req, clientSession.getUsername()); } finally { @@ -3663,7 +3684,7 @@ public void handleClientExit() { } PipeDataNodeAgent.receiver().thrift().handleClientExit(); PipeDataNodeAgent.receiver().legacy().handleClientExit(); - if (COMMON_CONFIG.getSubscriptionEnabled()) { + if (COMMON_CONFIG.getSubscriptionEnabled() && PipeDataNodeAgent.isPipeFeatureEnabled()) { SubscriptionAgent.receiver().handleClientExit(); } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java index 1f5f95b446020..8814303b70d6b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java @@ -1473,7 +1473,7 @@ public TPushPipeMetaRespExceptionMessage handleSinglePipeMeta(final ByteBuffer p @Override public TPushTopicMetaResp pushTopicMeta(TPushTopicMetaReq req) { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (!isSubscriptionFeatureEnabled()) { return new TPushTopicMetaResp() .setStatus(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode())); } @@ -1501,7 +1501,7 @@ public TPushTopicMetaResp pushTopicMeta(TPushTopicMetaReq req) { @Override public TPushTopicMetaResp pushSingleTopicMeta(TPushSingleTopicMetaReq req) { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (!isSubscriptionFeatureEnabled()) { return new TPushTopicMetaResp() .setStatus(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode())); } @@ -1539,7 +1539,7 @@ public TPushTopicMetaResp pushSingleTopicMeta(TPushSingleTopicMetaReq req) { @Override public TPushTopicMetaResp pushMultiTopicMeta(TPushMultiTopicMetaReq req) { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (!isSubscriptionFeatureEnabled()) { return new TPushTopicMetaResp() .setStatus(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode())); } @@ -1591,7 +1591,7 @@ public TPushTopicMetaResp pushMultiTopicMeta(TPushMultiTopicMetaReq req) { @Override public TSStatus pushTopicOwnerLease(TPushTopicOwnerLeaseReq req) { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (!isSubscriptionFeatureEnabled()) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } try { @@ -1605,7 +1605,7 @@ public TSStatus pushTopicOwnerLease(TPushTopicOwnerLeaseReq req) { @Override public TPushConsumerGroupMetaResp pushConsumerGroupMeta(TPushConsumerGroupMetaReq req) { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (!isSubscriptionFeatureEnabled()) { return new TPushConsumerGroupMetaResp() .setStatus(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode())); } @@ -1634,7 +1634,7 @@ public TPushConsumerGroupMetaResp pushConsumerGroupMeta(TPushConsumerGroupMetaRe @Override public TPushConsumerGroupMetaResp pushSingleConsumerGroupMeta( TPushSingleConsumerGroupMetaReq req) { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (!isSubscriptionFeatureEnabled()) { return new TPushConsumerGroupMetaResp() .setStatus(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode())); } @@ -1669,7 +1669,7 @@ public TPushConsumerGroupMetaResp pushSingleConsumerGroupMeta( @Override public TPullCommitProgressResp pullCommitProgress(TPullCommitProgressReq req) { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (!isSubscriptionFeatureEnabled()) { return new TPullCommitProgressResp(RpcUtils.getStatus(TSStatusCode.UNSUPPORTED_OPERATION)) .setCommitRegionProgress(Collections.emptyMap()) .setSubscriptionProgress(Collections.emptyMap()); @@ -1694,7 +1694,7 @@ public TPullCommitProgressResp pullCommitProgress(TPullCommitProgressReq req) { @Override public TSStatus syncSubscriptionProgress(TSyncSubscriptionProgressReq req) { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (!isSubscriptionFeatureEnabled()) { return RpcUtils.getStatus(TSStatusCode.UNSUPPORTED_OPERATION); } @@ -1719,7 +1719,7 @@ public TSStatus syncSubscriptionProgress(TSyncSubscriptionProgressReq req) { @Override public TSStatus pushSubscriptionRuntime(TPushSubscriptionRuntimeReq req) { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (!isSubscriptionFeatureEnabled()) { return RpcUtils.getStatus(TSStatusCode.UNSUPPORTED_OPERATION); } @@ -2530,7 +2530,7 @@ public TSStatus updateRegionCache(TRegionRouteReq req) { return RpcUtils.getStatus(TSStatusCode.PARTITION_CACHE_UPDATE_ERROR); } - if (SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (isSubscriptionFeatureEnabled()) { // Notify consensus subscription queues of any preferred-writer changes try { ConsensusSubscriptionSetupHandler.onRegionRouteChanged( @@ -3395,8 +3395,19 @@ private TEndPoint getConsensusEndPoint( return nodeLocation.getSchemaRegionConsensusEndPoint(); } + private boolean isSubscriptionFeatureEnabled() { + return SubscriptionConfig.getInstance().getSubscriptionEnabled() + && PipeDataNodeAgent.isPipeFeatureEnabled(); + } + @Override public TSStatus createPipePlugin(TCreatePipePluginInstanceReq req) { + if (!PipeDataNodeAgent.isPipeFeatureEnabled()) { + return RpcUtils.getStatus( + TSStatusCode.UNSUPPORTED_OPERATION, + PipeMessages + .MESSAGE_PIPE_AND_SUBSCRIPTION_ARE_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_3D8EAE6E); + } try { PipePluginMeta pipePluginMeta = PipePluginMeta.deserialize(req.pipePluginMeta); PipeDataNodeAgent.plugin().register(pipePluginMeta, req.jarFile); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java index 251d3f96afe63..55c291ac46294 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java @@ -435,6 +435,8 @@ private ClusterConfigTaskExecutorHolder() {} } private static final SettableFuture SUBSCRIPTION_NOT_ENABLED_ERROR_FUTURE; + private static final SettableFuture + PIPE_AND_SUBSCRIPTION_DISABLED_BY_RATIS_ERROR_FUTURE; static { SUBSCRIPTION_NOT_ENABLED_ERROR_FUTURE = SettableFuture.create(); @@ -442,6 +444,13 @@ private ClusterConfigTaskExecutorHolder() {} new IoTDBException( DataNodeQueryMessages.QUERY_EXCEPTION_SUBSCRIPTION_IS_NOT_ENABLED_7F43DCBB, TSStatusCode.SUBSCRIPTION_NOT_ENABLED_ERROR.getStatusCode())); + + PIPE_AND_SUBSCRIPTION_DISABLED_BY_RATIS_ERROR_FUTURE = SettableFuture.create(); + PIPE_AND_SUBSCRIPTION_DISABLED_BY_RATIS_ERROR_FUTURE.setException( + new IoTDBException( + PipeMessages + .MESSAGE_PIPE_AND_SUBSCRIPTION_ARE_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_3D8EAE6E, + TSStatusCode.UNSUPPORTED_OPERATION.getStatusCode())); } public static ClusterConfigTaskExecutor getInstance() { @@ -1020,6 +1029,10 @@ public SettableFuture showTriggers() { @Override public SettableFuture createPipePlugin( final CreatePipePluginStatement createPipePluginStatement) { + if (!PipeDataNodeAgent.isPipeFeatureEnabled()) { + return PIPE_AND_SUBSCRIPTION_DISABLED_BY_RATIS_ERROR_FUTURE; + } + final SettableFuture future = SettableFuture.create(); final String pluginName = createPipePluginStatement.getPluginName(); final String className = createPipePluginStatement.getClassName(); @@ -2245,6 +2258,10 @@ public SettableFuture unsetSchemaTemplate( @Override public SettableFuture createPipe( final CreatePipeStatement createPipeStatement) { + if (!PipeDataNodeAgent.isPipeFeatureEnabled()) { + return PIPE_AND_SUBSCRIPTION_DISABLED_BY_RATIS_ERROR_FUTURE; + } + final SettableFuture future = SettableFuture.create(); // Verify that Pipe is disabled if TSFile encryption is enabled @@ -2580,6 +2597,10 @@ private void dropPipeIfCreated( @Override public SettableFuture alterPipe(final AlterPipeStatement alterPipeStatement) { + if (!PipeDataNodeAgent.isPipeFeatureEnabled()) { + return PIPE_AND_SUBSCRIPTION_DISABLED_BY_RATIS_ERROR_FUTURE; + } + final SettableFuture future = SettableFuture.create(); // Validate pipe name @@ -2891,6 +2912,10 @@ private static boolean checkSinkType(final Map sinkAttributes) { @Override public SettableFuture startPipe(final StartPipeStatement startPipeStatement) { + if (!PipeDataNodeAgent.isPipeFeatureEnabled()) { + return PIPE_AND_SUBSCRIPTION_DISABLED_BY_RATIS_ERROR_FUTURE; + } + final SettableFuture future = SettableFuture.create(); // Validate pipe name @@ -3163,6 +3188,9 @@ public SettableFuture createTopic( if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { return SUBSCRIPTION_NOT_ENABLED_ERROR_FUTURE; } + if (!PipeDataNodeAgent.isPipeFeatureEnabled()) { + return PIPE_AND_SUBSCRIPTION_DISABLED_BY_RATIS_ERROR_FUTURE; + } final SettableFuture future = SettableFuture.create(); @@ -3237,6 +3265,9 @@ public SettableFuture alterTopic( if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { return SUBSCRIPTION_NOT_ENABLED_ERROR_FUTURE; } + if (!PipeDataNodeAgent.isPipeFeatureEnabled()) { + return PIPE_AND_SUBSCRIPTION_DISABLED_BY_RATIS_ERROR_FUTURE; + } final SettableFuture future = SettableFuture.create(); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/DataNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/DataNode.java index 6bca0cace69cf..3132c1e4689cd 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/DataNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/DataNode.java @@ -301,7 +301,8 @@ protected void start() { // TierManager need DataNodeId to do some operations so the reset method need to be invoked // after DataNode adding TierManager.getInstance().resetFolders(); - if (SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (SubscriptionConfig.getInstance().getSubscriptionEnabled() + && PipeDataNodeAgent.isPipeFeatureEnabled()) { ConsensusSubscriptionSetupHandler.ensureNewRegionListenerRegistered(); } // Active DataNode @@ -942,7 +943,8 @@ private void setUp() throws StartupException, IOException { registerInternalRPCService(); // Register subscription agent before pipe agent - if (SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (SubscriptionConfig.getInstance().getSubscriptionEnabled() + && PipeDataNodeAgent.isPipeFeatureEnabled()) { registerManager.register(SubscriptionAgent.runtime()); } registerManager.register(PipeDataNodeAgent.runtime()); @@ -1394,7 +1396,8 @@ public void stop() { private void initProtocols() throws StartupException { prepareExternalServiceResources(); - if (PipeConfig.getInstance().getPipeAirGapReceiverEnabled()) { + if (PipeConfig.getInstance().getPipeAirGapReceiverEnabled() + && PipeDataNodeAgent.isPipeFeatureEnabled()) { registerManager.register(PipeDataNodeAgent.receiver().airGap()); } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgent.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgent.java index a213eac6dfc1f..ae77238310832 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgent.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgent.java @@ -22,9 +22,11 @@ import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.concurrent.IoTDBThreadPoolFactory; import org.apache.iotdb.commons.concurrent.threadpool.ScheduledExecutorUtil; +import org.apache.iotdb.commons.i18n.PipeMessages; import org.apache.iotdb.commons.subscription.config.SubscriptionConfig; import org.apache.iotdb.db.i18n.DataNodePipeMessages; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.db.pipe.agent.PipeDataNodeAgent; import org.apache.iotdb.db.subscription.receiver.SubscriptionReceiver; import org.apache.iotdb.db.subscription.receiver.SubscriptionReceiverV1; import org.apache.iotdb.rpc.RpcUtils; @@ -63,6 +65,15 @@ public class SubscriptionReceiverAgent { PipeSubscribeResponseVersion.VERSION_1.getVersion(), PipeSubscribeResponseType.ACK.getType()); + private static final TPipeSubscribeResp PIPE_SUBSCRIPTION_DISABLED_BY_RATIS_RESP = + new TPipeSubscribeResp( + RpcUtils.getStatus( + TSStatusCode.UNSUPPORTED_OPERATION, + PipeMessages + .MESSAGE_PIPE_AND_SUBSCRIPTION_ARE_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_3D8EAE6E), + PipeSubscribeResponseVersion.VERSION_1.getVersion(), + PipeSubscribeResponseType.ACK.getType()); + private static final TPipeSubscribeResp SUBSCRIPTION_CONSUMER_FENCED_RESP = new TPipeSubscribeResp( RpcUtils.getStatus( @@ -90,8 +101,11 @@ public class SubscriptionReceiverAgent { SubscriptionReceiverAgent() { this( SubscriptionReceiverV1::new, - SubscriptionConfig.getInstance().getSubscriptionEnabled(), - () -> SubscriptionConfig.getInstance().getSubscriptionEnabled()); + SubscriptionConfig.getInstance().getSubscriptionEnabled() + && PipeDataNodeAgent.isPipeFeatureEnabled(), + () -> + SubscriptionConfig.getInstance().getSubscriptionEnabled() + && PipeDataNodeAgent.isPipeFeatureEnabled()); } SubscriptionReceiverAgent( @@ -129,6 +143,9 @@ public TPipeSubscribeResp handle(final TPipeSubscribeReq req, final String usern PipeSubscribeResponseVersion.VERSION_1.getVersion(), PipeSubscribeResponseType.ACK.getType()); } + if (!PipeDataNodeAgent.isPipeFeatureEnabled()) { + return PIPE_SUBSCRIPTION_DISABLED_BY_RATIS_RESP; + } if (!subscriptionEnabledSupplier.getAsBoolean()) { return SUBSCRIPTION_NOT_ENABLED_ERROR_RESP; } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionRuntimeAgent.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionRuntimeAgent.java index 2962f525e7020..ad37d64a7d5e3 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionRuntimeAgent.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionRuntimeAgent.java @@ -23,6 +23,7 @@ import org.apache.iotdb.commons.service.IService; import org.apache.iotdb.commons.service.ServiceType; import org.apache.iotdb.commons.subscription.config.SubscriptionConfig; +import org.apache.iotdb.db.pipe.agent.PipeDataNodeAgent; import org.apache.iotdb.db.subscription.broker.consensus.ConsensusSubscriptionSetupHandler; import org.apache.iotdb.db.subscription.task.execution.ConsensusSubscriptionPrefetchExecutorManager; @@ -64,6 +65,11 @@ SubscriptionTopicAgent topic() { @Override public void start() throws StartupException { + if (!PipeDataNodeAgent.isPipeFeatureEnabled()) { + isShutdown.set(true); + return; + } + if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { return; } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusSubscriptionSetupHandler.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusSubscriptionSetupHandler.java index 1ae30b123e100..221322ff62cfc 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusSubscriptionSetupHandler.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusSubscriptionSetupHandler.java @@ -38,6 +38,7 @@ import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.consensus.DataRegionConsensusImpl; import org.apache.iotdb.db.i18n.DataNodePipeMessages; +import org.apache.iotdb.db.pipe.agent.PipeDataNodeAgent; import org.apache.iotdb.db.storageengine.StorageEngine; import org.apache.iotdb.db.storageengine.dataregion.DataRegion; import org.apache.iotdb.db.subscription.agent.SubscriptionAgent; @@ -1138,7 +1139,8 @@ public static void handleNewSubscriptions( public static void applyRuntimeState( final TConsensusGroupId groupId, final ConsensusRegionRuntimeState runtimeState) { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (!SubscriptionConfig.getInstance().getSubscriptionEnabled() + || !PipeDataNodeAgent.isPipeFeatureEnabled()) { return; } @@ -1173,7 +1175,8 @@ public static void applyRuntimeState( public static void onRegionRouteChanged( final Map newMap, final long routingTimestamp) { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (!SubscriptionConfig.getInstance().getSubscriptionEnabled() + || !PipeDataNodeAgent.isPipeFeatureEnabled()) { return; } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/task/execution/ConsensusSubscriptionPrefetchExecutorManager.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/task/execution/ConsensusSubscriptionPrefetchExecutorManager.java index 9362a38a58b7e..43d459566f4ce 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/task/execution/ConsensusSubscriptionPrefetchExecutorManager.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/task/execution/ConsensusSubscriptionPrefetchExecutorManager.java @@ -20,6 +20,7 @@ package org.apache.iotdb.db.subscription.task.execution; import org.apache.iotdb.commons.subscription.config.SubscriptionConfig; +import org.apache.iotdb.db.pipe.agent.PipeDataNodeAgent; public class ConsensusSubscriptionPrefetchExecutorManager { @@ -31,7 +32,8 @@ private ConsensusSubscriptionPrefetchExecutorManager() { } public synchronized void start() { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (!SubscriptionConfig.getInstance().getSubscriptionEnabled() + || !PipeDataNodeAgent.isPipeFeatureEnabled()) { started = false; return; } @@ -42,7 +44,9 @@ public synchronized void start() { } public synchronized ConsensusSubscriptionPrefetchExecutor getExecutor() { - if (!started || !SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (!started + || !SubscriptionConfig.getInstance().getSubscriptionEnabled() + || !PipeDataNodeAgent.isPipeFeatureEnabled()) { return null; } if (executor == null || executor.isShutdown()) { diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiverTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiverTest.java index b71542a0cc0d1..c03c73eaca6fb 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiverTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiverTest.java @@ -24,7 +24,9 @@ import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.pipe.receiver.runtime.PipeReceiverRuntimeRegistry; import org.apache.iotdb.commons.pipe.receiver.runtime.PipeReceiverRuntimeSnapshot; +import org.apache.iotdb.consensus.ConsensusFactory; import org.apache.iotdb.db.conf.IoTDBDescriptor; +import org.apache.iotdb.db.pipe.sink.payload.evolvable.request.PipeTransferDataNodeHandshakeV2Req; import org.apache.iotdb.db.pipe.sink.payload.evolvable.request.PipeTransferTsFileSealWithModReq; import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertMultiTabletsStatement; import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowStatement; @@ -35,6 +37,7 @@ import org.apache.iotdb.db.storageengine.load.config.LoadTsFileConfigurator; import org.apache.iotdb.db.storageengine.load.converter.PipeTsFileConversionTaskManager; import org.apache.iotdb.rpc.TSStatusCode; +import org.apache.iotdb.service.rpc.thrift.TPipeTransferReq; import org.junit.After; import org.junit.Assert; @@ -64,6 +67,27 @@ public void tearDown() { registry.clear(); } + @Test + public void testRatisRejectsPipeTransfer() throws Exception { + final String originalConsensus = + IoTDBDescriptor.getInstance().getConfig().getDataRegionConsensusProtocolClass(); + try { + IoTDBDescriptor.getInstance() + .getConfig() + .setDataRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS); + + final TPipeTransferReq request = + PipeTransferDataNodeHandshakeV2Req.toTPipeTransferReq(Collections.emptyMap()); + Assert.assertEquals( + TSStatusCode.UNSUPPORTED_OPERATION.getStatusCode(), + new IoTDBDataNodeReceiver().receive(request).getStatus().getCode()); + } finally { + IoTDBDescriptor.getInstance() + .getConfig() + .setDataRegionConsensusProtocolClass(originalConsensus); + } + } + @Test public void testTableModelTabletRedirectDoesNotCachePerRowLeader() { final InsertTabletStatement statement = new InsertTabletStatement(); diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/source/dataregion/historical/PipeHistoricalDataRegionTsFileAndDeletionSourceTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/source/dataregion/historical/PipeHistoricalDataRegionTsFileAndDeletionSourceTest.java index 090124c7fc82e..991dab795f94a 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/source/dataregion/historical/PipeHistoricalDataRegionTsFileAndDeletionSourceTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/source/dataregion/historical/PipeHistoricalDataRegionTsFileAndDeletionSourceTest.java @@ -35,8 +35,6 @@ import org.apache.iotdb.commons.pipe.datastructure.resource.PersistentResource; import org.apache.iotdb.commons.pipe.event.ProgressReportEvent; import org.apache.iotdb.commons.utils.FileUtils; -import org.apache.iotdb.consensus.ConsensusFactory; -import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.pipe.consensus.ReplicateProgressDataNodeManager; import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource; import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResourceStatus; @@ -690,50 +688,6 @@ public void testMayTsFileContainUnprocessedDataUsesEqualOrAfterCoverage() throws } } - @Test - public void testRatisFilesWithoutPipeProgressAreNotCoveredByCheckpoint() throws Exception { - final String originalConsensus = - IoTDBDescriptor.getInstance().getConfig().getDataRegionConsensusProtocolClass(); - final File tempDir = Files.createTempDirectory("pipeHistoricalRatisCoverage").toFile(); - try { - IoTDBDescriptor.getInstance() - .getConfig() - .setDataRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS); - assertMayTsFileContainUnprocessedData( - tempDir, - "unknown-initial.tsfile", - MinimumProgressIndex.INSTANCE, - MinimumProgressIndex.INSTANCE, - true); - assertMayTsFileContainUnprocessedData( - tempDir, - "unknown-recovery.tsfile", - new SimpleProgressIndex(0, 100), - MinimumProgressIndex.INSTANCE, - true); - assertMayTsFileContainUnprocessedData( - tempDir, - "indexed-covered.tsfile", - new SimpleProgressIndex(0, 100), - new SimpleProgressIndex(0, 50), - false); - IoTDBDescriptor.getInstance() - .getConfig() - .setDataRegionConsensusProtocolClass(ConsensusFactory.IOT_CONSENSUS); - assertMayTsFileContainUnprocessedData( - tempDir, - "other-consensus.tsfile", - MinimumProgressIndex.INSTANCE, - MinimumProgressIndex.INSTANCE, - false); - } finally { - IoTDBDescriptor.getInstance() - .getConfig() - .setDataRegionConsensusProtocolClass(originalConsensus); - FileUtils.deleteFileOrDirectory(tempDir); - } - } - @Test public void testMayTsFileContainUnprocessedDataUsesTimePartitionProgressCoverage() throws Exception { diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgentTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgentTest.java index 21ef6ecb101de..fc68f97c6e856 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgentTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgentTest.java @@ -20,6 +20,8 @@ package org.apache.iotdb.db.subscription.agent; import org.apache.iotdb.common.rpc.thrift.TSStatus; +import org.apache.iotdb.consensus.ConsensusFactory; +import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.subscription.receiver.SubscriptionReceiver; import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; @@ -54,6 +56,34 @@ public class SubscriptionReceiverAgentTest { + @Test + public void testRatisRejectsSubscriptionBeforeCreatingReceiver() throws IOException { + final String originalConsensus = + IoTDBDescriptor.getInstance().getConfig().getDataRegionConsensusProtocolClass(); + try { + IoTDBDescriptor.getInstance() + .getConfig() + .setDataRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS); + final SubscriptionReceiverAgent agent = + new SubscriptionReceiverAgent( + () -> { + throw new AssertionError("Receiver must not be created when Ratis is enabled"); + }, + false, + () -> { + throw new AssertionError("Subscription supplier must not be called"); + }); + + Assert.assertEquals( + TSStatusCode.UNSUPPORTED_OPERATION.getStatusCode(), + agent.handle(createHandshakeRequest("group", "consumer"), "root").getStatus().getCode()); + } finally { + IoTDBDescriptor.getInstance() + .getConfig() + .setDataRegionConsensusProtocolClass(originalConsensus); + } + } + @Test public void testDisabledSubscriptionRejectsRequest() throws IOException { final SubscriptionReceiverAgent agent = diff --git a/iotdb-core/node-commons/src/main/i18n/en/org/apache/iotdb/commons/i18n/PipeMessages.java b/iotdb-core/node-commons/src/main/i18n/en/org/apache/iotdb/commons/i18n/PipeMessages.java index 0aafc35842770..b06c658a9a2e9 100644 --- a/iotdb-core/node-commons/src/main/i18n/en/org/apache/iotdb/commons/i18n/PipeMessages.java +++ b/iotdb-core/node-commons/src/main/i18n/en/org/apache/iotdb/commons/i18n/PipeMessages.java @@ -21,6 +21,9 @@ public final class PipeMessages { + public static final String MESSAGE_PIPE_AND_SUBSCRIPTION_ARE_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_3D8EAE6E = + "Pipe and Subscription are disabled when data region consensus protocol is Ratis."; + // ===================== Common / Shared ===================== public static final String UTILITY_CLASS = "Utility class"; diff --git a/iotdb-core/node-commons/src/main/i18n/zh/org/apache/iotdb/commons/i18n/PipeMessages.java b/iotdb-core/node-commons/src/main/i18n/zh/org/apache/iotdb/commons/i18n/PipeMessages.java index 9746b71500d2d..720f8206cea84 100644 --- a/iotdb-core/node-commons/src/main/i18n/zh/org/apache/iotdb/commons/i18n/PipeMessages.java +++ b/iotdb-core/node-commons/src/main/i18n/zh/org/apache/iotdb/commons/i18n/PipeMessages.java @@ -21,6 +21,9 @@ public final class PipeMessages { + public static final String MESSAGE_PIPE_AND_SUBSCRIPTION_ARE_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_3D8EAE6E = + "当数据区域共识协议为 Ratis 时,Pipe 和 Subscription 功能不可用。"; + // ===================== Common / Shared ===================== public static final String UTILITY_CLASS = "工具类"; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/PipeTaskAgent.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/PipeTaskAgent.java index 34bad05803fc5..767d1cb65169b 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/PipeTaskAgent.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/PipeTaskAgent.java @@ -209,6 +209,10 @@ protected TPushPipeMetaRespExceptionMessage handleSinglePipeMetaChangesInternal( protected abstract boolean isShutdown(); + protected boolean isPipeFeatureEnabled() { + return true; + } + private void executeSinglePipeMetaChanges(final PipeMeta metaFromCoordinator) throws IllegalPathException { final String pipeName = metaFromCoordinator.getStaticMeta().getPipeName(); @@ -226,6 +230,14 @@ private void executeSinglePipeMetaChanges(final PipeMeta metaFromCoordinator) return; } + if (!isPipeFeatureEnabled()) { + // Ignore active metadata while disabled, but still process drops to clean up stale tasks. + if (metaFromCoordinator.getRuntimeMeta().getStatus().get() == PipeStatus.DROPPED) { + dropPipe(pipeName, creationTime); + } + return; + } + if (metaFromCoordinator.getRuntimeMeta().getStatus().get() == PipeStatus.DROPPED) { dropPipe(pipeName, creationTime); return; From e2d213723e2213957dc8672128283f4cd270e0bf Mon Sep 17 00:00:00 2001 From: Caideyipi <87789683+Caideyipi@users.noreply.github.com> Date: Thu, 29 Oct 2026 18:49:39 +0800 Subject: [PATCH 3/9] Restore ConfigNode descriptor imports --- .../manager/pipe/agent/task/PipeConfigNodeTaskAgent.java | 1 + .../manager/pipe/receiver/protocol/IoTDBConfigNodeReceiver.java | 1 + 2 files changed, 2 insertions(+) diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgent.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgent.java index 1c343222e985c..b68e63f4ae8ad 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgent.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgent.java @@ -31,6 +31,7 @@ import org.apache.iotdb.commons.pipe.agent.task.meta.PipeTemporaryMeta; import org.apache.iotdb.commons.pipe.agent.task.meta.PipeTemporaryMetaInAgent; import org.apache.iotdb.commons.pipe.config.PipeConfig; +import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor; import org.apache.iotdb.confignode.i18n.ManagerMessages; import org.apache.iotdb.confignode.manager.pipe.agent.PipeConfigNodeAgent; import org.apache.iotdb.confignode.manager.pipe.metric.overview.PipeConfigNodeRemainingTimeMetrics; diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/receiver/protocol/IoTDBConfigNodeReceiver.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/receiver/protocol/IoTDBConfigNodeReceiver.java index 17b0b56c119a2..6057f3f17c2e5 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/receiver/protocol/IoTDBConfigNodeReceiver.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/receiver/protocol/IoTDBConfigNodeReceiver.java @@ -54,6 +54,7 @@ import org.apache.iotdb.commons.utils.PathUtils; import org.apache.iotdb.commons.utils.StatusUtils; import org.apache.iotdb.confignode.audit.CNAuditLogger; +import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor; import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlan; import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType; import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorPlan; From 6a0c76d4f202e8bc95e7e8e726a5cac4d4d26052 Mon Sep 17 00:00:00 2001 From: Caideyipi <87789683+Caideyipi@users.noreply.github.com> Date: Fri, 30 Oct 2026 10:01:39 +0800 Subject: [PATCH 4/9] Revert "Disable Pipe and Subscription with Ratis" This reverts commit 7d4912d72d1a29c98f52b415d30e79724486714d. --- .../confignode/manager/ConfigManager.java | 29 ++++-------- .../pipe/agent/PipeConfigNodeAgent.java | 7 --- .../runtime/PipeConfigNodeRuntimeAgent.java | 5 -- .../agent/task/PipeConfigNodeTaskAgent.java | 5 -- .../runtime/PipeRuntimeCoordinator.java | 39 ++++++---------- .../protocol/IoTDBConfigNodeReceiver.java | 9 ---- .../subscription/SubscriptionCoordinator.java | 7 +-- .../SubscriptionLeaderChangeHandler.java | 9 +--- .../db/pipe/agent/PipeDataNodeAgent.java | 7 --- .../pipe/agent/runtime/PipeAgentLauncher.java | 4 +- .../runtime/PipeDataNodeRuntimeAgent.java | 20 ++------ .../agent/task/PipeDataNodeTaskAgent.java | 6 --- .../execution/PipeSubtaskExecutorManager.java | 4 +- .../thrift/IoTDBDataNodeReceiver.java | 8 ---- ...icalDataRegionTsFileAndDeletionSource.java | 9 ++++ .../thrift/impl/ClientRPCServiceImpl.java | 29 ++---------- .../impl/DataNodeInternalRPCServiceImpl.java | 31 ++++--------- .../executor/ClusterConfigTaskExecutor.java | 31 ------------- .../org/apache/iotdb/db/service/DataNode.java | 9 ++-- .../agent/SubscriptionReceiverAgent.java | 21 +-------- .../agent/SubscriptionRuntimeAgent.java | 6 --- .../ConsensusSubscriptionSetupHandler.java | 7 +-- ...usSubscriptionPrefetchExecutorManager.java | 8 +--- .../thrift/IoTDBDataNodeReceiverTest.java | 24 ---------- ...DataRegionTsFileAndDeletionSourceTest.java | 46 +++++++++++++++++++ .../agent/SubscriptionReceiverAgentTest.java | 30 ------------ .../iotdb/commons/i18n/PipeMessages.java | 3 -- .../iotdb/commons/i18n/PipeMessages.java | 3 -- .../pipe/agent/task/PipeTaskAgent.java | 12 ----- 29 files changed, 112 insertions(+), 316 deletions(-) diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java index 2d9ee8c23ede9..3e407143e46ee 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java @@ -51,7 +51,6 @@ import org.apache.iotdb.commons.enums.RepairDataPartitionTableProgressState; import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.exception.MetadataException; -import org.apache.iotdb.commons.i18n.PipeMessages; import org.apache.iotdb.commons.path.MeasurementPath; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.path.PathDeserializeUtil; @@ -1732,7 +1731,7 @@ public TGetJarInListResp getTriggerJar(TGetJarInListReq req) { @Override public TSStatus createPipePlugin(TCreatePipePluginReq req) { - TSStatus status = confirmPipeAndSubscriptionFeatureAvailability(); + TSStatus status = confirmLeader(); return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() ? pipeManager.getPipePluginCoordinator().createPipePlugin(req) : status; @@ -1770,18 +1769,6 @@ public TGetJarInListResp getPipePluginJar(TGetJarInListReq req) { : new TGetJarInListResp(status, Collections.emptyList()); } - private TSStatus confirmPipeAndSubscriptionFeatureAvailability() { - final TSStatus status = confirmLeader(); - if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode() - || PipeConfigNodeAgent.isPipeFeatureEnabled()) { - return status; - } - return RpcUtils.getStatus( - TSStatusCode.UNSUPPORTED_OPERATION, - PipeMessages - .MESSAGE_PIPE_AND_SUBSCRIPTION_ARE_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_3D8EAE6E); - } - @Override public TSStatus merge() { TSStatus status = confirmLeader(); @@ -2585,7 +2572,7 @@ public TSStatus alterTimeSeriesDataType(final TAlterTimeSeriesReq req) { @Override public TSStatus createPipe(TCreatePipeReq req) { - TSStatus status = confirmPipeAndSubscriptionFeatureAvailability(); + TSStatus status = confirmLeader(); return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() ? pipeManager.getPipeTaskCoordinator().createPipe(req) : status; @@ -2593,7 +2580,7 @@ public TSStatus createPipe(TCreatePipeReq req) { @Override public TSStatus alterPipe(TAlterPipeReq req) { - TSStatus status = confirmPipeAndSubscriptionFeatureAvailability(); + TSStatus status = confirmLeader(); return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() ? pipeManager.getPipeTaskCoordinator().alterPipe(req) : status; @@ -2601,7 +2588,7 @@ public TSStatus alterPipe(TAlterPipeReq req) { @Override public TSStatus startPipe(TStartPipeReq req) { - TSStatus status = confirmPipeAndSubscriptionFeatureAvailability(); + TSStatus status = confirmLeader(); return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() ? pipeManager.getPipeTaskCoordinator().startPipe(req) : status; @@ -2641,7 +2628,7 @@ public TGetAllPipeInfoResp getAllPipeInfo() { @Override public TSStatus createTopic(TCreateTopicReq req) { - TSStatus status = confirmPipeAndSubscriptionFeatureAvailability(); + TSStatus status = confirmLeader(); return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() ? subscriptionManager.getSubscriptionCoordinator().createTopic(req) : status; @@ -2649,7 +2636,7 @@ public TSStatus createTopic(TCreateTopicReq req) { @Override public TSStatus alterTopic(TAlterTopicReq req) { - TSStatus status = confirmPipeAndSubscriptionFeatureAvailability(); + TSStatus status = confirmLeader(); return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() ? subscriptionManager.getSubscriptionCoordinator().alterTopic(req) : status; @@ -2681,7 +2668,7 @@ public TGetAllTopicInfoResp getAllTopicInfo() { @Override public TSStatus createConsumer(TCreateConsumerReq req) { - TSStatus status = confirmPipeAndSubscriptionFeatureAvailability(); + TSStatus status = confirmLeader(); return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() ? subscriptionManager.getSubscriptionCoordinator().createConsumer(req) : status; @@ -2697,7 +2684,7 @@ public TSStatus closeConsumer(TCloseConsumerReq req) { @Override public TSStatus createSubscription(TSubscribeReq req) { - TSStatus status = confirmPipeAndSubscriptionFeatureAvailability(); + TSStatus status = confirmLeader(); return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() ? subscriptionManager.getSubscriptionCoordinator().createSubscription(req) : status; diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/PipeConfigNodeAgent.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/PipeConfigNodeAgent.java index f68e259b5264f..834b4e0ce3573 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/PipeConfigNodeAgent.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/PipeConfigNodeAgent.java @@ -19,22 +19,15 @@ package org.apache.iotdb.confignode.manager.pipe.agent; -import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor; import org.apache.iotdb.confignode.manager.pipe.agent.plugin.PipeConfigNodePluginAgent; import org.apache.iotdb.confignode.manager.pipe.agent.receiver.IoTDBConfigNodeReceiverAgent; import org.apache.iotdb.confignode.manager.pipe.agent.runtime.PipeConfigNodeRuntimeAgent; import org.apache.iotdb.confignode.manager.pipe.agent.task.PipeConfigNodeTaskAgent; import org.apache.iotdb.confignode.service.ConfigNode; -import org.apache.iotdb.consensus.ConsensusFactory; /** {@link PipeConfigNodeAgent} is the entry point of the pipe module in {@link ConfigNode}. */ public class PipeConfigNodeAgent { - public static boolean isPipeFeatureEnabled() { - return !ConsensusFactory.RATIS_CONSENSUS.equals( - ConfigNodeDescriptor.getInstance().getConf().getDataRegionConsensusProtocolClass()); - } - private final PipeConfigNodeTaskAgent pipeConfigNodeTaskAgent; private final PipeConfigNodePluginAgent pipeConfigNodePluginAgent; private final PipeConfigNodeRuntimeAgent pipeConfigNodeRuntimeAgent; diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/runtime/PipeConfigNodeRuntimeAgent.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/runtime/PipeConfigNodeRuntimeAgent.java index 172eabdbf01c5..966501f06366f 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/runtime/PipeConfigNodeRuntimeAgent.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/runtime/PipeConfigNodeRuntimeAgent.java @@ -59,11 +59,6 @@ public class PipeConfigNodeRuntimeAgent implements IService { @Override public synchronized void start() { - if (!PipeConfigNodeAgent.isPipeFeatureEnabled()) { - isShutdown.set(true); - return; - } - PipeConfig.getInstance().printAllConfigs(); initLoggerPeriodicalLogReducer(); diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgent.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgent.java index b68e63f4ae8ad..323fdb8ca0045 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgent.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgent.java @@ -68,11 +68,6 @@ protected boolean isShutdown() { return PipeConfigNodeAgent.runtime().isShutdown(); } - @Override - protected boolean isPipeFeatureEnabled() { - return PipeConfigNodeAgent.isPipeFeatureEnabled(); - } - @Override protected void thawRate(final String pipeName, final long creationTime) { PipeConfigNodeRemainingTimeMetrics.getInstance().thawRate(pipeName + "_" + creationTime); diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/runtime/PipeRuntimeCoordinator.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/runtime/PipeRuntimeCoordinator.java index 935db15e0de34..cb0da3e0948cc 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/runtime/PipeRuntimeCoordinator.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/runtime/PipeRuntimeCoordinator.java @@ -25,7 +25,6 @@ import org.apache.iotdb.confignode.manager.ConfigManager; import org.apache.iotdb.confignode.manager.load.subscriber.ConsensusGroupStatisticsChangeEvent; import org.apache.iotdb.confignode.manager.load.subscriber.IClusterStatusSubscriber; -import org.apache.iotdb.confignode.manager.pipe.agent.PipeConfigNodeAgent; import org.apache.iotdb.confignode.manager.pipe.coordinator.runtime.heartbeat.PipeHeartbeat; import org.apache.iotdb.confignode.manager.pipe.coordinator.runtime.heartbeat.PipeHeartbeatScheduler; @@ -68,23 +67,17 @@ public ExecutorService getProcedureSubmitter() { } public synchronized void onConfigRegionGroupLeaderChanged() { - if (PipeConfigNodeAgent.isPipeFeatureEnabled()) { - pipeLeaderChangeHandler.onConfigRegionGroupLeaderChanged(); - } + pipeLeaderChangeHandler.onConfigRegionGroupLeaderChanged(); } @Override public synchronized void onConsensusGroupStatisticsChanged( final ConsensusGroupStatisticsChangeEvent event) { - if (PipeConfigNodeAgent.isPipeFeatureEnabled()) { - pipeLeaderChangeHandler.onConsensusGroupStatisticsChanged(event); - } + pipeLeaderChangeHandler.onConsensusGroupStatisticsChanged(event); } public void startPipeMetaSync() { - if (PipeConfigNodeAgent.isPipeFeatureEnabled()) { - pipeMetaSyncer.start(); - } + pipeMetaSyncer.start(); } public void stopPipeMetaSync() { @@ -92,9 +85,7 @@ public void stopPipeMetaSync() { } public void startPipeHeartbeat() { - if (PipeConfigNodeAgent.isPipeFeatureEnabled()) { - pipeHeartbeatScheduler.start(); - } + pipeHeartbeatScheduler.start(); } public void stopPipeHeartbeat() { @@ -110,17 +101,15 @@ public void parseHeartbeat( /* @Nullable */ final List pipeDegradedStatusListFromAgent, /* @Nullable */ final List> pipeRecentFailureListFromAgent, /* @Nullable */ final List pipeCompletedDataRegionListFromAgent) { - if (PipeConfigNodeAgent.isPipeFeatureEnabled()) { - pipeHeartbeatScheduler.parseHeartbeat( - dataNodeId, - new PipeHeartbeat( - pipeMetaByteBufferListFromDataNode, - pipeCompletedListFromAgent, - pipeRemainingEventCountListFromAgent, - pipeRemainingTimeListFromAgent, - pipeDegradedStatusListFromAgent, - pipeRecentFailureListFromAgent, - pipeCompletedDataRegionListFromAgent)); - } + pipeHeartbeatScheduler.parseHeartbeat( + dataNodeId, + new PipeHeartbeat( + pipeMetaByteBufferListFromDataNode, + pipeCompletedListFromAgent, + pipeRemainingEventCountListFromAgent, + pipeRemainingTimeListFromAgent, + pipeDegradedStatusListFromAgent, + pipeRecentFailureListFromAgent, + pipeCompletedDataRegionListFromAgent)); } } diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/receiver/protocol/IoTDBConfigNodeReceiver.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/receiver/protocol/IoTDBConfigNodeReceiver.java index 6057f3f17c2e5..748c8db9acfdf 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/receiver/protocol/IoTDBConfigNodeReceiver.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/receiver/protocol/IoTDBConfigNodeReceiver.java @@ -28,7 +28,6 @@ import org.apache.iotdb.commons.conf.CommonDescriptor; import org.apache.iotdb.commons.conf.IoTDBConstant; import org.apache.iotdb.commons.exception.pipe.PipeRuntimeOutOfMemoryCriticalException; -import org.apache.iotdb.commons.i18n.PipeMessages; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.path.PathPatternTree; import org.apache.iotdb.commons.path.PathPatternTreeUtils; @@ -98,7 +97,6 @@ import org.apache.iotdb.confignode.consensus.request.write.trigger.UpdateTriggerStateInTablePlan; import org.apache.iotdb.confignode.i18n.ManagerMessages; import org.apache.iotdb.confignode.manager.ConfigManager; -import org.apache.iotdb.confignode.manager.pipe.agent.PipeConfigNodeAgent; import org.apache.iotdb.confignode.manager.pipe.event.PipeConfigRegionSnapshotEvent; import org.apache.iotdb.confignode.manager.pipe.metric.receiver.PipeConfigNodeReceiverMetrics; import org.apache.iotdb.confignode.manager.pipe.receiver.visitor.PipeConfigPhysicalPlanExceptionVisitor; @@ -199,13 +197,6 @@ public TPipeTransferResp receive(final TPipeTransferReq req) { if (Objects.nonNull(authResp)) { return authResp; } - if (!PipeConfigNodeAgent.isPipeFeatureEnabled()) { - return new TPipeTransferResp( - RpcUtils.getStatus( - TSStatusCode.UNSUPPORTED_OPERATION, - PipeMessages - .MESSAGE_PIPE_AND_SUBSCRIPTION_ARE_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_3D8EAE6E)); - } TPipeTransferResp resp; final long startTime = System.nanoTime(); switch (type) { diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/subscription/SubscriptionCoordinator.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/subscription/SubscriptionCoordinator.java index 8ea81a7d172bb..72db41afe8e70 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/subscription/SubscriptionCoordinator.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/subscription/SubscriptionCoordinator.java @@ -31,7 +31,6 @@ import org.apache.iotdb.confignode.consensus.response.subscription.TopicTableResp; import org.apache.iotdb.confignode.i18n.ManagerMessages; import org.apache.iotdb.confignode.manager.ConfigManager; -import org.apache.iotdb.confignode.manager.pipe.agent.PipeConfigNodeAgent; import org.apache.iotdb.confignode.manager.pipe.coordinator.task.PipeTaskCoordinatorLock; import org.apache.iotdb.confignode.persistence.subscription.SubscriptionInfo; import org.apache.iotdb.confignode.rpc.thrift.TAlterTopicReq; @@ -188,10 +187,8 @@ public void unlockTopicAlteration(final String topicName, final boolean isTableM /////////////////////////////// Meta sync /////////////////////////////// public void startSubscriptionMetaSync() { - if (PipeConfigNodeAgent.isPipeFeatureEnabled()) { - subscriptionMetaSyncer.start(); - subscriptionOwnerLeaseSyncer.start(); - } + subscriptionMetaSyncer.start(); + subscriptionOwnerLeaseSyncer.start(); } public void stopSubscriptionMetaSync() { diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/subscription/runtime/SubscriptionLeaderChangeHandler.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/subscription/runtime/SubscriptionLeaderChangeHandler.java index fb8dd08b09d1d..6b888e424aa9c 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/subscription/runtime/SubscriptionLeaderChangeHandler.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/subscription/runtime/SubscriptionLeaderChangeHandler.java @@ -22,7 +22,6 @@ import org.apache.iotdb.confignode.manager.load.subscriber.ConsensusGroupStatisticsChangeEvent; import org.apache.iotdb.confignode.manager.load.subscriber.IClusterStatusSubscriber; import org.apache.iotdb.confignode.manager.load.subscriber.NodeStatisticsChangeEvent; -import org.apache.iotdb.confignode.manager.pipe.agent.PipeConfigNodeAgent; public class SubscriptionLeaderChangeHandler implements IClusterStatusSubscriber { @@ -34,15 +33,11 @@ public SubscriptionLeaderChangeHandler(final SubscriptionRuntimeCoordinator runt @Override public void onNodeStatisticsChanged(final NodeStatisticsChangeEvent event) { - if (PipeConfigNodeAgent.isPipeFeatureEnabled()) { - runtimeCoordinator.handleNodeStatisticsChange(event); - } + runtimeCoordinator.handleNodeStatisticsChange(event); } @Override public void onConsensusGroupStatisticsChanged(final ConsensusGroupStatisticsChangeEvent event) { - if (PipeConfigNodeAgent.isPipeFeatureEnabled()) { - runtimeCoordinator.handleLeaderChangeEvent(event); - } + runtimeCoordinator.handleLeaderChangeEvent(event); } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/PipeDataNodeAgent.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/PipeDataNodeAgent.java index f3e2ad3b66627..e350f0a6f4bbb 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/PipeDataNodeAgent.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/PipeDataNodeAgent.java @@ -19,8 +19,6 @@ package org.apache.iotdb.db.pipe.agent; -import org.apache.iotdb.consensus.ConsensusFactory; -import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.pipe.agent.plugin.PipeDataNodePluginAgent; import org.apache.iotdb.db.pipe.agent.receiver.PipeDataNodeReceiverAgent; import org.apache.iotdb.db.pipe.agent.runtime.PipeDataNodeRuntimeAgent; @@ -30,11 +28,6 @@ /** {@link PipeDataNodeAgent} is the entry point of the pipe module in {@link DataNode}. */ public class PipeDataNodeAgent { - public static boolean isPipeFeatureEnabled() { - return !ConsensusFactory.RATIS_CONSENSUS.equals( - IoTDBDescriptor.getInstance().getConfig().getDataRegionConsensusProtocolClass()); - } - private final PipeDataNodePluginAgent pipeDataNodePluginAgent; private final PipeDataNodeTaskAgent pipeDataNodeTaskAgent; private final PipeDataNodeRuntimeAgent pipeDataNodeRuntimeAgent; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/runtime/PipeAgentLauncher.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/runtime/PipeAgentLauncher.java index fb5a2538bc308..1b3e210b1e8f4 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/runtime/PipeAgentLauncher.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/runtime/PipeAgentLauncher.java @@ -62,7 +62,7 @@ private PipeAgentLauncher() { public static synchronized void launchPipePluginAgent( ResourcesInformationHolder resourcesInformationHolder) throws StartupException { - initializePipePluginRelatedInstances(); + initPipePluginRelatedInstances(); if (resourcesInformationHolder.getPipePluginMetaList() == null || resourcesInformationHolder.getPipePluginMetaList().isEmpty()) { @@ -106,7 +106,7 @@ public static synchronized void launchPipePluginAgent( } } - static void initializePipePluginRelatedInstances() throws StartupException { + private static void initPipePluginRelatedInstances() throws StartupException { try { PipePluginExecutableManager.setupAndGetInstance( IOTDB_CONFIG.getPipeTemporaryLibDir(), IOTDB_CONFIG.getPipeLibDir()); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/runtime/PipeDataNodeRuntimeAgent.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/runtime/PipeDataNodeRuntimeAgent.java index 77533b0009a24..8ca2ee1f52f64 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/runtime/PipeDataNodeRuntimeAgent.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/runtime/PipeDataNodeRuntimeAgent.java @@ -85,18 +85,13 @@ public class PipeDataNodeRuntimeAgent implements IService { public synchronized void preparePipeResources( final ResourcesInformationHolder resourcesInformationHolder) throws StartupException { - if (PipeDataNodeAgent.isPipeFeatureEnabled()) { - // Clean sender (connector) hardlink file dir and snapshot dir - PipeDataNodeHardlinkOrCopiedFileDirStartupCleaner.clean(this::registerPeriodicalJob); + // Clean sender (connector) hardlink file dir and snapshot dir + PipeDataNodeHardlinkOrCopiedFileDirStartupCleaner.clean(this::registerPeriodicalJob); - // Clean receiver file dir - PipeDataNodeAgent.receiver().cleanPipeReceiverDirs(); - - PipeAgentLauncher.launchPipePluginAgent(resourcesInformationHolder); - } else { - PipeAgentLauncher.initializePipePluginRelatedInstances(); - } + // Clean receiver file dir + PipeDataNodeAgent.receiver().cleanPipeReceiverDirs(); + PipeAgentLauncher.launchPipePluginAgent(resourcesInformationHolder); simpleProgressIndexAssigner.start(); IoTDBTreePattern.setDevicePathGetter(PipeDataNodeRuntimeAgent::getPath); @@ -138,11 +133,6 @@ private static PartialPath getPath(final IDeviceID device) throws IllegalPathExc @Override public synchronized void start() throws StartupException { - if (!PipeDataNodeAgent.isPipeFeatureEnabled()) { - isShutdown.set(true); - return; - } - PipeConfig.getInstance().printAllConfigs(); pipePeriodicalJobExecutor.start(); pipePeriodicalJobExecutor.runDirectly(PipeAgentLauncher::launchPipeTaskAgent); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgent.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgent.java index ec446a19da0c3..5b7775c231562 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgent.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgent.java @@ -146,11 +146,6 @@ protected boolean isShutdown() { return PipeDataNodeAgent.runtime().isShutdown(); } - @Override - protected boolean isPipeFeatureEnabled() { - return PipeDataNodeAgent.isPipeFeatureEnabled(); - } - @Override protected Map buildPipeTasks(final PipeMeta pipeMetaFromConfigNode) throws IllegalPathException { @@ -269,7 +264,6 @@ private void restoreCompletedEmptySnapshot(final PipeMeta pipeMeta) { final PipeStaticMeta staticMeta = pipeMeta.getStaticMeta(); if (isShutdown() || !SubscriptionConfig.getInstance().getSubscriptionEnabled() - || !PipeDataNodeAgent.isPipeFeatureEnabled() || pipeMeta.getRuntimeMeta().getStatus().get() != PipeStatus.RUNNING || !PipeStaticMeta.isSubscriptionPipe(staticMeta.getPipeName()) || !staticMeta diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/execution/PipeSubtaskExecutorManager.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/execution/PipeSubtaskExecutorManager.java index 9fca55c49a2f1..f83c23871f516 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/execution/PipeSubtaskExecutorManager.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/execution/PipeSubtaskExecutorManager.java @@ -21,7 +21,6 @@ import org.apache.iotdb.commons.consensus.iotv2.container.IoTV2GlobalComponentContainer; import org.apache.iotdb.commons.subscription.config.SubscriptionConfig; -import org.apache.iotdb.db.pipe.agent.PipeDataNodeAgent; import org.apache.iotdb.db.pipe.consensus.IoTConsensusV2SubtaskExecutor; import org.apache.iotdb.db.subscription.task.execution.SubscriptionSubtaskExecutor; @@ -66,8 +65,7 @@ private PipeSubtaskExecutorManager() { } public synchronized void ensureSubscriptionExecutors() { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled() - || !PipeDataNodeAgent.isPipeFeatureEnabled()) { + if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { return; } if (subscriptionExecutor == null || subscriptionExecutor.isShutdown()) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java index 3e4d83863f513..59f135d34c312 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java @@ -29,7 +29,6 @@ import org.apache.iotdb.commons.exception.IoTDBException; import org.apache.iotdb.commons.exception.IoTDBRuntimeException; import org.apache.iotdb.commons.exception.pipe.PipeRuntimeOutOfMemoryCriticalException; -import org.apache.iotdb.commons.i18n.PipeMessages; import org.apache.iotdb.commons.log.LoggerPeriodicalLogReducer; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.pipe.config.PipeConfig; @@ -239,13 +238,6 @@ public synchronized TPipeTransferResp receive(final TPipeTransferReq req) { if (Objects.nonNull(authResp)) { return authResp; } - if (!PipeDataNodeAgent.isPipeFeatureEnabled()) { - return new TPipeTransferResp( - RpcUtils.getStatus( - TSStatusCode.UNSUPPORTED_OPERATION, - PipeMessages - .MESSAGE_PIPE_AND_SUBSCRIPTION_ARE_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_3D8EAE6E)); - } switch (requestType) { case HANDSHAKE_DATANODE_V1: { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/historical/PipeHistoricalDataRegionTsFileAndDeletionSource.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/historical/PipeHistoricalDataRegionTsFileAndDeletionSource.java index 041d33c2faffb..c47fb606017dd 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/historical/PipeHistoricalDataRegionTsFileAndDeletionSource.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/dataregion/historical/PipeHistoricalDataRegionTsFileAndDeletionSource.java @@ -42,6 +42,7 @@ import org.apache.iotdb.commons.pipe.event.ProgressReportEvent; import org.apache.iotdb.commons.queryengine.utils.DateTimeUtils; import org.apache.iotdb.commons.utils.PathUtils; +import org.apache.iotdb.consensus.ConsensusFactory; import org.apache.iotdb.consensus.pipe.IoTConsensusV2; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.consensus.DataRegionConsensusImpl; @@ -919,6 +920,14 @@ private boolean mayTsFileContainUnprocessedData(final TsFileResource resource) { private boolean isProgressIndexNotCoveredByStartIndex( PersistentResource resource, ProgressIndex progressIndex) { + // Ratis writes do not carry Pipe progress indexes. Minimum therefore means unknown here, + // rather than an empty file already covered by the initial checkpoint. Replay conservatively + // after a leader change, or successful writes can disappear from the subscription backlog. + if (progressIndex instanceof MinimumProgressIndex + && ConsensusFactory.RATIS_CONSENSUS.equals( + IoTDBDescriptor.getInstance().getConfig().getDataRegionConsensusProtocolClass())) { + return true; + } final ProgressIndex innerStartIndex = getInnerProgressIndex(startIndex); if (innerStartIndex.isEqualOrAfter(progressIndex) || isProgressIndexCoveredByTimePartitionProgressIndex( diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java index 987e467ec8744..ad2a85db15203 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java @@ -36,7 +36,6 @@ import org.apache.iotdb.commons.conf.IoTDBConstant; import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.exception.IoTDBException; -import org.apache.iotdb.commons.i18n.PipeMessages; import org.apache.iotdb.commons.partition.DataPartition; import org.apache.iotdb.commons.partition.DataPartitionQueryParam; import org.apache.iotdb.commons.path.AlignedFullPath; @@ -3496,20 +3495,9 @@ private TSStatus checkLegacyPipeReceiverPermission() { if (!SESSION_MANAGER.checkLogin(clientSession)) { return getNotLoggedInStatus(); } - final TSStatus permissionStatus = - AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission( - clientSession.getUsername(), PrivilegeType.USE_PIPE), - PrivilegeType.USE_PIPE); - if (permissionStatus.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { - return permissionStatus; - } - return PipeDataNodeAgent.isPipeFeatureEnabled() - ? permissionStatus - : RpcUtils.getStatus( - TSStatusCode.UNSUPPORTED_OPERATION, - PipeMessages - .MESSAGE_PIPE_AND_SUBSCRIPTION_ARE_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_3D8EAE6E); + return AuthorityChecker.getTSStatus( + AuthorityChecker.checkSystemPermission(clientSession.getUsername(), PrivilegeType.USE_PIPE), + PrivilegeType.USE_PIPE); } @Override @@ -3524,15 +3512,6 @@ public TPipeSubscribeResp pipeSubscribe(final TPipeSubscribeReq req) { if (!SESSION_MANAGER.checkLogin(clientSession)) { return getNotLoggedInPipeSubscribeResp(); } - if (!PipeDataNodeAgent.isPipeFeatureEnabled()) { - return new TPipeSubscribeResp( - RpcUtils.getStatus( - TSStatusCode.UNSUPPORTED_OPERATION, - PipeMessages - .MESSAGE_PIPE_AND_SUBSCRIPTION_ARE_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_3D8EAE6E), - PipeSubscribeResponseVersion.VERSION_1.getVersion(), - PipeSubscribeResponseType.ACK.getType()); - } return SubscriptionAgent.receiver().handle(req, clientSession.getUsername()); } finally { @@ -3684,7 +3663,7 @@ public void handleClientExit() { } PipeDataNodeAgent.receiver().thrift().handleClientExit(); PipeDataNodeAgent.receiver().legacy().handleClientExit(); - if (COMMON_CONFIG.getSubscriptionEnabled() && PipeDataNodeAgent.isPipeFeatureEnabled()) { + if (COMMON_CONFIG.getSubscriptionEnabled()) { SubscriptionAgent.receiver().handleClientExit(); } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java index 8814303b70d6b..1f5f95b446020 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java @@ -1473,7 +1473,7 @@ public TPushPipeMetaRespExceptionMessage handleSinglePipeMeta(final ByteBuffer p @Override public TPushTopicMetaResp pushTopicMeta(TPushTopicMetaReq req) { - if (!isSubscriptionFeatureEnabled()) { + if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { return new TPushTopicMetaResp() .setStatus(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode())); } @@ -1501,7 +1501,7 @@ public TPushTopicMetaResp pushTopicMeta(TPushTopicMetaReq req) { @Override public TPushTopicMetaResp pushSingleTopicMeta(TPushSingleTopicMetaReq req) { - if (!isSubscriptionFeatureEnabled()) { + if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { return new TPushTopicMetaResp() .setStatus(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode())); } @@ -1539,7 +1539,7 @@ public TPushTopicMetaResp pushSingleTopicMeta(TPushSingleTopicMetaReq req) { @Override public TPushTopicMetaResp pushMultiTopicMeta(TPushMultiTopicMetaReq req) { - if (!isSubscriptionFeatureEnabled()) { + if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { return new TPushTopicMetaResp() .setStatus(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode())); } @@ -1591,7 +1591,7 @@ public TPushTopicMetaResp pushMultiTopicMeta(TPushMultiTopicMetaReq req) { @Override public TSStatus pushTopicOwnerLease(TPushTopicOwnerLeaseReq req) { - if (!isSubscriptionFeatureEnabled()) { + if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } try { @@ -1605,7 +1605,7 @@ public TSStatus pushTopicOwnerLease(TPushTopicOwnerLeaseReq req) { @Override public TPushConsumerGroupMetaResp pushConsumerGroupMeta(TPushConsumerGroupMetaReq req) { - if (!isSubscriptionFeatureEnabled()) { + if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { return new TPushConsumerGroupMetaResp() .setStatus(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode())); } @@ -1634,7 +1634,7 @@ public TPushConsumerGroupMetaResp pushConsumerGroupMeta(TPushConsumerGroupMetaRe @Override public TPushConsumerGroupMetaResp pushSingleConsumerGroupMeta( TPushSingleConsumerGroupMetaReq req) { - if (!isSubscriptionFeatureEnabled()) { + if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { return new TPushConsumerGroupMetaResp() .setStatus(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode())); } @@ -1669,7 +1669,7 @@ public TPushConsumerGroupMetaResp pushSingleConsumerGroupMeta( @Override public TPullCommitProgressResp pullCommitProgress(TPullCommitProgressReq req) { - if (!isSubscriptionFeatureEnabled()) { + if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { return new TPullCommitProgressResp(RpcUtils.getStatus(TSStatusCode.UNSUPPORTED_OPERATION)) .setCommitRegionProgress(Collections.emptyMap()) .setSubscriptionProgress(Collections.emptyMap()); @@ -1694,7 +1694,7 @@ public TPullCommitProgressResp pullCommitProgress(TPullCommitProgressReq req) { @Override public TSStatus syncSubscriptionProgress(TSyncSubscriptionProgressReq req) { - if (!isSubscriptionFeatureEnabled()) { + if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { return RpcUtils.getStatus(TSStatusCode.UNSUPPORTED_OPERATION); } @@ -1719,7 +1719,7 @@ public TSStatus syncSubscriptionProgress(TSyncSubscriptionProgressReq req) { @Override public TSStatus pushSubscriptionRuntime(TPushSubscriptionRuntimeReq req) { - if (!isSubscriptionFeatureEnabled()) { + if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { return RpcUtils.getStatus(TSStatusCode.UNSUPPORTED_OPERATION); } @@ -2530,7 +2530,7 @@ public TSStatus updateRegionCache(TRegionRouteReq req) { return RpcUtils.getStatus(TSStatusCode.PARTITION_CACHE_UPDATE_ERROR); } - if (isSubscriptionFeatureEnabled()) { + if (SubscriptionConfig.getInstance().getSubscriptionEnabled()) { // Notify consensus subscription queues of any preferred-writer changes try { ConsensusSubscriptionSetupHandler.onRegionRouteChanged( @@ -3395,19 +3395,8 @@ private TEndPoint getConsensusEndPoint( return nodeLocation.getSchemaRegionConsensusEndPoint(); } - private boolean isSubscriptionFeatureEnabled() { - return SubscriptionConfig.getInstance().getSubscriptionEnabled() - && PipeDataNodeAgent.isPipeFeatureEnabled(); - } - @Override public TSStatus createPipePlugin(TCreatePipePluginInstanceReq req) { - if (!PipeDataNodeAgent.isPipeFeatureEnabled()) { - return RpcUtils.getStatus( - TSStatusCode.UNSUPPORTED_OPERATION, - PipeMessages - .MESSAGE_PIPE_AND_SUBSCRIPTION_ARE_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_3D8EAE6E); - } try { PipePluginMeta pipePluginMeta = PipePluginMeta.deserialize(req.pipePluginMeta); PipeDataNodeAgent.plugin().register(pipePluginMeta, req.jarFile); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java index 55c291ac46294..251d3f96afe63 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java @@ -435,8 +435,6 @@ private ClusterConfigTaskExecutorHolder() {} } private static final SettableFuture SUBSCRIPTION_NOT_ENABLED_ERROR_FUTURE; - private static final SettableFuture - PIPE_AND_SUBSCRIPTION_DISABLED_BY_RATIS_ERROR_FUTURE; static { SUBSCRIPTION_NOT_ENABLED_ERROR_FUTURE = SettableFuture.create(); @@ -444,13 +442,6 @@ private ClusterConfigTaskExecutorHolder() {} new IoTDBException( DataNodeQueryMessages.QUERY_EXCEPTION_SUBSCRIPTION_IS_NOT_ENABLED_7F43DCBB, TSStatusCode.SUBSCRIPTION_NOT_ENABLED_ERROR.getStatusCode())); - - PIPE_AND_SUBSCRIPTION_DISABLED_BY_RATIS_ERROR_FUTURE = SettableFuture.create(); - PIPE_AND_SUBSCRIPTION_DISABLED_BY_RATIS_ERROR_FUTURE.setException( - new IoTDBException( - PipeMessages - .MESSAGE_PIPE_AND_SUBSCRIPTION_ARE_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_3D8EAE6E, - TSStatusCode.UNSUPPORTED_OPERATION.getStatusCode())); } public static ClusterConfigTaskExecutor getInstance() { @@ -1029,10 +1020,6 @@ public SettableFuture showTriggers() { @Override public SettableFuture createPipePlugin( final CreatePipePluginStatement createPipePluginStatement) { - if (!PipeDataNodeAgent.isPipeFeatureEnabled()) { - return PIPE_AND_SUBSCRIPTION_DISABLED_BY_RATIS_ERROR_FUTURE; - } - final SettableFuture future = SettableFuture.create(); final String pluginName = createPipePluginStatement.getPluginName(); final String className = createPipePluginStatement.getClassName(); @@ -2258,10 +2245,6 @@ public SettableFuture unsetSchemaTemplate( @Override public SettableFuture createPipe( final CreatePipeStatement createPipeStatement) { - if (!PipeDataNodeAgent.isPipeFeatureEnabled()) { - return PIPE_AND_SUBSCRIPTION_DISABLED_BY_RATIS_ERROR_FUTURE; - } - final SettableFuture future = SettableFuture.create(); // Verify that Pipe is disabled if TSFile encryption is enabled @@ -2597,10 +2580,6 @@ private void dropPipeIfCreated( @Override public SettableFuture alterPipe(final AlterPipeStatement alterPipeStatement) { - if (!PipeDataNodeAgent.isPipeFeatureEnabled()) { - return PIPE_AND_SUBSCRIPTION_DISABLED_BY_RATIS_ERROR_FUTURE; - } - final SettableFuture future = SettableFuture.create(); // Validate pipe name @@ -2912,10 +2891,6 @@ private static boolean checkSinkType(final Map sinkAttributes) { @Override public SettableFuture startPipe(final StartPipeStatement startPipeStatement) { - if (!PipeDataNodeAgent.isPipeFeatureEnabled()) { - return PIPE_AND_SUBSCRIPTION_DISABLED_BY_RATIS_ERROR_FUTURE; - } - final SettableFuture future = SettableFuture.create(); // Validate pipe name @@ -3188,9 +3163,6 @@ public SettableFuture createTopic( if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { return SUBSCRIPTION_NOT_ENABLED_ERROR_FUTURE; } - if (!PipeDataNodeAgent.isPipeFeatureEnabled()) { - return PIPE_AND_SUBSCRIPTION_DISABLED_BY_RATIS_ERROR_FUTURE; - } final SettableFuture future = SettableFuture.create(); @@ -3265,9 +3237,6 @@ public SettableFuture alterTopic( if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { return SUBSCRIPTION_NOT_ENABLED_ERROR_FUTURE; } - if (!PipeDataNodeAgent.isPipeFeatureEnabled()) { - return PIPE_AND_SUBSCRIPTION_DISABLED_BY_RATIS_ERROR_FUTURE; - } final SettableFuture future = SettableFuture.create(); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/DataNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/DataNode.java index 3132c1e4689cd..6bca0cace69cf 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/DataNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/DataNode.java @@ -301,8 +301,7 @@ protected void start() { // TierManager need DataNodeId to do some operations so the reset method need to be invoked // after DataNode adding TierManager.getInstance().resetFolders(); - if (SubscriptionConfig.getInstance().getSubscriptionEnabled() - && PipeDataNodeAgent.isPipeFeatureEnabled()) { + if (SubscriptionConfig.getInstance().getSubscriptionEnabled()) { ConsensusSubscriptionSetupHandler.ensureNewRegionListenerRegistered(); } // Active DataNode @@ -943,8 +942,7 @@ private void setUp() throws StartupException, IOException { registerInternalRPCService(); // Register subscription agent before pipe agent - if (SubscriptionConfig.getInstance().getSubscriptionEnabled() - && PipeDataNodeAgent.isPipeFeatureEnabled()) { + if (SubscriptionConfig.getInstance().getSubscriptionEnabled()) { registerManager.register(SubscriptionAgent.runtime()); } registerManager.register(PipeDataNodeAgent.runtime()); @@ -1396,8 +1394,7 @@ public void stop() { private void initProtocols() throws StartupException { prepareExternalServiceResources(); - if (PipeConfig.getInstance().getPipeAirGapReceiverEnabled() - && PipeDataNodeAgent.isPipeFeatureEnabled()) { + if (PipeConfig.getInstance().getPipeAirGapReceiverEnabled()) { registerManager.register(PipeDataNodeAgent.receiver().airGap()); } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgent.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgent.java index ae77238310832..a213eac6dfc1f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgent.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgent.java @@ -22,11 +22,9 @@ import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.concurrent.IoTDBThreadPoolFactory; import org.apache.iotdb.commons.concurrent.threadpool.ScheduledExecutorUtil; -import org.apache.iotdb.commons.i18n.PipeMessages; import org.apache.iotdb.commons.subscription.config.SubscriptionConfig; import org.apache.iotdb.db.i18n.DataNodePipeMessages; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; -import org.apache.iotdb.db.pipe.agent.PipeDataNodeAgent; import org.apache.iotdb.db.subscription.receiver.SubscriptionReceiver; import org.apache.iotdb.db.subscription.receiver.SubscriptionReceiverV1; import org.apache.iotdb.rpc.RpcUtils; @@ -65,15 +63,6 @@ public class SubscriptionReceiverAgent { PipeSubscribeResponseVersion.VERSION_1.getVersion(), PipeSubscribeResponseType.ACK.getType()); - private static final TPipeSubscribeResp PIPE_SUBSCRIPTION_DISABLED_BY_RATIS_RESP = - new TPipeSubscribeResp( - RpcUtils.getStatus( - TSStatusCode.UNSUPPORTED_OPERATION, - PipeMessages - .MESSAGE_PIPE_AND_SUBSCRIPTION_ARE_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_3D8EAE6E), - PipeSubscribeResponseVersion.VERSION_1.getVersion(), - PipeSubscribeResponseType.ACK.getType()); - private static final TPipeSubscribeResp SUBSCRIPTION_CONSUMER_FENCED_RESP = new TPipeSubscribeResp( RpcUtils.getStatus( @@ -101,11 +90,8 @@ public class SubscriptionReceiverAgent { SubscriptionReceiverAgent() { this( SubscriptionReceiverV1::new, - SubscriptionConfig.getInstance().getSubscriptionEnabled() - && PipeDataNodeAgent.isPipeFeatureEnabled(), - () -> - SubscriptionConfig.getInstance().getSubscriptionEnabled() - && PipeDataNodeAgent.isPipeFeatureEnabled()); + SubscriptionConfig.getInstance().getSubscriptionEnabled(), + () -> SubscriptionConfig.getInstance().getSubscriptionEnabled()); } SubscriptionReceiverAgent( @@ -143,9 +129,6 @@ public TPipeSubscribeResp handle(final TPipeSubscribeReq req, final String usern PipeSubscribeResponseVersion.VERSION_1.getVersion(), PipeSubscribeResponseType.ACK.getType()); } - if (!PipeDataNodeAgent.isPipeFeatureEnabled()) { - return PIPE_SUBSCRIPTION_DISABLED_BY_RATIS_RESP; - } if (!subscriptionEnabledSupplier.getAsBoolean()) { return SUBSCRIPTION_NOT_ENABLED_ERROR_RESP; } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionRuntimeAgent.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionRuntimeAgent.java index ad37d64a7d5e3..2962f525e7020 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionRuntimeAgent.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionRuntimeAgent.java @@ -23,7 +23,6 @@ import org.apache.iotdb.commons.service.IService; import org.apache.iotdb.commons.service.ServiceType; import org.apache.iotdb.commons.subscription.config.SubscriptionConfig; -import org.apache.iotdb.db.pipe.agent.PipeDataNodeAgent; import org.apache.iotdb.db.subscription.broker.consensus.ConsensusSubscriptionSetupHandler; import org.apache.iotdb.db.subscription.task.execution.ConsensusSubscriptionPrefetchExecutorManager; @@ -65,11 +64,6 @@ SubscriptionTopicAgent topic() { @Override public void start() throws StartupException { - if (!PipeDataNodeAgent.isPipeFeatureEnabled()) { - isShutdown.set(true); - return; - } - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { return; } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusSubscriptionSetupHandler.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusSubscriptionSetupHandler.java index 221322ff62cfc..1ae30b123e100 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusSubscriptionSetupHandler.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusSubscriptionSetupHandler.java @@ -38,7 +38,6 @@ import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.consensus.DataRegionConsensusImpl; import org.apache.iotdb.db.i18n.DataNodePipeMessages; -import org.apache.iotdb.db.pipe.agent.PipeDataNodeAgent; import org.apache.iotdb.db.storageengine.StorageEngine; import org.apache.iotdb.db.storageengine.dataregion.DataRegion; import org.apache.iotdb.db.subscription.agent.SubscriptionAgent; @@ -1139,8 +1138,7 @@ public static void handleNewSubscriptions( public static void applyRuntimeState( final TConsensusGroupId groupId, final ConsensusRegionRuntimeState runtimeState) { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled() - || !PipeDataNodeAgent.isPipeFeatureEnabled()) { + if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { return; } @@ -1175,8 +1173,7 @@ public static void applyRuntimeState( public static void onRegionRouteChanged( final Map newMap, final long routingTimestamp) { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled() - || !PipeDataNodeAgent.isPipeFeatureEnabled()) { + if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { return; } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/task/execution/ConsensusSubscriptionPrefetchExecutorManager.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/task/execution/ConsensusSubscriptionPrefetchExecutorManager.java index 43d459566f4ce..9362a38a58b7e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/task/execution/ConsensusSubscriptionPrefetchExecutorManager.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/task/execution/ConsensusSubscriptionPrefetchExecutorManager.java @@ -20,7 +20,6 @@ package org.apache.iotdb.db.subscription.task.execution; import org.apache.iotdb.commons.subscription.config.SubscriptionConfig; -import org.apache.iotdb.db.pipe.agent.PipeDataNodeAgent; public class ConsensusSubscriptionPrefetchExecutorManager { @@ -32,8 +31,7 @@ private ConsensusSubscriptionPrefetchExecutorManager() { } public synchronized void start() { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled() - || !PipeDataNodeAgent.isPipeFeatureEnabled()) { + if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { started = false; return; } @@ -44,9 +42,7 @@ public synchronized void start() { } public synchronized ConsensusSubscriptionPrefetchExecutor getExecutor() { - if (!started - || !SubscriptionConfig.getInstance().getSubscriptionEnabled() - || !PipeDataNodeAgent.isPipeFeatureEnabled()) { + if (!started || !SubscriptionConfig.getInstance().getSubscriptionEnabled()) { return null; } if (executor == null || executor.isShutdown()) { diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiverTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiverTest.java index c03c73eaca6fb..b71542a0cc0d1 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiverTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiverTest.java @@ -24,9 +24,7 @@ import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.pipe.receiver.runtime.PipeReceiverRuntimeRegistry; import org.apache.iotdb.commons.pipe.receiver.runtime.PipeReceiverRuntimeSnapshot; -import org.apache.iotdb.consensus.ConsensusFactory; import org.apache.iotdb.db.conf.IoTDBDescriptor; -import org.apache.iotdb.db.pipe.sink.payload.evolvable.request.PipeTransferDataNodeHandshakeV2Req; import org.apache.iotdb.db.pipe.sink.payload.evolvable.request.PipeTransferTsFileSealWithModReq; import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertMultiTabletsStatement; import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowStatement; @@ -37,7 +35,6 @@ import org.apache.iotdb.db.storageengine.load.config.LoadTsFileConfigurator; import org.apache.iotdb.db.storageengine.load.converter.PipeTsFileConversionTaskManager; import org.apache.iotdb.rpc.TSStatusCode; -import org.apache.iotdb.service.rpc.thrift.TPipeTransferReq; import org.junit.After; import org.junit.Assert; @@ -67,27 +64,6 @@ public void tearDown() { registry.clear(); } - @Test - public void testRatisRejectsPipeTransfer() throws Exception { - final String originalConsensus = - IoTDBDescriptor.getInstance().getConfig().getDataRegionConsensusProtocolClass(); - try { - IoTDBDescriptor.getInstance() - .getConfig() - .setDataRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS); - - final TPipeTransferReq request = - PipeTransferDataNodeHandshakeV2Req.toTPipeTransferReq(Collections.emptyMap()); - Assert.assertEquals( - TSStatusCode.UNSUPPORTED_OPERATION.getStatusCode(), - new IoTDBDataNodeReceiver().receive(request).getStatus().getCode()); - } finally { - IoTDBDescriptor.getInstance() - .getConfig() - .setDataRegionConsensusProtocolClass(originalConsensus); - } - } - @Test public void testTableModelTabletRedirectDoesNotCachePerRowLeader() { final InsertTabletStatement statement = new InsertTabletStatement(); diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/source/dataregion/historical/PipeHistoricalDataRegionTsFileAndDeletionSourceTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/source/dataregion/historical/PipeHistoricalDataRegionTsFileAndDeletionSourceTest.java index 991dab795f94a..090124c7fc82e 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/source/dataregion/historical/PipeHistoricalDataRegionTsFileAndDeletionSourceTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/source/dataregion/historical/PipeHistoricalDataRegionTsFileAndDeletionSourceTest.java @@ -35,6 +35,8 @@ import org.apache.iotdb.commons.pipe.datastructure.resource.PersistentResource; import org.apache.iotdb.commons.pipe.event.ProgressReportEvent; import org.apache.iotdb.commons.utils.FileUtils; +import org.apache.iotdb.consensus.ConsensusFactory; +import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.pipe.consensus.ReplicateProgressDataNodeManager; import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResource; import org.apache.iotdb.db.storageengine.dataregion.tsfile.TsFileResourceStatus; @@ -688,6 +690,50 @@ public void testMayTsFileContainUnprocessedDataUsesEqualOrAfterCoverage() throws } } + @Test + public void testRatisFilesWithoutPipeProgressAreNotCoveredByCheckpoint() throws Exception { + final String originalConsensus = + IoTDBDescriptor.getInstance().getConfig().getDataRegionConsensusProtocolClass(); + final File tempDir = Files.createTempDirectory("pipeHistoricalRatisCoverage").toFile(); + try { + IoTDBDescriptor.getInstance() + .getConfig() + .setDataRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS); + assertMayTsFileContainUnprocessedData( + tempDir, + "unknown-initial.tsfile", + MinimumProgressIndex.INSTANCE, + MinimumProgressIndex.INSTANCE, + true); + assertMayTsFileContainUnprocessedData( + tempDir, + "unknown-recovery.tsfile", + new SimpleProgressIndex(0, 100), + MinimumProgressIndex.INSTANCE, + true); + assertMayTsFileContainUnprocessedData( + tempDir, + "indexed-covered.tsfile", + new SimpleProgressIndex(0, 100), + new SimpleProgressIndex(0, 50), + false); + IoTDBDescriptor.getInstance() + .getConfig() + .setDataRegionConsensusProtocolClass(ConsensusFactory.IOT_CONSENSUS); + assertMayTsFileContainUnprocessedData( + tempDir, + "other-consensus.tsfile", + MinimumProgressIndex.INSTANCE, + MinimumProgressIndex.INSTANCE, + false); + } finally { + IoTDBDescriptor.getInstance() + .getConfig() + .setDataRegionConsensusProtocolClass(originalConsensus); + FileUtils.deleteFileOrDirectory(tempDir); + } + } + @Test public void testMayTsFileContainUnprocessedDataUsesTimePartitionProgressCoverage() throws Exception { diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgentTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgentTest.java index fc68f97c6e856..21ef6ecb101de 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgentTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgentTest.java @@ -20,8 +20,6 @@ package org.apache.iotdb.db.subscription.agent; import org.apache.iotdb.common.rpc.thrift.TSStatus; -import org.apache.iotdb.consensus.ConsensusFactory; -import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.subscription.receiver.SubscriptionReceiver; import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; @@ -56,34 +54,6 @@ public class SubscriptionReceiverAgentTest { - @Test - public void testRatisRejectsSubscriptionBeforeCreatingReceiver() throws IOException { - final String originalConsensus = - IoTDBDescriptor.getInstance().getConfig().getDataRegionConsensusProtocolClass(); - try { - IoTDBDescriptor.getInstance() - .getConfig() - .setDataRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS); - final SubscriptionReceiverAgent agent = - new SubscriptionReceiverAgent( - () -> { - throw new AssertionError("Receiver must not be created when Ratis is enabled"); - }, - false, - () -> { - throw new AssertionError("Subscription supplier must not be called"); - }); - - Assert.assertEquals( - TSStatusCode.UNSUPPORTED_OPERATION.getStatusCode(), - agent.handle(createHandshakeRequest("group", "consumer"), "root").getStatus().getCode()); - } finally { - IoTDBDescriptor.getInstance() - .getConfig() - .setDataRegionConsensusProtocolClass(originalConsensus); - } - } - @Test public void testDisabledSubscriptionRejectsRequest() throws IOException { final SubscriptionReceiverAgent agent = diff --git a/iotdb-core/node-commons/src/main/i18n/en/org/apache/iotdb/commons/i18n/PipeMessages.java b/iotdb-core/node-commons/src/main/i18n/en/org/apache/iotdb/commons/i18n/PipeMessages.java index b06c658a9a2e9..0aafc35842770 100644 --- a/iotdb-core/node-commons/src/main/i18n/en/org/apache/iotdb/commons/i18n/PipeMessages.java +++ b/iotdb-core/node-commons/src/main/i18n/en/org/apache/iotdb/commons/i18n/PipeMessages.java @@ -21,9 +21,6 @@ public final class PipeMessages { - public static final String MESSAGE_PIPE_AND_SUBSCRIPTION_ARE_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_3D8EAE6E = - "Pipe and Subscription are disabled when data region consensus protocol is Ratis."; - // ===================== Common / Shared ===================== public static final String UTILITY_CLASS = "Utility class"; diff --git a/iotdb-core/node-commons/src/main/i18n/zh/org/apache/iotdb/commons/i18n/PipeMessages.java b/iotdb-core/node-commons/src/main/i18n/zh/org/apache/iotdb/commons/i18n/PipeMessages.java index 720f8206cea84..9746b71500d2d 100644 --- a/iotdb-core/node-commons/src/main/i18n/zh/org/apache/iotdb/commons/i18n/PipeMessages.java +++ b/iotdb-core/node-commons/src/main/i18n/zh/org/apache/iotdb/commons/i18n/PipeMessages.java @@ -21,9 +21,6 @@ public final class PipeMessages { - public static final String MESSAGE_PIPE_AND_SUBSCRIPTION_ARE_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_3D8EAE6E = - "当数据区域共识协议为 Ratis 时,Pipe 和 Subscription 功能不可用。"; - // ===================== Common / Shared ===================== public static final String UTILITY_CLASS = "工具类"; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/PipeTaskAgent.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/PipeTaskAgent.java index 767d1cb65169b..34bad05803fc5 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/PipeTaskAgent.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/PipeTaskAgent.java @@ -209,10 +209,6 @@ protected TPushPipeMetaRespExceptionMessage handleSinglePipeMetaChangesInternal( protected abstract boolean isShutdown(); - protected boolean isPipeFeatureEnabled() { - return true; - } - private void executeSinglePipeMetaChanges(final PipeMeta metaFromCoordinator) throws IllegalPathException { final String pipeName = metaFromCoordinator.getStaticMeta().getPipeName(); @@ -230,14 +226,6 @@ private void executeSinglePipeMetaChanges(final PipeMeta metaFromCoordinator) return; } - if (!isPipeFeatureEnabled()) { - // Ignore active metadata while disabled, but still process drops to clean up stale tasks. - if (metaFromCoordinator.getRuntimeMeta().getStatus().get() == PipeStatus.DROPPED) { - dropPipe(pipeName, creationTime); - } - return; - } - if (metaFromCoordinator.getRuntimeMeta().getStatus().get() == PipeStatus.DROPPED) { dropPipe(pipeName, creationTime); return; From d1b7ed27b50d533bff54aa37b5453bfb2d7abbd5 Mon Sep 17 00:00:00 2001 From: Caideyipi <87789683+Caideyipi@users.noreply.github.com> Date: Fri, 30 Oct 2026 12:20:07 +0800 Subject: [PATCH 5/9] Refine Ratis Pipe and Subscription restrictions --- .../confignode/manager/ConfigManager.java | 39 +++++++++++++--- .../pipe/agent/PipeConfigNodeAgent.java | 36 +++++++++++++++ .../coordinator/task/PipeTaskCoordinator.java | 45 +++++++++++++++++++ .../subscription/SubscriptionCoordinator.java | 7 ++- .../SubscriptionLeaderChangeHandler.java | 9 +++- .../db/pipe/agent/PipeDataNodeAgent.java | 36 +++++++++++++++ .../agent/task/PipeDataNodeTaskAgent.java | 6 +++ .../execution/PipeSubtaskExecutorManager.java | 4 +- .../thrift/IoTDBDataNodeReceiver.java | 28 ++++++++++++ .../thrift/impl/ClientRPCServiceImpl.java | 13 +++++- .../impl/DataNodeInternalRPCServiceImpl.java | 25 ++++++----- .../executor/ClusterConfigTaskExecutor.java | 30 +++++++++++++ .../org/apache/iotdb/db/service/DataNode.java | 6 ++- .../agent/SubscriptionReceiverAgent.java | 21 ++++++++- .../agent/SubscriptionRuntimeAgent.java | 4 +- .../ConsensusSubscriptionSetupHandler.java | 7 ++- ...usSubscriptionPrefetchExecutorManager.java | 8 +++- .../agent/task/PipeDataNodeTaskAgentTest.java | 29 ++++++++++++ .../agent/SubscriptionReceiverAgentTest.java | 30 +++++++++++++ .../iotdb/commons/i18n/PipeMessages.java | 6 +++ .../iotdb/commons/i18n/PipeMessages.java | 6 +++ .../pipe/agent/task/PipeTaskAgent.java | 12 +++++ .../options/PipeInclusionOptionsTest.java | 27 +++++++++++ 23 files changed, 404 insertions(+), 30 deletions(-) diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java index 3e407143e46ee..a19b5e2a9eada 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java @@ -51,11 +51,13 @@ import org.apache.iotdb.commons.enums.RepairDataPartitionTableProgressState; import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.exception.MetadataException; +import org.apache.iotdb.commons.i18n.PipeMessages; import org.apache.iotdb.commons.path.MeasurementPath; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.path.PathDeserializeUtil; import org.apache.iotdb.commons.path.PathPatternTree; import org.apache.iotdb.commons.path.PathPatternUtil; +import org.apache.iotdb.commons.pipe.agent.task.meta.PipeStaticMeta; import org.apache.iotdb.commons.pipe.sink.payload.airgap.AirGapPseudoTPipeTransferRequest; import org.apache.iotdb.commons.schema.SchemaConstant; import org.apache.iotdb.commons.schema.table.AlterOrDropTableOperationType; @@ -1769,6 +1771,32 @@ public TGetJarInListResp getPipePluginJar(TGetJarInListReq req) { : new TGetJarInListResp(status, Collections.emptyList()); } + private TSStatus confirmSubscriptionFeatureAvailability() { + final TSStatus status = confirmLeader(); + if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode() + || PipeConfigNodeAgent.isSubscriptionFeatureEnabled()) { + return status; + } + return RpcUtils.getStatus( + TSStatusCode.UNSUPPORTED_OPERATION, + PipeMessages + .MESSAGE_SUBSCRIPTION_IS_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_194CE431); + } + + private TSStatus confirmPipeDataInsertionAvailability( + final String pipeName, final Map sourceAttributes) { + final TSStatus status = confirmLeader(); + if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode() + || pipeName.startsWith(PipeStaticMeta.CONSENSUS_PIPE_PREFIX) + || PipeConfigNodeAgent.isPipeDataInsertionEnabled(sourceAttributes)) { + return status; + } + return RpcUtils.getStatus( + TSStatusCode.UNSUPPORTED_OPERATION, + PipeMessages + .MESSAGE_PIPE_DATA_INSERTION_IS_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_7D669ECC); + } + @Override public TSStatus merge() { TSStatus status = confirmLeader(); @@ -2572,7 +2600,8 @@ public TSStatus alterTimeSeriesDataType(final TAlterTimeSeriesReq req) { @Override public TSStatus createPipe(TCreatePipeReq req) { - TSStatus status = confirmLeader(); + TSStatus status = + confirmPipeDataInsertionAvailability(req.getPipeName(), req.getExtractorAttributes()); return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() ? pipeManager.getPipeTaskCoordinator().createPipe(req) : status; @@ -2628,7 +2657,7 @@ public TGetAllPipeInfoResp getAllPipeInfo() { @Override public TSStatus createTopic(TCreateTopicReq req) { - TSStatus status = confirmLeader(); + TSStatus status = confirmSubscriptionFeatureAvailability(); return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() ? subscriptionManager.getSubscriptionCoordinator().createTopic(req) : status; @@ -2636,7 +2665,7 @@ public TSStatus createTopic(TCreateTopicReq req) { @Override public TSStatus alterTopic(TAlterTopicReq req) { - TSStatus status = confirmLeader(); + TSStatus status = confirmSubscriptionFeatureAvailability(); return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() ? subscriptionManager.getSubscriptionCoordinator().alterTopic(req) : status; @@ -2668,7 +2697,7 @@ public TGetAllTopicInfoResp getAllTopicInfo() { @Override public TSStatus createConsumer(TCreateConsumerReq req) { - TSStatus status = confirmLeader(); + TSStatus status = confirmSubscriptionFeatureAvailability(); return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() ? subscriptionManager.getSubscriptionCoordinator().createConsumer(req) : status; @@ -2684,7 +2713,7 @@ public TSStatus closeConsumer(TCloseConsumerReq req) { @Override public TSStatus createSubscription(TSubscribeReq req) { - TSStatus status = confirmLeader(); + TSStatus status = confirmSubscriptionFeatureAvailability(); return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() ? subscriptionManager.getSubscriptionCoordinator().createSubscription(req) : status; diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/PipeConfigNodeAgent.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/PipeConfigNodeAgent.java index 834b4e0ce3573..b8f027b45c84f 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/PipeConfigNodeAgent.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/PipeConfigNodeAgent.java @@ -19,15 +19,51 @@ package org.apache.iotdb.confignode.manager.pipe.agent; +import org.apache.iotdb.commons.exception.IllegalPathException; +import org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant; +import org.apache.iotdb.commons.pipe.datastructure.options.PipeInclusionOptions; +import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor; import org.apache.iotdb.confignode.manager.pipe.agent.plugin.PipeConfigNodePluginAgent; import org.apache.iotdb.confignode.manager.pipe.agent.receiver.IoTDBConfigNodeReceiverAgent; import org.apache.iotdb.confignode.manager.pipe.agent.runtime.PipeConfigNodeRuntimeAgent; import org.apache.iotdb.confignode.manager.pipe.agent.task.PipeConfigNodeTaskAgent; import org.apache.iotdb.confignode.service.ConfigNode; +import org.apache.iotdb.consensus.ConsensusFactory; +import org.apache.iotdb.pipe.api.customizer.parameter.PipeParameters; + +import java.util.Collections; +import java.util.Map; /** {@link PipeConfigNodeAgent} is the entry point of the pipe module in {@link ConfigNode}. */ public class PipeConfigNodeAgent { + public static boolean isRatisConsensus() { + return ConsensusFactory.RATIS_CONSENSUS.equals( + ConfigNodeDescriptor.getInstance().getConf().getDataRegionConsensusProtocolClass()); + } + + public static boolean isSubscriptionFeatureEnabled() { + return !isRatisConsensus(); + } + + public static boolean isPipeDataInsertionEnabled(final Map sourceAttributes) { + return isPipeDataInsertionEnabled( + new PipeParameters(sourceAttributes == null ? Collections.emptyMap() : sourceAttributes)); + } + + public static boolean isPipeDataInsertionEnabled(final PipeParameters sourceParameters) { + if (!isRatisConsensus()) { + return true; + } + try { + return !PipeInclusionOptions.areOptionsEnabled( + sourceParameters, PipeSourceConstant.EXTRACTOR_INCLUSION_DEFAULT_VALUE); + } catch (final IllegalPathException e) { + // Let the normal pipe parameter validation report malformed inclusion options. + return true; + } + } + private final PipeConfigNodeTaskAgent pipeConfigNodeTaskAgent; private final PipeConfigNodePluginAgent pipeConfigNodePluginAgent; private final PipeConfigNodeRuntimeAgent pipeConfigNodeRuntimeAgent; diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/task/PipeTaskCoordinator.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/task/PipeTaskCoordinator.java index 8ac1567be88ea..a2d4ff2234a04 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/task/PipeTaskCoordinator.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/task/PipeTaskCoordinator.java @@ -21,6 +21,7 @@ import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.i18n.PipeMessages; +import org.apache.iotdb.commons.pipe.agent.task.meta.PipeMeta; import org.apache.iotdb.commons.pipe.agent.task.meta.PipeStaticMeta; import org.apache.iotdb.commons.pipe.agent.task.meta.PipeStatus; import org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant; @@ -31,6 +32,7 @@ import org.apache.iotdb.confignode.i18n.ConfigNodeMessages; import org.apache.iotdb.confignode.i18n.ManagerMessages; import org.apache.iotdb.confignode.manager.ConfigManager; +import org.apache.iotdb.confignode.manager.pipe.agent.PipeConfigNodeAgent; import org.apache.iotdb.confignode.persistence.pipe.PipeTaskInfo; import org.apache.iotdb.confignode.rpc.thrift.TAlterPipeReq; import org.apache.iotdb.confignode.rpc.thrift.TCreatePipeReq; @@ -106,6 +108,11 @@ public boolean isLocked() { /** Caller should ensure that the method is called in the lock {@link #lock()}. */ public TSStatus createPipe(TCreatePipeReq req) { + if (!req.getPipeName().startsWith(PipeStaticMeta.CONSENSUS_PIPE_PREFIX) + && !PipeConfigNodeAgent.isPipeDataInsertionEnabled(req.getExtractorAttributes())) { + return pipeDataInsertionDisabledStatus(); + } + final TSStatus status; if (req.getPipeName().startsWith(PipeStaticMeta.CONSENSUS_PIPE_PREFIX)) { status = configManager.getProcedureManager().createConsensusPipe(req); @@ -243,6 +250,12 @@ public TSStatus alterPipe(TAlterPipeReq req) { String.format( "Failed to alter pipe %s. Failures: %s does not exist.", pipeName, pipeName)); } + final PipeMeta currentPipeMeta = pipeTaskInfo.getPipeMetaByPipeName(pipeName, isTableModel); + if (!pipeName.startsWith(PipeStaticMeta.CONSENSUS_PIPE_PREFIX) + && !PipeConfigNodeAgent.isPipeDataInsertionEnabled( + getUpdatedSourceParameters(req, currentPipeMeta))) { + return pipeDataInsertionDisabledStatus(); + } req.setIsTableModel(isTableModel); final TSStatus status = configManager.getProcedureManager().alterPipe(req); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { @@ -288,9 +301,41 @@ public TSStatus startPipe(TStartPipeReq req) { String.format( "Failed to start pipe %s. Failures: %s does not exist.", pipeName, pipeName)); } + final PipeMeta pipeMeta = pipeTaskInfo.getPipeMetaByPipeName(pipeName, isTableModel); + if (!pipeName.startsWith(PipeStaticMeta.CONSENSUS_PIPE_PREFIX) + && !PipeConfigNodeAgent.isPipeDataInsertionEnabled( + pipeMeta.getStaticMeta().getSourceParameters())) { + return pipeDataInsertionDisabledStatus(); + } return startPipe(pipeName, isTableModel); } + private PipeParameters getUpdatedSourceParameters( + final TAlterPipeReq req, final PipeMeta currentPipeMeta) { + if (req.isReplaceAllExtractorAttributes) { + return new PipeParameters( + req.getExtractorAttributes() == null + ? Collections.emptyMap() + : req.getExtractorAttributes()); + } + + final PipeParameters sourceParameters = + new PipeParameters( + new HashMap<>(currentPipeMeta.getStaticMeta().getSourceParameters().getAttribute())); + if (req.getExtractorAttributes() != null && !req.getExtractorAttributes().isEmpty()) { + sourceParameters.addOrReplaceEquivalentAttributes( + new PipeParameters(req.getExtractorAttributes())); + } + return sourceParameters; + } + + private TSStatus pipeDataInsertionDisabledStatus() { + return RpcUtils.getStatus( + TSStatusCode.UNSUPPORTED_OPERATION, + PipeMessages + .MESSAGE_PIPE_DATA_INSERTION_IS_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_7D669ECC); + } + /** Caller should ensure that the method is called in the lock {@link #lock()}. */ private TSStatus stopPipe(String pipeName) { return stopPipe(pipeName, false); diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/subscription/SubscriptionCoordinator.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/subscription/SubscriptionCoordinator.java index 72db41afe8e70..bc85318035e76 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/subscription/SubscriptionCoordinator.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/subscription/SubscriptionCoordinator.java @@ -31,6 +31,7 @@ import org.apache.iotdb.confignode.consensus.response.subscription.TopicTableResp; import org.apache.iotdb.confignode.i18n.ManagerMessages; import org.apache.iotdb.confignode.manager.ConfigManager; +import org.apache.iotdb.confignode.manager.pipe.agent.PipeConfigNodeAgent; import org.apache.iotdb.confignode.manager.pipe.coordinator.task.PipeTaskCoordinatorLock; import org.apache.iotdb.confignode.persistence.subscription.SubscriptionInfo; import org.apache.iotdb.confignode.rpc.thrift.TAlterTopicReq; @@ -187,8 +188,10 @@ public void unlockTopicAlteration(final String topicName, final boolean isTableM /////////////////////////////// Meta sync /////////////////////////////// public void startSubscriptionMetaSync() { - subscriptionMetaSyncer.start(); - subscriptionOwnerLeaseSyncer.start(); + if (PipeConfigNodeAgent.isSubscriptionFeatureEnabled()) { + subscriptionMetaSyncer.start(); + subscriptionOwnerLeaseSyncer.start(); + } } public void stopSubscriptionMetaSync() { diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/subscription/runtime/SubscriptionLeaderChangeHandler.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/subscription/runtime/SubscriptionLeaderChangeHandler.java index 6b888e424aa9c..a54b0ae2cb81b 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/subscription/runtime/SubscriptionLeaderChangeHandler.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/subscription/runtime/SubscriptionLeaderChangeHandler.java @@ -22,6 +22,7 @@ import org.apache.iotdb.confignode.manager.load.subscriber.ConsensusGroupStatisticsChangeEvent; import org.apache.iotdb.confignode.manager.load.subscriber.IClusterStatusSubscriber; import org.apache.iotdb.confignode.manager.load.subscriber.NodeStatisticsChangeEvent; +import org.apache.iotdb.confignode.manager.pipe.agent.PipeConfigNodeAgent; public class SubscriptionLeaderChangeHandler implements IClusterStatusSubscriber { @@ -33,11 +34,15 @@ public SubscriptionLeaderChangeHandler(final SubscriptionRuntimeCoordinator runt @Override public void onNodeStatisticsChanged(final NodeStatisticsChangeEvent event) { - runtimeCoordinator.handleNodeStatisticsChange(event); + if (PipeConfigNodeAgent.isSubscriptionFeatureEnabled()) { + runtimeCoordinator.handleNodeStatisticsChange(event); + } } @Override public void onConsensusGroupStatisticsChanged(final ConsensusGroupStatisticsChangeEvent event) { - runtimeCoordinator.handleLeaderChangeEvent(event); + if (PipeConfigNodeAgent.isSubscriptionFeatureEnabled()) { + runtimeCoordinator.handleLeaderChangeEvent(event); + } } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/PipeDataNodeAgent.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/PipeDataNodeAgent.java index e350f0a6f4bbb..cd0fc98109179 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/PipeDataNodeAgent.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/PipeDataNodeAgent.java @@ -19,15 +19,51 @@ package org.apache.iotdb.db.pipe.agent; +import org.apache.iotdb.commons.exception.IllegalPathException; +import org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant; +import org.apache.iotdb.commons.pipe.datastructure.options.PipeInclusionOptions; +import org.apache.iotdb.consensus.ConsensusFactory; +import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.pipe.agent.plugin.PipeDataNodePluginAgent; import org.apache.iotdb.db.pipe.agent.receiver.PipeDataNodeReceiverAgent; import org.apache.iotdb.db.pipe.agent.runtime.PipeDataNodeRuntimeAgent; import org.apache.iotdb.db.pipe.agent.task.PipeDataNodeTaskAgent; import org.apache.iotdb.db.service.DataNode; +import org.apache.iotdb.pipe.api.customizer.parameter.PipeParameters; + +import java.util.Collections; +import java.util.Map; /** {@link PipeDataNodeAgent} is the entry point of the pipe module in {@link DataNode}. */ public class PipeDataNodeAgent { + public static boolean isRatisConsensus() { + return ConsensusFactory.RATIS_CONSENSUS.equals( + IoTDBDescriptor.getInstance().getConfig().getDataRegionConsensusProtocolClass()); + } + + public static boolean isSubscriptionFeatureEnabled() { + return !isRatisConsensus(); + } + + public static boolean isPipeDataInsertionEnabled(final Map sourceAttributes) { + return isPipeDataInsertionEnabled( + new PipeParameters(sourceAttributes == null ? Collections.emptyMap() : sourceAttributes)); + } + + public static boolean isPipeDataInsertionEnabled(final PipeParameters sourceParameters) { + if (!isRatisConsensus()) { + return true; + } + try { + return !PipeInclusionOptions.areOptionsEnabled( + sourceParameters, PipeSourceConstant.EXTRACTOR_INCLUSION_DEFAULT_VALUE); + } catch (final IllegalPathException e) { + // Let the normal pipe parameter validation report malformed inclusion options. + return true; + } + } + private final PipeDataNodePluginAgent pipeDataNodePluginAgent; private final PipeDataNodeTaskAgent pipeDataNodeTaskAgent; private final PipeDataNodeRuntimeAgent pipeDataNodeRuntimeAgent; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgent.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgent.java index 5b7775c231562..565a62c4996e6 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgent.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgent.java @@ -146,6 +146,12 @@ protected boolean isShutdown() { return PipeDataNodeAgent.runtime().isShutdown(); } + @Override + protected boolean isPipeDataInsertionEnabled(final PipeStaticMeta pipeStaticMeta) { + return pipeStaticMeta.getPipeName().startsWith(PipeStaticMeta.CONSENSUS_PIPE_PREFIX) + || PipeDataNodeAgent.isPipeDataInsertionEnabled(pipeStaticMeta.getSourceParameters()); + } + @Override protected Map buildPipeTasks(final PipeMeta pipeMetaFromConfigNode) throws IllegalPathException { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/execution/PipeSubtaskExecutorManager.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/execution/PipeSubtaskExecutorManager.java index f83c23871f516..e37f977e00972 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/execution/PipeSubtaskExecutorManager.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/execution/PipeSubtaskExecutorManager.java @@ -21,6 +21,7 @@ import org.apache.iotdb.commons.consensus.iotv2.container.IoTV2GlobalComponentContainer; import org.apache.iotdb.commons.subscription.config.SubscriptionConfig; +import org.apache.iotdb.db.pipe.agent.PipeDataNodeAgent; import org.apache.iotdb.db.pipe.consensus.IoTConsensusV2SubtaskExecutor; import org.apache.iotdb.db.subscription.task.execution.SubscriptionSubtaskExecutor; @@ -65,7 +66,8 @@ private PipeSubtaskExecutorManager() { } public synchronized void ensureSubscriptionExecutors() { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (!SubscriptionConfig.getInstance().getSubscriptionEnabled() + || !PipeDataNodeAgent.isSubscriptionFeatureEnabled()) { return; } if (subscriptionExecutor == null || subscriptionExecutor.isShutdown()) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java index 59f135d34c312..97c8ca490fb99 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java @@ -29,6 +29,7 @@ import org.apache.iotdb.commons.exception.IoTDBException; import org.apache.iotdb.commons.exception.IoTDBRuntimeException; import org.apache.iotdb.commons.exception.pipe.PipeRuntimeOutOfMemoryCriticalException; +import org.apache.iotdb.commons.i18n.PipeMessages; import org.apache.iotdb.commons.log.LoggerPeriodicalLogReducer; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.pipe.config.PipeConfig; @@ -238,6 +239,13 @@ public synchronized TPipeTransferResp receive(final TPipeTransferReq req) { if (Objects.nonNull(authResp)) { return authResp; } + if (PipeDataNodeAgent.isRatisConsensus() && isDataInsertionRequest(requestType)) { + return new TPipeTransferResp( + RpcUtils.getStatus( + TSStatusCode.UNSUPPORTED_OPERATION, + PipeMessages + .MESSAGE_PIPE_DATA_INSERTION_IS_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_7D669ECC)); + } switch (requestType) { case HANDSHAKE_DATANODE_V1: { @@ -533,6 +541,26 @@ private TPipeTransferResp handleTransferTabletInsertNode( : executeStatementAndAddRedirectInfo(statement)); } + private static boolean isDataInsertionRequest(final PipeRequestType requestType) { + switch (requestType) { + case TRANSFER_TABLET_INSERT_NODE: + case TRANSFER_TABLET_INSERT_NODE_V2: + case TRANSFER_TABLET_RAW: + case TRANSFER_TABLET_RAW_V2: + case TRANSFER_TABLET_BINARY: + case TRANSFER_TABLET_BINARY_V2: + case TRANSFER_TABLET_BATCH: + case TRANSFER_TABLET_BATCH_V2: + case TRANSFER_TS_FILE_PIECE: + case TRANSFER_TS_FILE_SEAL: + case TRANSFER_TS_FILE_PIECE_WITH_MOD: + case TRANSFER_TS_FILE_SEAL_WITH_MOD: + return true; + default: + return false; + } + } + private TPipeTransferResp handleTransferTabletBinary(final PipeTransferTabletBinaryReq req) { final InsertBaseStatement statement = req.constructStatement(); return new TPipeTransferResp( diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java index ad2a85db15203..6acd0c4166539 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java @@ -36,6 +36,7 @@ import org.apache.iotdb.commons.conf.IoTDBConstant; import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.exception.IoTDBException; +import org.apache.iotdb.commons.i18n.PipeMessages; import org.apache.iotdb.commons.partition.DataPartition; import org.apache.iotdb.commons.partition.DataPartitionQueryParam; import org.apache.iotdb.commons.path.AlignedFullPath; @@ -3512,6 +3513,15 @@ public TPipeSubscribeResp pipeSubscribe(final TPipeSubscribeReq req) { if (!SESSION_MANAGER.checkLogin(clientSession)) { return getNotLoggedInPipeSubscribeResp(); } + if (!PipeDataNodeAgent.isSubscriptionFeatureEnabled()) { + return new TPipeSubscribeResp( + RpcUtils.getStatus( + TSStatusCode.UNSUPPORTED_OPERATION, + PipeMessages + .MESSAGE_SUBSCRIPTION_IS_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_194CE431), + PipeSubscribeResponseVersion.VERSION_1.getVersion(), + PipeSubscribeResponseType.ACK.getType()); + } return SubscriptionAgent.receiver().handle(req, clientSession.getUsername()); } finally { @@ -3663,7 +3673,8 @@ public void handleClientExit() { } PipeDataNodeAgent.receiver().thrift().handleClientExit(); PipeDataNodeAgent.receiver().legacy().handleClientExit(); - if (COMMON_CONFIG.getSubscriptionEnabled()) { + if (COMMON_CONFIG.getSubscriptionEnabled() + && PipeDataNodeAgent.isSubscriptionFeatureEnabled()) { SubscriptionAgent.receiver().handleClientExit(); } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java index 1f5f95b446020..02889262e0d04 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java @@ -1473,7 +1473,7 @@ public TPushPipeMetaRespExceptionMessage handleSinglePipeMeta(final ByteBuffer p @Override public TPushTopicMetaResp pushTopicMeta(TPushTopicMetaReq req) { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (!isSubscriptionFeatureEnabled()) { return new TPushTopicMetaResp() .setStatus(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode())); } @@ -1501,7 +1501,7 @@ public TPushTopicMetaResp pushTopicMeta(TPushTopicMetaReq req) { @Override public TPushTopicMetaResp pushSingleTopicMeta(TPushSingleTopicMetaReq req) { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (!isSubscriptionFeatureEnabled()) { return new TPushTopicMetaResp() .setStatus(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode())); } @@ -1539,7 +1539,7 @@ public TPushTopicMetaResp pushSingleTopicMeta(TPushSingleTopicMetaReq req) { @Override public TPushTopicMetaResp pushMultiTopicMeta(TPushMultiTopicMetaReq req) { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (!isSubscriptionFeatureEnabled()) { return new TPushTopicMetaResp() .setStatus(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode())); } @@ -1591,7 +1591,7 @@ public TPushTopicMetaResp pushMultiTopicMeta(TPushMultiTopicMetaReq req) { @Override public TSStatus pushTopicOwnerLease(TPushTopicOwnerLeaseReq req) { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (!isSubscriptionFeatureEnabled()) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } try { @@ -1605,7 +1605,7 @@ public TSStatus pushTopicOwnerLease(TPushTopicOwnerLeaseReq req) { @Override public TPushConsumerGroupMetaResp pushConsumerGroupMeta(TPushConsumerGroupMetaReq req) { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (!isSubscriptionFeatureEnabled()) { return new TPushConsumerGroupMetaResp() .setStatus(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode())); } @@ -1634,7 +1634,7 @@ public TPushConsumerGroupMetaResp pushConsumerGroupMeta(TPushConsumerGroupMetaRe @Override public TPushConsumerGroupMetaResp pushSingleConsumerGroupMeta( TPushSingleConsumerGroupMetaReq req) { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (!isSubscriptionFeatureEnabled()) { return new TPushConsumerGroupMetaResp() .setStatus(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode())); } @@ -1669,7 +1669,7 @@ public TPushConsumerGroupMetaResp pushSingleConsumerGroupMeta( @Override public TPullCommitProgressResp pullCommitProgress(TPullCommitProgressReq req) { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (!isSubscriptionFeatureEnabled()) { return new TPullCommitProgressResp(RpcUtils.getStatus(TSStatusCode.UNSUPPORTED_OPERATION)) .setCommitRegionProgress(Collections.emptyMap()) .setSubscriptionProgress(Collections.emptyMap()); @@ -1694,7 +1694,7 @@ public TPullCommitProgressResp pullCommitProgress(TPullCommitProgressReq req) { @Override public TSStatus syncSubscriptionProgress(TSyncSubscriptionProgressReq req) { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (!isSubscriptionFeatureEnabled()) { return RpcUtils.getStatus(TSStatusCode.UNSUPPORTED_OPERATION); } @@ -1719,7 +1719,7 @@ public TSStatus syncSubscriptionProgress(TSyncSubscriptionProgressReq req) { @Override public TSStatus pushSubscriptionRuntime(TPushSubscriptionRuntimeReq req) { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (!isSubscriptionFeatureEnabled()) { return RpcUtils.getStatus(TSStatusCode.UNSUPPORTED_OPERATION); } @@ -2530,7 +2530,7 @@ public TSStatus updateRegionCache(TRegionRouteReq req) { return RpcUtils.getStatus(TSStatusCode.PARTITION_CACHE_UPDATE_ERROR); } - if (SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (isSubscriptionFeatureEnabled()) { // Notify consensus subscription queues of any preferred-writer changes try { ConsensusSubscriptionSetupHandler.onRegionRouteChanged( @@ -3395,6 +3395,11 @@ private TEndPoint getConsensusEndPoint( return nodeLocation.getSchemaRegionConsensusEndPoint(); } + private boolean isSubscriptionFeatureEnabled() { + return SubscriptionConfig.getInstance().getSubscriptionEnabled() + && PipeDataNodeAgent.isSubscriptionFeatureEnabled(); + } + @Override public TSStatus createPipePlugin(TCreatePipePluginInstanceReq req) { try { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java index 251d3f96afe63..28fc904800124 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java @@ -435,6 +435,9 @@ private ClusterConfigTaskExecutorHolder() {} } private static final SettableFuture SUBSCRIPTION_NOT_ENABLED_ERROR_FUTURE; + private static final SettableFuture SUBSCRIPTION_DISABLED_BY_RATIS_ERROR_FUTURE; + private static final SettableFuture + PIPE_DATA_INSERTION_DISABLED_BY_RATIS_ERROR_FUTURE; static { SUBSCRIPTION_NOT_ENABLED_ERROR_FUTURE = SettableFuture.create(); @@ -442,6 +445,20 @@ private ClusterConfigTaskExecutorHolder() {} new IoTDBException( DataNodeQueryMessages.QUERY_EXCEPTION_SUBSCRIPTION_IS_NOT_ENABLED_7F43DCBB, TSStatusCode.SUBSCRIPTION_NOT_ENABLED_ERROR.getStatusCode())); + + SUBSCRIPTION_DISABLED_BY_RATIS_ERROR_FUTURE = SettableFuture.create(); + SUBSCRIPTION_DISABLED_BY_RATIS_ERROR_FUTURE.setException( + new IoTDBException( + PipeMessages + .MESSAGE_SUBSCRIPTION_IS_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_194CE431, + TSStatusCode.UNSUPPORTED_OPERATION.getStatusCode())); + + PIPE_DATA_INSERTION_DISABLED_BY_RATIS_ERROR_FUTURE = SettableFuture.create(); + PIPE_DATA_INSERTION_DISABLED_BY_RATIS_ERROR_FUTURE.setException( + new IoTDBException( + PipeMessages + .MESSAGE_PIPE_DATA_INSERTION_IS_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_7D669ECC, + TSStatusCode.UNSUPPORTED_OPERATION.getStatusCode())); } public static ClusterConfigTaskExecutor getInstance() { @@ -2245,6 +2262,10 @@ public SettableFuture unsetSchemaTemplate( @Override public SettableFuture createPipe( final CreatePipeStatement createPipeStatement) { + if (!PipeDataNodeAgent.isPipeDataInsertionEnabled(createPipeStatement.getSourceAttributes())) { + return PIPE_DATA_INSERTION_DISABLED_BY_RATIS_ERROR_FUTURE; + } + final SettableFuture future = SettableFuture.create(); // Verify that Pipe is disabled if TSFile encryption is enabled @@ -2745,6 +2766,9 @@ public SettableFuture alterPipe(final AlterPipeStatement alter checkedSink.remove( PipeParameters.KeyReducer.reduce(PipeSinkConstant.SINK_IOTDB_PASSWORD_KEY)); } + if (!PipeDataNodeAgent.isPipeDataInsertionEnabled(sourceAttributes)) { + return PIPE_DATA_INSERTION_DISABLED_BY_RATIS_ERROR_FUTURE; + } PipeDataNodeAgent.plugin() .validate(pipeName, checkedSource, processorAttributes, checkedSink); } catch (final Exception e) { @@ -3163,6 +3187,9 @@ public SettableFuture createTopic( if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { return SUBSCRIPTION_NOT_ENABLED_ERROR_FUTURE; } + if (!PipeDataNodeAgent.isSubscriptionFeatureEnabled()) { + return SUBSCRIPTION_DISABLED_BY_RATIS_ERROR_FUTURE; + } final SettableFuture future = SettableFuture.create(); @@ -3237,6 +3264,9 @@ public SettableFuture alterTopic( if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { return SUBSCRIPTION_NOT_ENABLED_ERROR_FUTURE; } + if (!PipeDataNodeAgent.isSubscriptionFeatureEnabled()) { + return SUBSCRIPTION_DISABLED_BY_RATIS_ERROR_FUTURE; + } final SettableFuture future = SettableFuture.create(); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/DataNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/DataNode.java index 6bca0cace69cf..aad41584276f8 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/DataNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/DataNode.java @@ -301,7 +301,8 @@ protected void start() { // TierManager need DataNodeId to do some operations so the reset method need to be invoked // after DataNode adding TierManager.getInstance().resetFolders(); - if (SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (SubscriptionConfig.getInstance().getSubscriptionEnabled() + && PipeDataNodeAgent.isSubscriptionFeatureEnabled()) { ConsensusSubscriptionSetupHandler.ensureNewRegionListenerRegistered(); } // Active DataNode @@ -942,7 +943,8 @@ private void setUp() throws StartupException, IOException { registerInternalRPCService(); // Register subscription agent before pipe agent - if (SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (SubscriptionConfig.getInstance().getSubscriptionEnabled() + && PipeDataNodeAgent.isSubscriptionFeatureEnabled()) { registerManager.register(SubscriptionAgent.runtime()); } registerManager.register(PipeDataNodeAgent.runtime()); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgent.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgent.java index a213eac6dfc1f..7dabbbd856aee 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgent.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgent.java @@ -22,9 +22,11 @@ import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.concurrent.IoTDBThreadPoolFactory; import org.apache.iotdb.commons.concurrent.threadpool.ScheduledExecutorUtil; +import org.apache.iotdb.commons.i18n.PipeMessages; import org.apache.iotdb.commons.subscription.config.SubscriptionConfig; import org.apache.iotdb.db.i18n.DataNodePipeMessages; import org.apache.iotdb.db.i18n.DataNodeQueryMessages; +import org.apache.iotdb.db.pipe.agent.PipeDataNodeAgent; import org.apache.iotdb.db.subscription.receiver.SubscriptionReceiver; import org.apache.iotdb.db.subscription.receiver.SubscriptionReceiverV1; import org.apache.iotdb.rpc.RpcUtils; @@ -63,6 +65,15 @@ public class SubscriptionReceiverAgent { PipeSubscribeResponseVersion.VERSION_1.getVersion(), PipeSubscribeResponseType.ACK.getType()); + private static final TPipeSubscribeResp SUBSCRIPTION_DISABLED_BY_RATIS_RESP = + new TPipeSubscribeResp( + RpcUtils.getStatus( + TSStatusCode.UNSUPPORTED_OPERATION, + PipeMessages + .MESSAGE_SUBSCRIPTION_IS_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_194CE431), + PipeSubscribeResponseVersion.VERSION_1.getVersion(), + PipeSubscribeResponseType.ACK.getType()); + private static final TPipeSubscribeResp SUBSCRIPTION_CONSUMER_FENCED_RESP = new TPipeSubscribeResp( RpcUtils.getStatus( @@ -90,8 +101,11 @@ public class SubscriptionReceiverAgent { SubscriptionReceiverAgent() { this( SubscriptionReceiverV1::new, - SubscriptionConfig.getInstance().getSubscriptionEnabled(), - () -> SubscriptionConfig.getInstance().getSubscriptionEnabled()); + SubscriptionConfig.getInstance().getSubscriptionEnabled() + && PipeDataNodeAgent.isSubscriptionFeatureEnabled(), + () -> + SubscriptionConfig.getInstance().getSubscriptionEnabled() + && PipeDataNodeAgent.isSubscriptionFeatureEnabled()); } SubscriptionReceiverAgent( @@ -129,6 +143,9 @@ public TPipeSubscribeResp handle(final TPipeSubscribeReq req, final String usern PipeSubscribeResponseVersion.VERSION_1.getVersion(), PipeSubscribeResponseType.ACK.getType()); } + if (!PipeDataNodeAgent.isSubscriptionFeatureEnabled()) { + return SUBSCRIPTION_DISABLED_BY_RATIS_RESP; + } if (!subscriptionEnabledSupplier.getAsBoolean()) { return SUBSCRIPTION_NOT_ENABLED_ERROR_RESP; } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionRuntimeAgent.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionRuntimeAgent.java index 2962f525e7020..ff8ac5556055b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionRuntimeAgent.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionRuntimeAgent.java @@ -23,6 +23,7 @@ import org.apache.iotdb.commons.service.IService; import org.apache.iotdb.commons.service.ServiceType; import org.apache.iotdb.commons.subscription.config.SubscriptionConfig; +import org.apache.iotdb.db.pipe.agent.PipeDataNodeAgent; import org.apache.iotdb.db.subscription.broker.consensus.ConsensusSubscriptionSetupHandler; import org.apache.iotdb.db.subscription.task.execution.ConsensusSubscriptionPrefetchExecutorManager; @@ -64,7 +65,8 @@ SubscriptionTopicAgent topic() { @Override public void start() throws StartupException { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (!SubscriptionConfig.getInstance().getSubscriptionEnabled() + || !PipeDataNodeAgent.isSubscriptionFeatureEnabled()) { return; } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusSubscriptionSetupHandler.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusSubscriptionSetupHandler.java index 1ae30b123e100..654030eaa2be6 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusSubscriptionSetupHandler.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/broker/consensus/ConsensusSubscriptionSetupHandler.java @@ -38,6 +38,7 @@ import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.consensus.DataRegionConsensusImpl; import org.apache.iotdb.db.i18n.DataNodePipeMessages; +import org.apache.iotdb.db.pipe.agent.PipeDataNodeAgent; import org.apache.iotdb.db.storageengine.StorageEngine; import org.apache.iotdb.db.storageengine.dataregion.DataRegion; import org.apache.iotdb.db.subscription.agent.SubscriptionAgent; @@ -1138,7 +1139,8 @@ public static void handleNewSubscriptions( public static void applyRuntimeState( final TConsensusGroupId groupId, final ConsensusRegionRuntimeState runtimeState) { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (!SubscriptionConfig.getInstance().getSubscriptionEnabled() + || !PipeDataNodeAgent.isSubscriptionFeatureEnabled()) { return; } @@ -1173,7 +1175,8 @@ public static void applyRuntimeState( public static void onRegionRouteChanged( final Map newMap, final long routingTimestamp) { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (!SubscriptionConfig.getInstance().getSubscriptionEnabled() + || !PipeDataNodeAgent.isSubscriptionFeatureEnabled()) { return; } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/task/execution/ConsensusSubscriptionPrefetchExecutorManager.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/task/execution/ConsensusSubscriptionPrefetchExecutorManager.java index 9362a38a58b7e..f442ac8a9b479 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/task/execution/ConsensusSubscriptionPrefetchExecutorManager.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/task/execution/ConsensusSubscriptionPrefetchExecutorManager.java @@ -20,6 +20,7 @@ package org.apache.iotdb.db.subscription.task.execution; import org.apache.iotdb.commons.subscription.config.SubscriptionConfig; +import org.apache.iotdb.db.pipe.agent.PipeDataNodeAgent; public class ConsensusSubscriptionPrefetchExecutorManager { @@ -31,7 +32,8 @@ private ConsensusSubscriptionPrefetchExecutorManager() { } public synchronized void start() { - if (!SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (!SubscriptionConfig.getInstance().getSubscriptionEnabled() + || !PipeDataNodeAgent.isSubscriptionFeatureEnabled()) { started = false; return; } @@ -42,7 +44,9 @@ public synchronized void start() { } public synchronized ConsensusSubscriptionPrefetchExecutor getExecutor() { - if (!started || !SubscriptionConfig.getInstance().getSubscriptionEnabled()) { + if (!started + || !SubscriptionConfig.getInstance().getSubscriptionEnabled() + || !PipeDataNodeAgent.isSubscriptionFeatureEnabled()) { return null; } if (executor == null || executor.isShutdown()) { diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgentTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgentTest.java index 83709d41c3fb4..c1368441148e2 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgentTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgentTest.java @@ -34,6 +34,7 @@ import org.apache.iotdb.commons.pipe.agent.task.meta.PipeStatus; import org.apache.iotdb.commons.pipe.agent.task.meta.PipeTaskMeta; import org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant; +import org.apache.iotdb.consensus.ConsensusFactory; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.pipe.agent.PipeDataNodeAgent; import org.apache.iotdb.pipe.api.exception.PipeException; @@ -55,6 +56,34 @@ public class PipeDataNodeTaskAgentTest { private static final int LOCAL_NODE_ID = 1; private static final int REGION_ID = 7; + @Test + public void testRatisDetectsPipeDataInsertionOption() { + final String originalConsensus = + IoTDBDescriptor.getInstance().getConfig().getDataRegionConsensusProtocolClass(); + try { + IoTDBDescriptor.getInstance() + .getConfig() + .setDataRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS); + + Assert.assertTrue( + PipeDataNodeAgent.isPipeDataInsertionEnabled( + Map.of(PipeSourceConstant.EXTRACTOR_INCLUSION_KEY, "schema"))); + Assert.assertTrue( + PipeDataNodeAgent.isPipeDataInsertionEnabled( + Map.of(PipeSourceConstant.EXTRACTOR_INCLUSION_KEY, "data.delete"))); + Assert.assertFalse( + PipeDataNodeAgent.isPipeDataInsertionEnabled( + Map.of(PipeSourceConstant.EXTRACTOR_INCLUSION_KEY, "data.insert"))); + Assert.assertFalse( + PipeDataNodeAgent.isPipeDataInsertionEnabled( + Map.of(PipeSourceConstant.EXTRACTOR_INCLUSION_KEY, "data.insert,data.delete"))); + } finally { + IoTDBDescriptor.getInstance() + .getConfig() + .setDataRegionConsensusProtocolClass(originalConsensus); + } + } + @Test public void testGetPipeTaskProgressIndexReportsMissingTaskMeta() throws Exception { final PipeDataNodeTaskAgent taskAgent = new PipeDataNodeTaskAgent(); diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgentTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgentTest.java index 21ef6ecb101de..fc68f97c6e856 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgentTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgentTest.java @@ -20,6 +20,8 @@ package org.apache.iotdb.db.subscription.agent; import org.apache.iotdb.common.rpc.thrift.TSStatus; +import org.apache.iotdb.consensus.ConsensusFactory; +import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.subscription.receiver.SubscriptionReceiver; import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; @@ -54,6 +56,34 @@ public class SubscriptionReceiverAgentTest { + @Test + public void testRatisRejectsSubscriptionBeforeCreatingReceiver() throws IOException { + final String originalConsensus = + IoTDBDescriptor.getInstance().getConfig().getDataRegionConsensusProtocolClass(); + try { + IoTDBDescriptor.getInstance() + .getConfig() + .setDataRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS); + final SubscriptionReceiverAgent agent = + new SubscriptionReceiverAgent( + () -> { + throw new AssertionError("Receiver must not be created when Ratis is enabled"); + }, + false, + () -> { + throw new AssertionError("Subscription supplier must not be called"); + }); + + Assert.assertEquals( + TSStatusCode.UNSUPPORTED_OPERATION.getStatusCode(), + agent.handle(createHandshakeRequest("group", "consumer"), "root").getStatus().getCode()); + } finally { + IoTDBDescriptor.getInstance() + .getConfig() + .setDataRegionConsensusProtocolClass(originalConsensus); + } + } + @Test public void testDisabledSubscriptionRejectsRequest() throws IOException { final SubscriptionReceiverAgent agent = diff --git a/iotdb-core/node-commons/src/main/i18n/en/org/apache/iotdb/commons/i18n/PipeMessages.java b/iotdb-core/node-commons/src/main/i18n/en/org/apache/iotdb/commons/i18n/PipeMessages.java index 0aafc35842770..0a33845d4608c 100644 --- a/iotdb-core/node-commons/src/main/i18n/en/org/apache/iotdb/commons/i18n/PipeMessages.java +++ b/iotdb-core/node-commons/src/main/i18n/en/org/apache/iotdb/commons/i18n/PipeMessages.java @@ -21,6 +21,12 @@ public final class PipeMessages { + public static final String MESSAGE_SUBSCRIPTION_IS_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_194CE431 = + "Subscription is disabled when data region consensus protocol is Ratis."; + + public static final String MESSAGE_PIPE_DATA_INSERTION_IS_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_7D669ECC = + "Pipe synchronization with data insertion is disabled when data region consensus protocol is Ratis."; + // ===================== Common / Shared ===================== public static final String UTILITY_CLASS = "Utility class"; diff --git a/iotdb-core/node-commons/src/main/i18n/zh/org/apache/iotdb/commons/i18n/PipeMessages.java b/iotdb-core/node-commons/src/main/i18n/zh/org/apache/iotdb/commons/i18n/PipeMessages.java index 9746b71500d2d..5a9dc3ccf4ed1 100644 --- a/iotdb-core/node-commons/src/main/i18n/zh/org/apache/iotdb/commons/i18n/PipeMessages.java +++ b/iotdb-core/node-commons/src/main/i18n/zh/org/apache/iotdb/commons/i18n/PipeMessages.java @@ -21,6 +21,12 @@ public final class PipeMessages { + public static final String MESSAGE_SUBSCRIPTION_IS_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_194CE431 = + "当数据区域共识协议为 Ratis 时,Subscription 功能不可用。"; + + public static final String MESSAGE_PIPE_DATA_INSERTION_IS_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_7D669ECC = + "当数据区域共识协议为 Ratis 时,包含 data.insert 的 Pipe 同步不可用。"; + // ===================== Common / Shared ===================== public static final String UTILITY_CLASS = "工具类"; diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/PipeTaskAgent.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/PipeTaskAgent.java index 34bad05803fc5..bdd3c384a4e95 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/PipeTaskAgent.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/agent/task/PipeTaskAgent.java @@ -209,6 +209,10 @@ protected TPushPipeMetaRespExceptionMessage handleSinglePipeMetaChangesInternal( protected abstract boolean isShutdown(); + protected boolean isPipeDataInsertionEnabled(final PipeStaticMeta pipeStaticMeta) { + return true; + } + private void executeSinglePipeMetaChanges(final PipeMeta metaFromCoordinator) throws IllegalPathException { final String pipeName = metaFromCoordinator.getStaticMeta().getPipeName(); @@ -231,6 +235,14 @@ private void executeSinglePipeMetaChanges(final PipeMeta metaFromCoordinator) return; } + if (!isPipeDataInsertionEnabled(metaFromCoordinator.getStaticMeta())) { + final PipeMeta metaInAgent = pipeMetaKeeper.getPipeMeta(metaFromCoordinator.getStaticMeta()); + if (metaInAgent != null) { + dropPipe(pipeName, metaInAgent.getStaticMeta().getCreationTime()); + } + return; + } + final PipeMeta metaInAgent = pipeMetaKeeper.getPipeMeta(metaFromCoordinator.getStaticMeta()); // If pipe meta does not exist on local agent, create a new pipe diff --git a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/pipe/datastructure/options/PipeInclusionOptionsTest.java b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/pipe/datastructure/options/PipeInclusionOptionsTest.java index 63ab1c739939e..a2d7d5fab615d 100644 --- a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/pipe/datastructure/options/PipeInclusionOptionsTest.java +++ b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/pipe/datastructure/options/PipeInclusionOptionsTest.java @@ -20,10 +20,14 @@ package org.apache.iotdb.commons.pipe.datastructure.options; import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.pipe.api.customizer.parameter.PipeParameters; import org.junit.Assert; import org.junit.Test; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import java.util.Set; public class PipeInclusionOptionsTest { @@ -45,4 +49,27 @@ public void testSchemaDeleteAliasIncludesOrdinaryTimeseriesDrop() throws Excepti Assert.assertFalse(options.contains(new PartialPath("schema.timeseries.ordinary.delete"))); Assert.assertTrue(PipeInclusionOptions.optionsAreAllLegal("schema.delete", true, false)); } + + @Test + public void testDataInsertionOptionSelection() throws Exception { + Assert.assertTrue( + PipeInclusionOptions.areOptionsEnabled( + new PipeParameters(Collections.emptyMap()), "data.insert")); + Assert.assertTrue( + PipeInclusionOptions.areOptionsEnabled( + new PipeParameters(Map.of("extractor.inclusion", "data.insert,data.delete")), + "data.insert")); + Assert.assertFalse( + PipeInclusionOptions.areOptionsEnabled( + new PipeParameters(Map.of("extractor.inclusion", "schema")), "data.insert")); + Assert.assertFalse( + PipeInclusionOptions.areOptionsEnabled( + new PipeParameters(Map.of("extractor.inclusion", "data.delete")), "data.insert")); + + final Map attributes = new HashMap<>(); + attributes.put("extractor.inclusion", "all"); + attributes.put("extractor.inclusion.exclusion", "data.insert"); + Assert.assertFalse( + PipeInclusionOptions.areOptionsEnabled(new PipeParameters(attributes), "data.insert")); + } } From eab49c25efe9cf004dc84f2c28ea2216b2b0c6c5 Mon Sep 17 00:00:00 2001 From: Caideyipi <87789683+Caideyipi@users.noreply.github.com> Date: Fri, 30 Oct 2026 14:08:16 +0800 Subject: [PATCH 6/9] Fix Ratis pipe recovery and receiver validation gaps --- .../pipe/agent/PipeConfigNodeAgent.java | 4 +- .../agent/task/PipeConfigNodeTaskAgent.java | 6 + .../persistence/pipe/PipeTaskInfo.java | 18 ++ .../task/PipeConfigNodeTaskAgentTest.java | 118 +++++++++++++ .../pipe/PipeTaskInfoRatisTest.java | 157 ++++++++++++++++++ .../db/pipe/agent/PipeDataNodeAgent.java | 4 +- .../agent/task/PipeDataNodeTaskAgent.java | 1 + .../legacy/IoTDBLegacyPipeReceiverAgent.java | 14 ++ .../agent/task/PipeDataNodeTaskAgentTest.java | 68 +++++++- .../IoTDBLegacyPipeReceiverAgentTest.java | 72 ++++++++ .../options/PipeInclusionOptions.java | 11 ++ .../options/PipeInclusionOptionsTest.java | 14 ++ 12 files changed, 480 insertions(+), 7 deletions(-) create mode 100644 iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgentTest.java create mode 100644 iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/pipe/PipeTaskInfoRatisTest.java diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/PipeConfigNodeAgent.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/PipeConfigNodeAgent.java index b8f027b45c84f..2bd1d65c01622 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/PipeConfigNodeAgent.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/PipeConfigNodeAgent.java @@ -20,7 +20,6 @@ package org.apache.iotdb.confignode.manager.pipe.agent; import org.apache.iotdb.commons.exception.IllegalPathException; -import org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant; import org.apache.iotdb.commons.pipe.datastructure.options.PipeInclusionOptions; import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor; import org.apache.iotdb.confignode.manager.pipe.agent.plugin.PipeConfigNodePluginAgent; @@ -56,8 +55,7 @@ public static boolean isPipeDataInsertionEnabled(final PipeParameters sourcePara return true; } try { - return !PipeInclusionOptions.areOptionsEnabled( - sourceParameters, PipeSourceConstant.EXTRACTOR_INCLUSION_DEFAULT_VALUE); + return !PipeInclusionOptions.isDataInsertionIncluded(sourceParameters); } catch (final IllegalPathException e) { // Let the normal pipe parameter validation report malformed inclusion options. return true; diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgent.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgent.java index 323fdb8ca0045..adb9391513b3d 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgent.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgent.java @@ -68,6 +68,12 @@ protected boolean isShutdown() { return PipeConfigNodeAgent.runtime().isShutdown(); } + @Override + protected boolean isPipeDataInsertionEnabled(final PipeStaticMeta pipeStaticMeta) { + return pipeStaticMeta.getPipeName().startsWith(PipeStaticMeta.CONSENSUS_PIPE_PREFIX) + || PipeConfigNodeAgent.isPipeDataInsertionEnabled(pipeStaticMeta.getSourceParameters()); + } + @Override protected void thawRate(final String pipeName, final long creationTime) { PipeConfigNodeRemainingTimeMetrics.getInstance().thawRate(pipeName + "_" + creationTime); diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/pipe/PipeTaskInfo.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/pipe/PipeTaskInfo.java index 2427e7f3329af..7ac708c3ad52f 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/pipe/PipeTaskInfo.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/pipe/PipeTaskInfo.java @@ -25,6 +25,7 @@ import org.apache.iotdb.commons.consensus.index.impl.MinimumProgressIndex; import org.apache.iotdb.commons.exception.pipe.PipeRuntimeCriticalException; import org.apache.iotdb.commons.exception.pipe.PipeRuntimeException; +import org.apache.iotdb.commons.i18n.PipeMessages; import org.apache.iotdb.commons.pipe.agent.plugin.builtin.BuiltinPipePlugin; import org.apache.iotdb.commons.pipe.agent.task.PipeTaskAgent; import org.apache.iotdb.commons.pipe.agent.task.meta.PipeMeta; @@ -55,6 +56,7 @@ import org.apache.iotdb.confignode.consensus.request.write.pipe.task.SetPipeStatusWithStoppedByRuntimeExceptionPlanV2; import org.apache.iotdb.confignode.consensus.response.pipe.task.PipeTableResp; import org.apache.iotdb.confignode.i18n.ConfigNodeMessages; +import org.apache.iotdb.confignode.manager.pipe.agent.PipeConfigNodeAgent; import org.apache.iotdb.confignode.manager.pipe.resource.PipeConfigNodeResourceManager; import org.apache.iotdb.confignode.procedure.impl.pipe.runtime.PipeHandleMetaChangeProcedure; import org.apache.iotdb.confignode.rpc.thrift.TAlterPipeReq; @@ -193,6 +195,7 @@ private boolean checkBeforeCreatePipeInternal(final TCreatePipeReq createPipeReq createPipeRequest.getExtractorAttributes(), createPipeRequest.getProcessorAttributes(), createPipeRequest.getConnectorAttributes()); + checkPipeDataInsertionAvailability(pipeStaticMeta); if (!pipeMetaKeeper.containsPipeMetaOverlapped(pipeStaticMeta)) { return true; } @@ -309,6 +312,7 @@ private void checkAndUpdateRequestBeforeAlterPipeInternal(final TAlterPipeReq al alterPipeRequest.getExtractorAttributes(), alterPipeRequest.getProcessorAttributes(), alterPipeRequest.getConnectorAttributes()); + checkPipeDataInsertionAvailability(updatedPipeStaticMeta); if (pipeMetaKeeper.containsPipeMetaOverlapped( updatedPipeStaticMeta, pipeStaticMetaFromCoordinator)) { final String exceptionMessage = @@ -374,6 +378,7 @@ private void checkBeforeStartPipeInternal(final String pipeName) throws PipeExce throw new PipeException(exceptionMessage); } + checkPipeDataInsertionAvailability(getPipeMetaByPipeName(pipeName).getStaticMeta()); final PipeStatus pipeStatus = getPipeStatus(pipeName); if (pipeStatus == PipeStatus.DROPPED) { final String exceptionMessage = @@ -403,6 +408,8 @@ private void checkBeforeStartPipeInternal(final String pipeName, final boolean i throw new PipeException(exceptionMessage); } + checkPipeDataInsertionAvailability( + getPipeMetaByPipeName(pipeName, isTableModel).getStaticMeta()); final PipeStatus pipeStatus = getPipeStatus(pipeName, isTableModel); if (pipeStatus == PipeStatus.DROPPED) { final String exceptionMessage = @@ -422,6 +429,17 @@ private void checkBeforeStartPipeInternal(final String pipeName, final boolean i } } + private void checkPipeDataInsertionAvailability(final PipeStaticMeta staticMeta) { + // Procedures call these validators while holding the coordinator lock. Recheck the merged + // source parameters here because concurrent ALTER requests can change the RPC-time snapshot. + if (!staticMeta.getPipeName().startsWith(PipeStaticMeta.CONSENSUS_PIPE_PREFIX) + && !PipeConfigNodeAgent.isPipeDataInsertionEnabled(staticMeta.getSourceParameters())) { + throw new PipeException( + PipeMessages + .MESSAGE_PIPE_DATA_INSERTION_IS_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_7D669ECC); + } + } + public void checkBeforeStopPipe(final String pipeName) throws PipeException { acquireReadLock(); try { diff --git a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgentTest.java b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgentTest.java new file mode 100644 index 0000000000000..738737d65c7d4 --- /dev/null +++ b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgentTest.java @@ -0,0 +1,118 @@ +/* + * 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.iotdb.confignode.manager.pipe.agent.task; + +import org.apache.iotdb.commons.pipe.agent.task.PipeTask; +import org.apache.iotdb.commons.pipe.agent.task.meta.PipeMeta; +import org.apache.iotdb.commons.pipe.agent.task.meta.PipeRuntimeMeta; +import org.apache.iotdb.commons.pipe.agent.task.meta.PipeStaticMeta; +import org.apache.iotdb.commons.pipe.agent.task.meta.PipeStatus; +import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor; +import org.apache.iotdb.confignode.manager.pipe.agent.PipeConfigNodeAgent; +import org.apache.iotdb.consensus.ConsensusFactory; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +public class PipeConfigNodeTaskAgentTest { + @Test + public void testRecoveryDropsDisallowedPipeAndKeepsMetadataPipes() { + final String originalConsensus = + ConfigNodeDescriptor.getInstance().getConf().getDataRegionConsensusProtocolClass(); + final boolean leaderReady = PipeConfigNodeAgent.runtime().isLeaderReady(); + final TestingTaskAgent agent = new TestingTaskAgent(); + try { + PipeConfigNodeAgent.runtime().notifyLeaderReady(); + ConfigNodeDescriptor.getInstance() + .getConf() + .setDataRegionConsensusProtocolClass(ConsensusFactory.IOT_CONSENSUS); + final PipeMeta insertion = meta("old", "all"); + Assert.assertNull(agent.handleSinglePipeMetaChanges(insertion)); + final PipeTask oldTask = agent.tasks.get("old"); + verify(oldTask).start(); + ConfigNodeDescriptor.getInstance() + .getConf() + .setDataRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS); + Assert.assertNull(agent.handleSinglePipeMetaChanges(insertion)); + verify(oldTask).stop(); + verify(oldTask).drop(); + Assert.assertFalse(agent.contains("old")); + Assert.assertNull(agent.handleSinglePipeMetaChanges(meta("blocked", "all"))); + Assert.assertFalse(agent.contains("blocked")); + Assert.assertFalse(agent.tasks.containsKey("blocked")); + for (String inclusion : new String[] {"schema", "data.delete", "schema,data.delete"}) { + Assert.assertNull(agent.handleSinglePipeMetaChanges(meta(inclusion, inclusion))); + verify(agent.tasks.get(inclusion)).start(); + Assert.assertTrue(agent.contains(inclusion)); + } + ConfigNodeDescriptor.getInstance() + .getConf() + .setDataRegionConsensusProtocolClass(ConsensusFactory.IOT_CONSENSUS_V2); + final String internal = PipeStaticMeta.CONSENSUS_PIPE_PREFIX + "DataRegion[7]_1_2"; + Assert.assertNull(agent.handleSinglePipeMetaChanges(meta(internal, "data.insert"))); + verify(agent.tasks.get(internal)).start(); + Assert.assertTrue(agent.contains(internal)); + } finally { + agent.dropAllPipeTasks(); + ConfigNodeDescriptor.getInstance() + .getConf() + .setDataRegionConsensusProtocolClass(originalConsensus); + if (!leaderReady) { + PipeConfigNodeAgent.runtime().notifyLeaderUnavailable(); + } + } + } + + private PipeMeta meta(String name, String inclusion) { + final PipeMeta meta = + new PipeMeta( + new PipeStaticMeta( + name, + 1L, + new HashMap<>(Map.of("inclusion", inclusion)), + new HashMap<>(), + new HashMap<>()), + new PipeRuntimeMeta()); + meta.getRuntimeMeta().getStatus().set(PipeStatus.RUNNING); + return meta; + } + + private static class TestingTaskAgent extends PipeConfigNodeTaskAgent { + private final Map tasks = new HashMap<>(); + + @Override + protected Map buildPipeTasks(PipeMeta meta) { + final PipeTask task = mock(PipeTask.class); + tasks.put(meta.getStaticMeta().getPipeName(), task); + return Collections.singletonMap(1, task); + } + + private boolean contains(String name) { + return pipeMetaKeeper.containsPipeMeta(name); + } + } +} diff --git a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/pipe/PipeTaskInfoRatisTest.java b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/pipe/PipeTaskInfoRatisTest.java new file mode 100644 index 0000000000000..1a7050aa0228b --- /dev/null +++ b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/pipe/PipeTaskInfoRatisTest.java @@ -0,0 +1,157 @@ +/* + * 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.iotdb.confignode.persistence.pipe; + +import org.apache.iotdb.commons.i18n.PipeMessages; +import org.apache.iotdb.commons.pipe.agent.task.meta.PipeRuntimeMeta; +import org.apache.iotdb.commons.pipe.agent.task.meta.PipeStaticMeta; +import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor; +import org.apache.iotdb.confignode.consensus.request.write.pipe.task.AlterPipePlanV2; +import org.apache.iotdb.confignode.consensus.request.write.pipe.task.CreatePipePlanV2; +import org.apache.iotdb.confignode.manager.pipe.agent.PipeConfigNodeAgent; +import org.apache.iotdb.confignode.rpc.thrift.TAlterPipeReq; +import org.apache.iotdb.confignode.rpc.thrift.TCreatePipeReq; +import org.apache.iotdb.consensus.ConsensusFactory; +import org.apache.iotdb.pipe.api.exception.PipeException; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; + +public class PipeTaskInfoRatisTest { + private String originalConsensus; + private final PipeTaskInfo pipeTaskInfo = new PipeTaskInfo(); + + @Before + public void setUp() { + originalConsensus = + ConfigNodeDescriptor.getInstance().getConf().getDataRegionConsensusProtocolClass(); + ConfigNodeDescriptor.getInstance() + .getConf() + .setDataRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS); + } + + @After + public void tearDown() { + ConfigNodeDescriptor.getInstance() + .getConf() + .setDataRegionConsensusProtocolClass(originalConsensus); + } + + @Test + public void testCreateAndStartValidateUnderProcedureLock() { + final TCreatePipeReq insertion = createRequest("insertion", Map.of()); + assertDisabled(() -> pipeTaskInfo.checkBeforeCreatePipe(insertion)); + assertDisabled( + () -> + pipeTaskInfo.checkBeforeCreatePipe( + createRequest("suffix", Map.of("inclusion", "data.insert.extra")))); + for (final String inclusion : new String[] {"schema", "data.delete", "schema,data.delete"}) { + final String name = inclusion.replace('.', '_').replace(',', '_'); + final TCreatePipeReq allowed = createRequest(name, Map.of("inclusion", inclusion)); + Assert.assertTrue(pipeTaskInfo.checkBeforeCreatePipe(allowed)); + store(allowed); + pipeTaskInfo.checkBeforeStartPipe(name); + pipeTaskInfo.checkBeforeStartPipe(name, false); + } + // Persisted metadata can predate the restriction and must be checked on START as well. + store(insertion); + assertDisabled(() -> pipeTaskInfo.checkBeforeStartPipe("insertion")); + assertDisabled(() -> pipeTaskInfo.checkBeforeStartPipe("insertion", false)); + ConfigNodeDescriptor.getInstance() + .getConf() + .setDataRegionConsensusProtocolClass(ConsensusFactory.IOT_CONSENSUS_V2); + final String consensus = PipeStaticMeta.CONSENSUS_PIPE_PREFIX + "DataRegion[7]_1_2"; + final TCreatePipeReq internal = createRequest(consensus, Map.of()); + Assert.assertTrue(pipeTaskInfo.checkBeforeCreatePipe(internal)); + store(internal); + pipeTaskInfo.checkBeforeStartPipe(consensus); + } + + @Test + public void testAlterRevalidatesAgainstLatestSerializedMetadata() { + final TCreatePipeReq initial = createRequest("p", Map.of("inclusion", "schema")); + store(initial); + final Map firstAttributes = + Map.of("source.inclusion", "all", "source.inclusion.exclusion", "data.insert"); + final Map secondAttributes = Map.of("extractor.inclusion.exclusion", ""); + // Both partial updates are allowed against the original RPC-time snapshot. + Assert.assertTrue(PipeConfigNodeAgent.isPipeDataInsertionEnabled(firstAttributes)); + Assert.assertTrue( + PipeConfigNodeAgent.isPipeDataInsertionEnabled( + Map.of("inclusion", "schema", "inclusion.exclusion", ""))); + final TAlterPipeReq first = alterRequest(firstAttributes, false); + pipeTaskInfo.checkAndUpdateRequestBeforeAlterPipe(first); + final PipeStaticMeta oldMeta = pipeTaskInfo.getPipeMetaByPipeName("p").getStaticMeta(); + final PipeStaticMeta newMeta = + new PipeStaticMeta( + "p", 2L, first.getExtractorAttributes(), new HashMap<>(), new HashMap<>()); + pipeTaskInfo.alterPipe(new AlterPipePlanV2(oldMeta, newMeta, new PipeRuntimeMeta())); + assertDisabled( + () -> + pipeTaskInfo.checkAndUpdateRequestBeforeAlterPipe( + alterRequest(secondAttributes, false))); + Assert.assertTrue( + PipeConfigNodeAgent.isPipeDataInsertionEnabled( + pipeTaskInfo.getPipeMetaByPipeName("p").getStaticMeta().getSourceParameters())); + assertDisabled( + () -> pipeTaskInfo.checkAndUpdateRequestBeforeAlterPipe(alterRequest(Map.of(), true))); + pipeTaskInfo.checkAndUpdateRequestBeforeAlterPipe( + alterRequest(Map.of("inclusion", "schema,data.delete"), true)); + } + + private TCreatePipeReq createRequest(String name, Map source) { + return new TCreatePipeReq() + .setPipeName(name) + .setExtractorAttributes(new HashMap<>(source)) + .setProcessorAttributes(new HashMap<>()) + .setConnectorAttributes(new HashMap<>()); + } + + private TAlterPipeReq alterRequest(Map source, boolean replace) { + return new TAlterPipeReq("p", new HashMap<>(), new HashMap<>(), false, false) + .setExtractorAttributes(new HashMap<>(source)) + .setIsReplaceAllExtractorAttributes(replace) + .setIsTableModel(false); + } + + private void store(TCreatePipeReq request) { + pipeTaskInfo.createPipe( + new CreatePipePlanV2( + new PipeStaticMeta( + request.getPipeName(), + 1L, + request.getExtractorAttributes(), + request.getProcessorAttributes(), + request.getConnectorAttributes()), + new PipeRuntimeMeta())); + } + + private void assertDisabled(Runnable action) { + Assert.assertEquals( + PipeMessages + .MESSAGE_PIPE_DATA_INSERTION_IS_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_7D669ECC, + Assert.assertThrows(PipeException.class, action::run).getMessage()); + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/PipeDataNodeAgent.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/PipeDataNodeAgent.java index cd0fc98109179..838a41e85a1b5 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/PipeDataNodeAgent.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/PipeDataNodeAgent.java @@ -20,7 +20,6 @@ package org.apache.iotdb.db.pipe.agent; import org.apache.iotdb.commons.exception.IllegalPathException; -import org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant; import org.apache.iotdb.commons.pipe.datastructure.options.PipeInclusionOptions; import org.apache.iotdb.consensus.ConsensusFactory; import org.apache.iotdb.db.conf.IoTDBDescriptor; @@ -56,8 +55,7 @@ public static boolean isPipeDataInsertionEnabled(final PipeParameters sourcePara return true; } try { - return !PipeInclusionOptions.areOptionsEnabled( - sourceParameters, PipeSourceConstant.EXTRACTOR_INCLUSION_DEFAULT_VALUE); + return !PipeInclusionOptions.isDataInsertionIncluded(sourceParameters); } catch (final IllegalPathException e) { // Let the normal pipe parameter validation report malformed inclusion options. return true; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgent.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgent.java index 565a62c4996e6..85dc2d9cf4235 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgent.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgent.java @@ -270,6 +270,7 @@ private void restoreCompletedEmptySnapshot(final PipeMeta pipeMeta) { final PipeStaticMeta staticMeta = pipeMeta.getStaticMeta(); if (isShutdown() || !SubscriptionConfig.getInstance().getSubscriptionEnabled() + || !PipeDataNodeAgent.isSubscriptionFeatureEnabled() || pipeMeta.getRuntimeMeta().getStatus().get() != PipeStatus.RUNNING || !PipeStaticMeta.isSubscriptionPipe(staticMeta.getPipeName()) || !staticMeta diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/legacy/IoTDBLegacyPipeReceiverAgent.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/legacy/IoTDBLegacyPipeReceiverAgent.java index 3bad4b45398f3..dd670bdfa9921 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/legacy/IoTDBLegacyPipeReceiverAgent.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/legacy/IoTDBLegacyPipeReceiverAgent.java @@ -28,6 +28,7 @@ import org.apache.iotdb.commons.utils.FileUtils; import org.apache.iotdb.db.i18n.DataNodeMiscMessages; import org.apache.iotdb.db.i18n.DataNodePipeMessages; +import org.apache.iotdb.db.pipe.agent.PipeDataNodeAgent; import org.apache.iotdb.db.pipe.sink.payload.legacy.PipeData; import org.apache.iotdb.db.pipe.sink.payload.legacy.TsFilePipeData; import org.apache.iotdb.db.protocol.session.SessionManager; @@ -116,6 +117,13 @@ public TSStatus handshake( return RpcUtils.getStatus(TSStatusCode.SUCCESS_STATUS, ""); } + private TSStatus pipeDataInsertionDisabledStatus() { + return RpcUtils.getStatus( + TSStatusCode.UNSUPPORTED_OPERATION, + PipeMessages + .MESSAGE_PIPE_DATA_INSERTION_IS_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_7D669ECC); + } + private boolean validatePipeName(final TSyncIdentityInfo info) { return info.isSetPipeName() && Objects.isNull(FileUtils.getIllegalError4Directory(info.getPipeName())); @@ -159,6 +167,9 @@ public TSStatus transportPipeData(final ByteBuffer buff, final SessionInfo sessi buff.get(byteArray); pipeData = PipeData.createPipeData(byteArray); if (pipeData instanceof TsFilePipeData) { + if (PipeDataNodeAgent.isRatisConsensus()) { + return pipeDataInsertionDisabledStatus(); + } TsFilePipeData tsFilePipeData = (TsFilePipeData) pipeData; tsFilePipeData.setDatabase(identityInfo.getDatabase()); handleTsFilePipeData(tsFilePipeData, fileDir); @@ -265,6 +276,9 @@ public TSStatus transportFile(final TSyncTransportMetaInfo metaInfo, final ByteB if (identityInfo == null) { throw new TException(DataNodePipeMessages.THRIFT_CONNECTION_IS_NOT_ALIVE); } + if (PipeDataNodeAgent.isRatisConsensus()) { + return pipeDataInsertionDisabledStatus(); + } LOGGER.debug( DataNodePipeMessages.INVOKE_TRANSPORTDATA_METHOD_FROM_CLIENT_IP, identityInfo.getRemoteAddress()); diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgentTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgentTest.java index c1368441148e2..50c75e05c6d36 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgentTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgentTest.java @@ -33,10 +33,15 @@ import org.apache.iotdb.commons.pipe.agent.task.meta.PipeStaticMeta; import org.apache.iotdb.commons.pipe.agent.task.meta.PipeStatus; import org.apache.iotdb.commons.pipe.agent.task.meta.PipeTaskMeta; +import org.apache.iotdb.commons.pipe.config.constant.PipeSinkConstant; import org.apache.iotdb.commons.pipe.config.constant.PipeSourceConstant; +import org.apache.iotdb.commons.pipe.config.constant.SystemConstant; import org.apache.iotdb.consensus.ConsensusFactory; import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.pipe.agent.PipeDataNodeAgent; +import org.apache.iotdb.db.pipe.source.dataregion.DataRegionListeningFilter; +import org.apache.iotdb.db.subscription.agent.SubscriptionAgent; +import org.apache.iotdb.pipe.api.customizer.parameter.PipeParameters; import org.apache.iotdb.pipe.api.exception.PipeException; import org.awaitility.Awaitility; @@ -46,6 +51,7 @@ import java.lang.reflect.Field; import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -57,7 +63,7 @@ public class PipeDataNodeTaskAgentTest { private static final int REGION_ID = 7; @Test - public void testRatisDetectsPipeDataInsertionOption() { + public void testRatisDetectsPipeDataInsertionOption() throws Exception { final String originalConsensus = IoTDBDescriptor.getInstance().getConfig().getDataRegionConsensusProtocolClass(); try { @@ -77,6 +83,20 @@ public void testRatisDetectsPipeDataInsertionOption() { Assert.assertFalse( PipeDataNodeAgent.isPipeDataInsertionEnabled( Map.of(PipeSourceConstant.EXTRACTOR_INCLUSION_KEY, "data.insert,data.delete"))); + for (final Map attributes : + List.of( + Map.of(), + Map.of("inclusion", "schema"), + Map.of("inclusion", "data.delete"), + Map.of("source.inclusion", "data.insert.extra"), + Map.of("inclusion", "all", "inclusion.exclusion", "data.insert.extra"))) { + Assert.assertEquals( + attributes.toString(), + !DataRegionListeningFilter.parseInsertionDeletionListeningOptionPair( + new PipeParameters(attributes)) + .getLeft(), + PipeDataNodeAgent.isPipeDataInsertionEnabled(attributes)); + } } finally { IoTDBDescriptor.getInstance() .getConfig() @@ -84,6 +104,52 @@ public void testRatisDetectsPipeDataInsertionOption() { } } + @Test + public void testRatisDoesNotRestoreCompletedSubscriptionBroker() { + final String originalConsensus = + IoTDBDescriptor.getInstance().getConfig().getDataRegionConsensusProtocolClass(); + final String group = "ratis-completed-snapshot"; + final PipeDataNodeTaskAgent taskAgent = + new PipeDataNodeTaskAgent() { + @Override + protected boolean isShutdown() { + return false; + } + }; + try { + IoTDBDescriptor.getInstance() + .getConfig() + .setDataRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS); + Assert.assertFalse(SubscriptionAgent.broker().isBrokerExist(group)); + final PipeMeta meta = + new PipeMeta( + new PipeStaticMeta( + PipeStaticMeta.SUBSCRIPTION_PIPE_PREFIX + "topic." + group, + 1L, + new HashMap<>( + Map.of(SystemConstant.SUBSCRIPTION_EMPTY_SNAPSHOT_COMPLETED_KEY, "true")), + new HashMap<>(), + new HashMap<>( + Map.of( + PipeSinkConstant.SINK_CONSUMER_GROUP_KEY, + group, + PipeSinkConstant.SINK_TOPIC_KEY, + "topic"))), + new PipeRuntimeMeta()); + meta.getRuntimeMeta().getStatus().set(PipeStatus.RUNNING); + + Assert.assertNull(taskAgent.handleSinglePipeMetaChanges(meta)); + Assert.assertFalse(SubscriptionAgent.broker().isBrokerExist(group)); + Assert.assertTrue(taskAgent.handlePipeMetaChanges(Collections.singletonList(meta)).isEmpty()); + Assert.assertFalse(SubscriptionAgent.broker().isBrokerExist(group)); + } finally { + SubscriptionAgent.broker().dropBroker(group); + IoTDBDescriptor.getInstance() + .getConfig() + .setDataRegionConsensusProtocolClass(originalConsensus); + } + } + @Test public void testGetPipeTaskProgressIndexReportsMissingTaskMeta() throws Exception { final PipeDataNodeTaskAgent taskAgent = new PipeDataNodeTaskAgent(); diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/receiver/protocol/legacy/IoTDBLegacyPipeReceiverAgentTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/receiver/protocol/legacy/IoTDBLegacyPipeReceiverAgentTest.java index 111350d760735..d3f661f9e9fa9 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/receiver/protocol/legacy/IoTDBLegacyPipeReceiverAgentTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/receiver/protocol/legacy/IoTDBLegacyPipeReceiverAgentTest.java @@ -20,10 +20,16 @@ package org.apache.iotdb.db.pipe.receiver.protocol.legacy; import org.apache.iotdb.common.rpc.thrift.TSStatus; +import org.apache.iotdb.commons.cluster.NodeStatus; import org.apache.iotdb.commons.conf.CommonDescriptor; import org.apache.iotdb.commons.i18n.PipeMessages; +import org.apache.iotdb.commons.path.MeasurementPath; +import org.apache.iotdb.commons.queryengine.common.SessionInfo; +import org.apache.iotdb.consensus.ConsensusFactory; +import org.apache.iotdb.db.conf.IoTDBDescriptor; import org.apache.iotdb.db.i18n.DataNodePipeMessages; import org.apache.iotdb.db.pipe.sink.payload.legacy.TsFilePipeData; +import org.apache.iotdb.db.storageengine.dataregion.modification.v1.Deletion; import org.apache.iotdb.rpc.TSStatusCode; import org.apache.iotdb.service.rpc.thrift.TSyncIdentityInfo; import org.apache.iotdb.service.rpc.thrift.TSyncTransportMetaInfo; @@ -31,17 +37,21 @@ import ch.qos.logback.classic.Logger; import ch.qos.logback.classic.spi.ILoggingEvent; import ch.qos.logback.core.read.ListAppender; +import org.apache.tsfile.utils.ReadWriteIOUtils; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.slf4j.LoggerFactory; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; import java.io.File; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.time.ZoneId; public class IoTDBLegacyPipeReceiverAgentTest { @@ -80,6 +90,68 @@ public void tearDown() throws Exception { } } + @Test + public void testRatisRejectsFileTransfersButAllowsDeletion() throws Exception { + final String originalConsensus = + IoTDBDescriptor.getInstance().getConfig().getDataRegionConsensusProtocolClass(); + final NodeStatus originalStatus = CommonDescriptor.getInstance().getConfig().getNodeStatus(); + final String fileName = "ratis.tsfile"; + try { + IoTDBDescriptor.getInstance() + .getConfig() + .setDataRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS); + Assert.assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + agent + .handshake( + new TSyncIdentityInfo(PIPE_NAME, CREATE_TIME, "UNKNOWN", ""), + REMOTE_ADDRESS, + null, + null) + .getCode()); + final ByteBuffer bytes = ByteBuffer.wrap(new byte[] {1, 2, 3}); + Assert.assertEquals( + TSStatusCode.UNSUPPORTED_OPERATION.getStatusCode(), + agent.transportFile(new TSyncTransportMetaInfo(fileName, 0), bytes).getCode()); + Assert.assertEquals(0, bytes.position()); + Assert.assertFalse(Files.exists(getFileDataDir().resolve(fileName + ".patch"))); + + final Path patch = getFileDataDir().resolve(fileName + ".patch"); + Files.write(patch, new byte[] {4, 5, 6}); + Assert.assertEquals( + TSStatusCode.UNSUPPORTED_OPERATION.getStatusCode(), + agent + .transportPipeData(ByteBuffer.wrap(new TsFilePipeData("", fileName, 1).serialize())) + .getCode()); + Assert.assertTrue(Files.exists(patch)); + Assert.assertFalse(Files.exists(getFileDataDir().resolve(fileName))); + + final ByteArrayOutputStream deletionBytes = new ByteArrayOutputStream(); + try (final DataOutputStream output = new DataOutputStream(deletionBytes)) { + output.writeByte(1); + output.writeLong(2L); + ReadWriteIOUtils.write("root.sg", output); + new Deletion(new MeasurementPath("root.sg.d.s"), 0, 0, 1) + .serializeWithoutFileOffset(output); + } + // The real deletion loader must still be reached under Ratis. Read-only mode gives it a + // deterministic error without requiring a running cluster or executing an actual deletion. + CommonDescriptor.getInstance().getConfig().setNodeStatus(NodeStatus.ReadOnly); + final TSStatus deletionStatus = + agent.transportPipeData( + ByteBuffer.wrap(deletionBytes.toByteArray()), + new SessionInfo(1L, "root", ZoneId.systemDefault())); + Assert.assertEquals(TSStatusCode.PIPESERVER_ERROR.getStatusCode(), deletionStatus.getCode()); + Assert.assertTrue( + deletionStatus.getMessage().contains(DataNodePipeMessages.STORAGE_ENGINE_READONLY)); + } finally { + CommonDescriptor.getInstance().getConfig().setNodeStatus(originalStatus); + IoTDBDescriptor.getInstance() + .getConfig() + .setDataRegionConsensusProtocolClass(originalConsensus); + } + } + @Test public void testTransportFileRejectsPathTraversal() throws Exception { final String traversal = diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/datastructure/options/PipeInclusionOptions.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/datastructure/options/PipeInclusionOptions.java index 3bde97c1a12ae..7f3dae5158361 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/datastructure/options/PipeInclusionOptions.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/pipe/datastructure/options/PipeInclusionOptions.java @@ -257,6 +257,17 @@ public static Set parseOptions(final String optionsString) return options; } + public static boolean isDataInsertionIncluded(final PipeParameters parameters) + throws IllegalPathException { + // Match the data-region listening filter, including prefixes longer than the concrete option + // that the existing option validator accepts. + final PartialPath insertion = new PartialPath(EXTRACTOR_INCLUSION_DEFAULT_VALUE); + return parseOptions(getInclusionString(parameters)).stream() + .anyMatch(insertion::overlapWithFullPathPrefix) + && parseOptions(getExclusionString(parameters)).stream() + .noneMatch(insertion::overlapWithFullPathPrefix); + } + public static boolean areOptionsEnabled(final PipeParameters parameters, final String... options) throws IllegalPathException { final Set inclusionOptions = parseOptions(getInclusionString(parameters)); diff --git a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/pipe/datastructure/options/PipeInclusionOptionsTest.java b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/pipe/datastructure/options/PipeInclusionOptionsTest.java index a2d7d5fab615d..7251a3980507f 100644 --- a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/pipe/datastructure/options/PipeInclusionOptionsTest.java +++ b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/pipe/datastructure/options/PipeInclusionOptionsTest.java @@ -72,4 +72,18 @@ public void testDataInsertionOptionSelection() throws Exception { Assert.assertFalse( PipeInclusionOptions.areOptionsEnabled(new PipeParameters(attributes), "data.insert")); } + + @Test + public void testDataInsertionUsesListeningPrefixSemantics() throws Exception { + Assert.assertTrue(PipeInclusionOptions.optionsAreAllLegal("data.insert.extra", true, true)); + Assert.assertTrue( + PipeInclusionOptions.isDataInsertionIncluded( + new PipeParameters(Map.of("source.inclusion", "data.insert.extra")))); + Assert.assertFalse( + PipeInclusionOptions.isDataInsertionIncluded( + new PipeParameters( + Map.of( + "source.inclusion", "all", + "source.inclusion.exclusion", "data.insert.extra")))); + } } From f6dedbdddf584d1be959bd3a38bdb1f262c93191 Mon Sep 17 00:00:00 2001 From: Caideyipi <87789683+Caideyipi@users.noreply.github.com> Date: Fri, 30 Oct 2026 15:08:56 +0800 Subject: [PATCH 7/9] Fix Pipe coordinator drop races and Ratis PlanNode admission --- .../coordinator/task/PipeTaskCoordinator.java | 8 +- .../task/PipeTaskCoordinatorTest.java | 157 ++++++++++++++ .../thrift/IoTDBDataNodeReceiver.java | 18 +- .../thrift/IoTDBDataNodeReceiverTest.java | 202 ++++++++++++++++++ 4 files changed, 376 insertions(+), 9 deletions(-) create mode 100644 iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/pipe/coordinator/task/PipeTaskCoordinatorTest.java diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/task/PipeTaskCoordinator.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/task/PipeTaskCoordinator.java index a2d4ff2234a04..992f0997f1d7f 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/task/PipeTaskCoordinator.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/task/PipeTaskCoordinator.java @@ -242,7 +242,8 @@ public TSStatus alterPipe(TAlterPipeReq req) { .EXCEPTION_FAILED_TO_ALTER_PIPE_ARG_THE_PIPE_IS_BEING_DROPPED_919F1E2B, pipeName)); } - if (!pipeTaskInfo.isPipeExisted(pipeName, isTableModel)) { + final PipeMeta currentPipeMeta = pipeTaskInfo.getPipeMetaByPipeName(pipeName, isTableModel); + if (currentPipeMeta == null) { return isSetIfExistsCondition ? RpcUtils.getStatus(TSStatusCode.SUCCESS_STATUS) : RpcUtils.getStatus( @@ -250,7 +251,6 @@ public TSStatus alterPipe(TAlterPipeReq req) { String.format( "Failed to alter pipe %s. Failures: %s does not exist.", pipeName, pipeName)); } - final PipeMeta currentPipeMeta = pipeTaskInfo.getPipeMetaByPipeName(pipeName, isTableModel); if (!pipeName.startsWith(PipeStaticMeta.CONSENSUS_PIPE_PREFIX) && !PipeConfigNodeAgent.isPipeDataInsertionEnabled( getUpdatedSourceParameters(req, currentPipeMeta))) { @@ -295,13 +295,13 @@ public TSStatus startPipe(TStartPipeReq req) { .EXCEPTION_FAILED_TO_START_PIPE_ARG_THE_PIPE_IS_BEING_DROPPED_B41F4638, pipeName)); } - if (!pipeTaskInfo.isPipeExisted(pipeName, isTableModel)) { + final PipeMeta pipeMeta = pipeTaskInfo.getPipeMetaByPipeName(pipeName, isTableModel); + if (pipeMeta == null) { return RpcUtils.getStatus( TSStatusCode.PIPE_NOT_EXIST_ERROR, String.format( "Failed to start pipe %s. Failures: %s does not exist.", pipeName, pipeName)); } - final PipeMeta pipeMeta = pipeTaskInfo.getPipeMetaByPipeName(pipeName, isTableModel); if (!pipeName.startsWith(PipeStaticMeta.CONSENSUS_PIPE_PREFIX) && !PipeConfigNodeAgent.isPipeDataInsertionEnabled( pipeMeta.getStaticMeta().getSourceParameters())) { diff --git a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/pipe/coordinator/task/PipeTaskCoordinatorTest.java b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/pipe/coordinator/task/PipeTaskCoordinatorTest.java new file mode 100644 index 0000000000000..bbe1706c57f07 --- /dev/null +++ b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/pipe/coordinator/task/PipeTaskCoordinatorTest.java @@ -0,0 +1,157 @@ +/* + * 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.iotdb.confignode.manager.pipe.coordinator.task; + +import org.apache.iotdb.commons.pipe.agent.task.meta.PipeMeta; +import org.apache.iotdb.commons.pipe.agent.task.meta.PipeRuntimeMeta; +import org.apache.iotdb.commons.pipe.agent.task.meta.PipeStaticMeta; +import org.apache.iotdb.commons.pipe.config.constant.SystemConstant; +import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor; +import org.apache.iotdb.confignode.consensus.request.write.pipe.task.CreatePipePlanV2; +import org.apache.iotdb.confignode.consensus.request.write.pipe.task.DropPipePlanV2; +import org.apache.iotdb.confignode.manager.ConfigManager; +import org.apache.iotdb.confignode.persistence.pipe.PipeTaskInfo; +import org.apache.iotdb.confignode.rpc.thrift.TAlterPipeReq; +import org.apache.iotdb.confignode.rpc.thrift.TStartPipeReq; +import org.apache.iotdb.consensus.ConsensusFactory; +import org.apache.iotdb.rpc.TSStatusCode; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +@RunWith(Parameterized.class) +public class PipeTaskCoordinatorTest { + @Parameterized.Parameters(name = "{0}, tableModel={1}") + public static Collection parameters() { + final Collection cases = new ArrayList<>(); + for (String protocol : + new String[] { + ConsensusFactory.RATIS_CONSENSUS, + ConsensusFactory.IOT_CONSENSUS, + ConsensusFactory.IOT_CONSENSUS_V2, + ConsensusFactory.SIMPLE_CONSENSUS + }) { + cases.add(new Object[] {protocol, false}); + cases.add(new Object[] {protocol, true}); + } + return cases; + } + + private final String protocol; + private final boolean isTableModel; + private String originalProtocol; + private final ConfigManager configManager = mock(ConfigManager.class); + private final PipeTaskInfo pipeTaskInfo = + new PipeTaskInfo() { + @Override + public PipeMeta getPipeMetaByPipeName(String name, boolean tableModel) { + // Schedule a real metadata removal just before lookup, after the RPC's earlier checks. + Assert.assertTrue(isPipeExisted(name, tableModel)); + Assert.assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + dropPipe(new DropPipePlanV2(name, tableModel)).getCode()); + return super.getPipeMetaByPipeName(name, tableModel); + } + }; + private final PipeTaskCoordinator coordinator = + new PipeTaskCoordinator(configManager, pipeTaskInfo); + + public PipeTaskCoordinatorTest(String protocol, boolean isTableModel) { + this.protocol = protocol; + this.isTableModel = isTableModel; + } + + @Before + public void setUp() { + originalProtocol = + ConfigNodeDescriptor.getInstance().getConf().getDataRegionConsensusProtocolClass(); + ConfigNodeDescriptor.getInstance().getConf().setDataRegionConsensusProtocolClass(protocol); + final Map source = + new HashMap<>( + Map.of( + "inclusion", + "data.insert", + SystemConstant.SQL_DIALECT_KEY, + isTableModel + ? SystemConstant.SQL_DIALECT_TABLE_VALUE + : SystemConstant.SQL_DIALECT_TREE_VALUE)); + pipeTaskInfo.createPipe( + new CreatePipePlanV2( + new PipeStaticMeta("p", 1L, source, new HashMap<>(), new HashMap<>()), + new PipeRuntimeMeta())); + Assert.assertTrue(pipeTaskInfo.isPipeExisted("p", isTableModel)); + } + + @After + public void tearDown() { + ConfigNodeDescriptor.getInstance() + .getConf() + .setDataRegionConsensusProtocolClass(originalProtocol); + } + + @Test + public void testAlterHandlesConcurrentDrop() { + Assert.assertEquals( + TSStatusCode.PIPE_NOT_EXIST_ERROR.getStatusCode(), + coordinator.alterPipe(alterRequest(false)).getCode()); + verifyNoMoreInteractions(configManager); + Assert.assertFalse(pipeTaskInfo.isPipeExisted("p", isTableModel)); + } + + @Test + public void testAlterIfExistsHandlesConcurrentDrop() { + Assert.assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + coordinator.alterPipe(alterRequest(true)).getCode()); + verifyNoMoreInteractions(configManager); + Assert.assertFalse(pipeTaskInfo.isPipeExisted("p", isTableModel)); + } + + @Test + public void testStartHandlesConcurrentDrop() { + Assert.assertEquals( + TSStatusCode.PIPE_NOT_EXIST_ERROR.getStatusCode(), + coordinator + .startPipe(new TStartPipeReq().setPipeName("p").setIsTableModel(isTableModel)) + .getCode()); + verifyNoMoreInteractions(configManager); + Assert.assertFalse(pipeTaskInfo.isPipeExisted("p", isTableModel)); + } + + private TAlterPipeReq alterRequest(boolean ifExists) { + return new TAlterPipeReq("p", new HashMap<>(), new HashMap<>(), false, false) + .setExtractorAttributes(new HashMap<>(Map.of("inclusion", "schema"))) + .setIsReplaceAllExtractorAttributes(false) + .setIfExistsCondition(ifExists) + .setIsTableModel(isTableModel); + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java index 97c8ca490fb99..6c1034a238692 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java @@ -100,6 +100,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.CreateDBTask; import org.apache.iotdb.db.queryengine.plan.planner.LocalExecutionPlanner; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.metadata.write.view.AlterLogicalViewNode; +import org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertNode; import org.apache.iotdb.db.queryengine.plan.relational.security.TreeAccessCheckContext; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.PipeEnriched; import org.apache.iotdb.db.queryengine.plan.relational.sql.parser.SqlParser; @@ -240,11 +241,7 @@ public synchronized TPipeTransferResp receive(final TPipeTransferReq req) { return authResp; } if (PipeDataNodeAgent.isRatisConsensus() && isDataInsertionRequest(requestType)) { - return new TPipeTransferResp( - RpcUtils.getStatus( - TSStatusCode.UNSUPPORTED_OPERATION, - PipeMessages - .MESSAGE_PIPE_DATA_INSERTION_IS_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_7D669ECC)); + return pipeDataInsertionDisabledResponse(); } switch (requestType) { case HANDSHAKE_DATANODE_V1: @@ -541,6 +538,14 @@ private TPipeTransferResp handleTransferTabletInsertNode( : executeStatementAndAddRedirectInfo(statement)); } + private static TPipeTransferResp pipeDataInsertionDisabledResponse() { + return new TPipeTransferResp( + RpcUtils.getStatus( + TSStatusCode.UNSUPPORTED_OPERATION, + PipeMessages + .MESSAGE_PIPE_DATA_INSERTION_IS_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_7D669ECC)); + } + private static boolean isDataInsertionRequest(final PipeRequestType requestType) { switch (requestType) { case TRANSFER_TABLET_INSERT_NODE: @@ -1054,6 +1059,9 @@ private TSStatus createSchemaSnapshotDatabaseIfNecessary(final PartialPath datab } private TPipeTransferResp handleTransferSchemaPlan(final PipeTransferPlanNodeReq req) { + if (PipeDataNodeAgent.isRatisConsensus() && req.getPlanNode() instanceof InsertNode) { + return pipeDataInsertionDisabledResponse(); + } // We may be able to skip the alter logical view's exception parsing because // the "AlterLogicalViewNode" is itself idempotent if (req.getPlanNode() instanceof AlterLogicalViewNode) { diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiverTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiverTest.java index b71542a0cc0d1..4f7d933385ee3 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiverTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiverTest.java @@ -21,11 +21,28 @@ import org.apache.iotdb.common.rpc.thrift.TEndPoint; import org.apache.iotdb.common.rpc.thrift.TSStatus; +import org.apache.iotdb.commons.path.MeasurementPath; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.pipe.receiver.runtime.PipeReceiverRuntimeRegistry; import org.apache.iotdb.commons.pipe.receiver.runtime.PipeReceiverRuntimeSnapshot; +import org.apache.iotdb.commons.pipe.sink.compressor.PipeCompressor; +import org.apache.iotdb.commons.pipe.sink.compressor.PipeCompressorFactory; +import org.apache.iotdb.commons.pipe.sink.payload.airgap.AirGapPseudoTPipeTransferRequest; +import org.apache.iotdb.commons.pipe.sink.payload.thrift.request.PipeTransferCompressedReq; +import org.apache.iotdb.commons.pipe.sink.payload.thrift.request.PipeTransferSliceReq; +import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNode; +import org.apache.iotdb.commons.queryengine.plan.planner.plan.node.PlanNodeId; +import org.apache.iotdb.commons.schema.table.column.TsTableColumnCategory; +import org.apache.iotdb.consensus.ConsensusFactory; import org.apache.iotdb.db.conf.IoTDBDescriptor; +import org.apache.iotdb.db.pipe.sink.payload.evolvable.request.PipeTransferPlanNodeReq; import org.apache.iotdb.db.pipe.sink.payload.evolvable.request.PipeTransferTsFileSealWithModReq; +import org.apache.iotdb.db.queryengine.plan.planner.plan.node.metadata.write.CreateTimeSeriesNode; +import org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.DeleteDataNode; +import org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertRowNode; +import org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertRowsNode; +import org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertTabletNode; +import org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.RelationalInsertTabletNode; import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertMultiTabletsStatement; import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowStatement; import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowsStatement; @@ -35,13 +52,18 @@ import org.apache.iotdb.db.storageengine.load.config.LoadTsFileConfigurator; import org.apache.iotdb.db.storageengine.load.converter.PipeTsFileConversionTaskManager; import org.apache.iotdb.rpc.TSStatusCode; +import org.apache.iotdb.service.rpc.thrift.TPipeTransferReq; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.file.metadata.enums.CompressionType; +import org.apache.tsfile.file.metadata.enums.TSEncoding; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import java.lang.reflect.Field; +import java.nio.ByteBuffer; import java.nio.file.Files; import java.nio.file.Path; import java.util.Arrays; @@ -64,6 +86,186 @@ public void tearDown() { registry.clear(); } + @Test + public void testRatisRejectsInsertionInPlanNodeRequests() throws Exception { + final String originalProtocol = + IoTDBDescriptor.getInstance().getConfig().getDataRegionConsensusProtocolClass(); + try { + IoTDBDescriptor.getInstance() + .getConfig() + .setDataRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS); + for (final PlanNode node : insertionPlans()) { + assertPlanRequestEnvelopes(node, true); + } + } finally { + IoTDBDescriptor.getInstance() + .getConfig() + .setDataRegionConsensusProtocolClass(originalProtocol); + } + } + + @Test + public void testRatisAllowsMetadataAndDeletionPlanNodeRequests() throws Exception { + final String originalProtocol = + IoTDBDescriptor.getInstance().getConfig().getDataRegionConsensusProtocolClass(); + try { + IoTDBDescriptor.getInstance() + .getConfig() + .setDataRegionConsensusProtocolClass(ConsensusFactory.RATIS_CONSENSUS); + assertPlanRequestEnvelopes( + new DeleteDataNode( + new PlanNodeId("delete"), + Collections.singletonList(new MeasurementPath("root.sg.d.s")), + 0, + 1), + false); + assertPlanRequestEnvelopes( + new CreateTimeSeriesNode( + new PlanNodeId("schema"), + new MeasurementPath("root.sg.d.s"), + TSDataType.INT32, + TSEncoding.PLAIN, + CompressionType.UNCOMPRESSED, + null, + null, + null, + null), + false); + } finally { + IoTDBDescriptor.getInstance() + .getConfig() + .setDataRegionConsensusProtocolClass(originalProtocol); + } + } + + @Test + public void testNonRatisAllowsInsertionPlanNodeRequests() throws Exception { + final String originalProtocol = + IoTDBDescriptor.getInstance().getConfig().getDataRegionConsensusProtocolClass(); + try { + for (final String protocol : + new String[] { + ConsensusFactory.IOT_CONSENSUS, + ConsensusFactory.IOT_CONSENSUS_V2, + ConsensusFactory.SIMPLE_CONSENSUS + }) { + IoTDBDescriptor.getInstance().getConfig().setDataRegionConsensusProtocolClass(protocol); + for (final PlanNode node : insertionPlans()) { + assertPlanRequestEnvelopes(node, false); + } + } + } finally { + IoTDBDescriptor.getInstance() + .getConfig() + .setDataRegionConsensusProtocolClass(originalProtocol); + } + } + + private List insertionPlans() throws Exception { + final InsertRowNode row = + new InsertRowNode( + new PlanNodeId("row"), + new PartialPath("root.sg.d"), + false, + new String[] {"s"}, + new TSDataType[] {TSDataType.INT32}, + 1L, + new Object[] {1}, + false); + return Arrays.asList( + row, + new InsertRowsNode( + new PlanNodeId("rows"), Collections.singletonList(0), Collections.singletonList(row)), + new InsertTabletNode( + new PlanNodeId("tablet"), + new PartialPath("root.sg.d"), + false, + new String[] {"s"}, + new TSDataType[] {TSDataType.INT32}, + new long[] {1}, + null, + new Object[] {new int[] {1}}, + 1), + new RelationalInsertTabletNode( + new PlanNodeId("table"), + new PartialPath("t"), + false, + new String[] {"s"}, + new TSDataType[] {TSDataType.INT32}, + new long[] {1}, + null, + new Object[] {new int[] {1}}, + 1, + new TsTableColumnCategory[] {TsTableColumnCategory.FIELD})); + } + + private void assertPlanRequestEnvelopes(final PlanNode node, final boolean rejected) + throws Exception { + final AdmissionTestReceiver receiver = new AdmissionTestReceiver(); + final int expectedCode = + rejected + ? TSStatusCode.UNSUPPORTED_OPERATION.getStatusCode() + : TSStatusCode.PIPE_HANDSHAKE_ERROR.getStatusCode(); + Assert.assertEquals( + expectedCode, + receiver.receive(PipeTransferPlanNodeReq.toTPipeTransferReq(node)).getStatus().getCode()); + + final TPipeTransferReq plain = PipeTransferPlanNodeReq.toTPipeTransferReq(node); + final TPipeTransferReq airGap = + new AirGapPseudoTPipeTransferRequest() + .setVersion(plain.getVersion()) + .setType(plain.getType()) + .setBody(plain.body.duplicate()); + Assert.assertEquals(expectedCode, receiver.receive(airGap).getStatus().getCode()); + Assert.assertEquals( + expectedCode, + receiver + .receive( + PipeTransferCompressedReq.toTPipeTransferReq( + PipeTransferPlanNodeReq.toTPipeTransferReq(node), + Collections.singletonList( + PipeCompressorFactory.getCompressor( + PipeCompressor.PipeCompressionType.GZIP.getIndex())))) + .getStatus() + .getCode()); + + final TPipeTransferReq sliced = PipeTransferPlanNodeReq.toTPipeTransferReq(node); + final ByteBuffer body = sliced.body.duplicate(); + final int midpoint = body.limit() / 2; + Assert.assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + receiver + .receive( + PipeTransferSliceReq.toTPipeTransferReq( + 1, sliced.getType(), 0, 2, body, 0, midpoint)) + .getStatus() + .getCode()); + Assert.assertEquals( + expectedCode, + receiver + .receive( + PipeTransferSliceReq.toTPipeTransferReq( + 1, sliced.getType(), 1, 2, body, midpoint, body.limit())) + .getStatus() + .getCode()); + Assert.assertEquals(rejected ? 0 : 4, receiver.executionAttempts); + } + + private static class AdmissionTestReceiver extends IoTDBDataNodeReceiver { + private int executionAttempts; + + private AdmissionTestReceiver() { + hasPipeHandshakeCredential = true; + } + + @Override + protected TSStatus loginIfNecessary() { + // Record arrival at statement execution without starting a cluster or writing data. + executionAttempts++; + return new TSStatus(TSStatusCode.PIPE_HANDSHAKE_ERROR.getStatusCode()); + } + } + @Test public void testTableModelTabletRedirectDoesNotCachePerRowLeader() { final InsertTabletStatement statement = new InsertTabletStatement(); From 7936b04cea75395dcf3e65cfcde89b7a7c04ec65 Mon Sep 17 00:00:00 2001 From: Caideyipi <87789683+Caideyipi@users.noreply.github.com> Date: Mon, 2 Nov 2026 10:26:00 +0800 Subject: [PATCH 8/9] Remove redundant consensus pipe exemptions from Ratis checks --- .../iotdb/confignode/manager/ConfigManager.java | 7 ++----- .../pipe/agent/task/PipeConfigNodeTaskAgent.java | 3 +-- .../pipe/coordinator/task/PipeTaskCoordinator.java | 13 +++++-------- .../confignode/persistence/pipe/PipeTaskInfo.java | 3 +-- .../agent/task/PipeConfigNodeTaskAgentTest.java | 7 ------- .../persistence/pipe/PipeTaskInfoRatisTest.java | 8 -------- .../db/pipe/agent/task/PipeDataNodeTaskAgent.java | 3 +-- 7 files changed, 10 insertions(+), 34 deletions(-) diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java index a19b5e2a9eada..d34749cce0e2e 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java @@ -57,7 +57,6 @@ import org.apache.iotdb.commons.path.PathDeserializeUtil; import org.apache.iotdb.commons.path.PathPatternTree; import org.apache.iotdb.commons.path.PathPatternUtil; -import org.apache.iotdb.commons.pipe.agent.task.meta.PipeStaticMeta; import org.apache.iotdb.commons.pipe.sink.payload.airgap.AirGapPseudoTPipeTransferRequest; import org.apache.iotdb.commons.schema.SchemaConstant; import org.apache.iotdb.commons.schema.table.AlterOrDropTableOperationType; @@ -1784,10 +1783,9 @@ private TSStatus confirmSubscriptionFeatureAvailability() { } private TSStatus confirmPipeDataInsertionAvailability( - final String pipeName, final Map sourceAttributes) { + final Map sourceAttributes) { final TSStatus status = confirmLeader(); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode() - || pipeName.startsWith(PipeStaticMeta.CONSENSUS_PIPE_PREFIX) || PipeConfigNodeAgent.isPipeDataInsertionEnabled(sourceAttributes)) { return status; } @@ -2600,8 +2598,7 @@ public TSStatus alterTimeSeriesDataType(final TAlterTimeSeriesReq req) { @Override public TSStatus createPipe(TCreatePipeReq req) { - TSStatus status = - confirmPipeDataInsertionAvailability(req.getPipeName(), req.getExtractorAttributes()); + TSStatus status = confirmPipeDataInsertionAvailability(req.getExtractorAttributes()); return status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode() ? pipeManager.getPipeTaskCoordinator().createPipe(req) : status; diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgent.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgent.java index adb9391513b3d..f640507fead41 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgent.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgent.java @@ -70,8 +70,7 @@ protected boolean isShutdown() { @Override protected boolean isPipeDataInsertionEnabled(final PipeStaticMeta pipeStaticMeta) { - return pipeStaticMeta.getPipeName().startsWith(PipeStaticMeta.CONSENSUS_PIPE_PREFIX) - || PipeConfigNodeAgent.isPipeDataInsertionEnabled(pipeStaticMeta.getSourceParameters()); + return PipeConfigNodeAgent.isPipeDataInsertionEnabled(pipeStaticMeta.getSourceParameters()); } @Override diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/task/PipeTaskCoordinator.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/task/PipeTaskCoordinator.java index 992f0997f1d7f..e173364fbb5a4 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/task/PipeTaskCoordinator.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/coordinator/task/PipeTaskCoordinator.java @@ -108,8 +108,7 @@ public boolean isLocked() { /** Caller should ensure that the method is called in the lock {@link #lock()}. */ public TSStatus createPipe(TCreatePipeReq req) { - if (!req.getPipeName().startsWith(PipeStaticMeta.CONSENSUS_PIPE_PREFIX) - && !PipeConfigNodeAgent.isPipeDataInsertionEnabled(req.getExtractorAttributes())) { + if (!PipeConfigNodeAgent.isPipeDataInsertionEnabled(req.getExtractorAttributes())) { return pipeDataInsertionDisabledStatus(); } @@ -251,9 +250,8 @@ public TSStatus alterPipe(TAlterPipeReq req) { String.format( "Failed to alter pipe %s. Failures: %s does not exist.", pipeName, pipeName)); } - if (!pipeName.startsWith(PipeStaticMeta.CONSENSUS_PIPE_PREFIX) - && !PipeConfigNodeAgent.isPipeDataInsertionEnabled( - getUpdatedSourceParameters(req, currentPipeMeta))) { + if (!PipeConfigNodeAgent.isPipeDataInsertionEnabled( + getUpdatedSourceParameters(req, currentPipeMeta))) { return pipeDataInsertionDisabledStatus(); } req.setIsTableModel(isTableModel); @@ -302,9 +300,8 @@ public TSStatus startPipe(TStartPipeReq req) { String.format( "Failed to start pipe %s. Failures: %s does not exist.", pipeName, pipeName)); } - if (!pipeName.startsWith(PipeStaticMeta.CONSENSUS_PIPE_PREFIX) - && !PipeConfigNodeAgent.isPipeDataInsertionEnabled( - pipeMeta.getStaticMeta().getSourceParameters())) { + if (!PipeConfigNodeAgent.isPipeDataInsertionEnabled( + pipeMeta.getStaticMeta().getSourceParameters())) { return pipeDataInsertionDisabledStatus(); } return startPipe(pipeName, isTableModel); diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/pipe/PipeTaskInfo.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/pipe/PipeTaskInfo.java index 7ac708c3ad52f..92bfb858d22b7 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/pipe/PipeTaskInfo.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/pipe/PipeTaskInfo.java @@ -432,8 +432,7 @@ private void checkBeforeStartPipeInternal(final String pipeName, final boolean i private void checkPipeDataInsertionAvailability(final PipeStaticMeta staticMeta) { // Procedures call these validators while holding the coordinator lock. Recheck the merged // source parameters here because concurrent ALTER requests can change the RPC-time snapshot. - if (!staticMeta.getPipeName().startsWith(PipeStaticMeta.CONSENSUS_PIPE_PREFIX) - && !PipeConfigNodeAgent.isPipeDataInsertionEnabled(staticMeta.getSourceParameters())) { + if (!PipeConfigNodeAgent.isPipeDataInsertionEnabled(staticMeta.getSourceParameters())) { throw new PipeException( PipeMessages .MESSAGE_PIPE_DATA_INSERTION_IS_DISABLED_WHEN_DATA_REGION_CONSENSUS_PROTOCOL_IS_RATIS_7D669ECC); diff --git a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgentTest.java b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgentTest.java index 738737d65c7d4..a6d7e0317dd71 100644 --- a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgentTest.java +++ b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/pipe/agent/task/PipeConfigNodeTaskAgentTest.java @@ -69,13 +69,6 @@ public void testRecoveryDropsDisallowedPipeAndKeepsMetadataPipes() { verify(agent.tasks.get(inclusion)).start(); Assert.assertTrue(agent.contains(inclusion)); } - ConfigNodeDescriptor.getInstance() - .getConf() - .setDataRegionConsensusProtocolClass(ConsensusFactory.IOT_CONSENSUS_V2); - final String internal = PipeStaticMeta.CONSENSUS_PIPE_PREFIX + "DataRegion[7]_1_2"; - Assert.assertNull(agent.handleSinglePipeMetaChanges(meta(internal, "data.insert"))); - verify(agent.tasks.get(internal)).start(); - Assert.assertTrue(agent.contains(internal)); } finally { agent.dropAllPipeTasks(); ConfigNodeDescriptor.getInstance() diff --git a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/pipe/PipeTaskInfoRatisTest.java b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/pipe/PipeTaskInfoRatisTest.java index 1a7050aa0228b..deb3640547304 100644 --- a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/pipe/PipeTaskInfoRatisTest.java +++ b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/pipe/PipeTaskInfoRatisTest.java @@ -79,14 +79,6 @@ public void testCreateAndStartValidateUnderProcedureLock() { store(insertion); assertDisabled(() -> pipeTaskInfo.checkBeforeStartPipe("insertion")); assertDisabled(() -> pipeTaskInfo.checkBeforeStartPipe("insertion", false)); - ConfigNodeDescriptor.getInstance() - .getConf() - .setDataRegionConsensusProtocolClass(ConsensusFactory.IOT_CONSENSUS_V2); - final String consensus = PipeStaticMeta.CONSENSUS_PIPE_PREFIX + "DataRegion[7]_1_2"; - final TCreatePipeReq internal = createRequest(consensus, Map.of()); - Assert.assertTrue(pipeTaskInfo.checkBeforeCreatePipe(internal)); - store(internal); - pipeTaskInfo.checkBeforeStartPipe(consensus); } @Test diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgent.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgent.java index 5ea4bfac856c8..fbf356c166763 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgent.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/agent/task/PipeDataNodeTaskAgent.java @@ -148,8 +148,7 @@ protected boolean isShutdown() { @Override protected boolean isPipeDataInsertionEnabled(final PipeStaticMeta pipeStaticMeta) { - return pipeStaticMeta.getPipeName().startsWith(PipeStaticMeta.CONSENSUS_PIPE_PREFIX) - || PipeDataNodeAgent.isPipeDataInsertionEnabled(pipeStaticMeta.getSourceParameters()); + return PipeDataNodeAgent.isPipeDataInsertionEnabled(pipeStaticMeta.getSourceParameters()); } @Override From 35f1f0cd86381fc3242ab5f5add948130fec4406 Mon Sep 17 00:00:00 2001 From: Caideyipi <87789683+Caideyipi@users.noreply.github.com> Date: Mon, 2 Nov 2026 18:17:34 +0800 Subject: [PATCH 9/9] Address receiver review feedback --- .../thrift/IoTDBDataNodeReceiver.java | 46 ++++++++++++------- .../agent/SubscriptionReceiverAgent.java | 12 +++-- 2 files changed, 36 insertions(+), 22 deletions(-) diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java index d247c3ec33437..57c5de46c7a99 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java @@ -594,23 +594,35 @@ private static TPipeTransferResp pipeDataInsertionDisabledResponse() { } private static boolean isDataInsertionRequest(final PipeRequestType requestType) { - switch (requestType) { - case TRANSFER_TABLET_INSERT_NODE: - case TRANSFER_TABLET_INSERT_NODE_V2: - case TRANSFER_TABLET_RAW: - case TRANSFER_TABLET_RAW_V2: - case TRANSFER_TABLET_BINARY: - case TRANSFER_TABLET_BINARY_V2: - case TRANSFER_TABLET_BATCH: - case TRANSFER_TABLET_BATCH_V2: - case TRANSFER_TS_FILE_PIECE: - case TRANSFER_TS_FILE_SEAL: - case TRANSFER_TS_FILE_PIECE_WITH_MOD: - case TRANSFER_TS_FILE_SEAL_WITH_MOD: - return true; - default: - return false; - } + return switch (requestType) { + case TRANSFER_TABLET_INSERT_NODE, + TRANSFER_TABLET_INSERT_NODE_V2, + TRANSFER_TABLET_RAW, + TRANSFER_TABLET_RAW_V2, + TRANSFER_TABLET_BINARY, + TRANSFER_TABLET_BINARY_V2, + TRANSFER_TABLET_BATCH, + TRANSFER_TABLET_BATCH_V2, + TRANSFER_TS_FILE_PIECE, + TRANSFER_TS_FILE_SEAL, + TRANSFER_TS_FILE_PIECE_WITH_MOD, + TRANSFER_TS_FILE_SEAL_WITH_MOD -> + true; + case HANDSHAKE_CONFIGNODE_V1, + HANDSHAKE_DATANODE_V1, + HANDSHAKE_CONFIGNODE_V2, + HANDSHAKE_DATANODE_V2, + TRANSFER_PLAN_NODE, + TRANSFER_SCHEMA_SNAPSHOT_PIECE, + TRANSFER_SCHEMA_SNAPSHOT_SEAL, + TRANSFER_CONFIG_PLAN, + TRANSFER_CONFIG_SNAPSHOT_PIECE, + TRANSFER_CONFIG_SNAPSHOT_SEAL, + TRANSFER_COMPRESSED, + TRANSFER_SLICE, + TRANSFER_PIPE_RECEIVER_RUNTIME_INFO_CLEANUP -> + false; + }; } private TPipeTransferResp handleTransferTabletBinary(final PipeTransferTabletBinaryReq req) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgent.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgent.java index f14ca01a8185a..828b188720458 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgent.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/subscription/agent/SubscriptionReceiverAgent.java @@ -101,11 +101,13 @@ public class SubscriptionReceiverAgent { SubscriptionReceiverAgent() { this( SubscriptionReceiverV1::new, - SubscriptionConfig.getInstance().getSubscriptionEnabled() - && PipeDataNodeAgent.isSubscriptionFeatureEnabled(), - () -> - SubscriptionConfig.getInstance().getSubscriptionEnabled() - && PipeDataNodeAgent.isSubscriptionFeatureEnabled()); + isSubscriptionEnabled(), + SubscriptionReceiverAgent::isSubscriptionEnabled); + } + + private static boolean isSubscriptionEnabled() { + return SubscriptionConfig.getInstance().getSubscriptionEnabled() + && PipeDataNodeAgent.isSubscriptionFeatureEnabled(); } SubscriptionReceiverAgent(