This is an automated email from the ASF dual-hosted git repository.

rong pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git


The following commit(s) were added to refs/heads/master by this push:
     new 98c4aa324ec Pipe: skip pipe CN heartbeat and sync when 
`PipeTaskCoordinatorLock` is held by another thread (#11509)
98c4aa324ec is described below

commit 98c4aa324ecc256a750da58227d9e870bf798421
Author: V_Galaxy <[email protected]>
AuthorDate: Thu Nov 9 20:25:55 2023 +0800

    Pipe: skip pipe CN heartbeat and sync when `PipeTaskCoordinatorLock` is 
held by another thread (#11509)
    
    When the DN processing logic is blocked during the create (start) pipe, due 
to the timeout and retry mechanism, the write locks of CN's 
`PipeTaskCoordinatorLock` and DN's `PipeMetaKeeper` will be held for a long 
period of time, causing the timed `heartbeat` logic and `sync` logic to make no 
progress at all during this period, which in turn leads to a large number of 
related procedures and RPCs piling up. This PR partially sacrifices the 
liveness of `heartbeat` and `sync` to avoid the  [...]
    
    ---
    
    Minimum Reproduction:
    
    ```sql
    INSERT INTO root.db.d(time, m) values (1, 1);
    
    create pipe a2b
    with connector (
        'connector'='iotdb-air-gap-connector',
        'connector.ip'='192.0.2.0',
        'connector.port'='12345',
        'connector.air-gap.handshake-timeout-ms'='400000'
    );
    ```
    
    Here, the IP is a reserved address, so the socket connect process in 
`iotdb-air-gap-connector` will continue to be blocked until timeout.
    
    The client can observe the error message `Msg: 1107: Procedure execution 
timed out.`. Meanwhile, CN will be unable to process additional pipe requests 
due to the accumulation of procedures and RPCs.
    
    ---
    
    Detailed Explanation for added skip logic:
    
    For the `heartbeat` operation, if it is found that 
`PipeTaskCoordinatorLock` is held by another thread before execution, according 
to the calling relationship, there are no more than the following situations:
    
    - create / start / stop / drop pipe, these procedures will call the 
`pushSinglePipeMeta` rpc of DN in the `OPERATE_ON_DATA_NODES` step 
(`org.apache.iotdb.confignode.procedure.env.ConfigNodeProcedureEnv#pushSinglePipeMetaToDataNodes/dropSinglePipeOnDataNodes`),
 these two steps will acquire the write lock of `PipeMetaKeeper` (DN) on the DN 
side. Notice that `heartbeat` will also call the `pipeHeartbeat` rpc of DN, and 
the read lock of `PipeMetaKeeper` will be acquired on the DN side. If [...]
    - parse heartbeat, indicating that the previous round of `heartbeat` has 
not been processed (impossible, guaranteed by `synchronized`?)
    - auto restart / handle successful restart, indicating that `sync` is being 
processed at this time, these two steps will acquire the write lock of 
`PipeMetaKeeper` (CN) (No effect)
    - `PipeMetaSyncProcedure` / `PipeHandleMetaChangeProcedure` / 
`PipeHandleLeaderChangeProcedure`, these procedures will call the 
`pushPipeMeta` rpc of DN, analysis of the same as above
    
    In this way, when performing the `heartbeat` operation, if it is found that 
`PipeTaskCoordinatorLock` is held by another thread, if the other thread is 
blocked, the `heartbeat` will **most likely** be blocked, so just skip.
    
    The same analysis can be performed for `sync` operation.
    
    ------
    
    * fix: skip heartbeat and sync when PipeTaskCoordinatorLock is held by 
another thread
    
    * fix: skip the sync logic ahead of time
---
 .../confignode/manager/pipe/runtime/PipeHeartbeatScheduler.java   | 6 ++++++
 .../iotdb/confignode/manager/pipe/runtime/PipeMetaSyncer.java     | 8 +++++++-
 .../iotdb/confignode/manager/pipe/task/PipeTaskCoordinator.java   | 7 ++++++-
 .../confignode/manager/pipe/task/PipeTaskCoordinatorLock.java     | 4 ++++
 4 files changed, 23 insertions(+), 2 deletions(-)

diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/runtime/PipeHeartbeatScheduler.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/runtime/PipeHeartbeatScheduler.java
index 04ecb0fcf0c..02400e1a3da 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/runtime/PipeHeartbeatScheduler.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/runtime/PipeHeartbeatScheduler.java
@@ -82,6 +82,12 @@ public class PipeHeartbeatScheduler {
       return;
     }
 
+    if (configManager.getPipeManager().getPipeTaskCoordinator().isLocked()) {
+      LOGGER.warn(
+          "PipeTaskCoordinatorLock is held by another thread, skip this round 
of heartbeat to avoid procedure and rpc accumulation as much as possible");
+      return;
+    }
+
     final Map<Integer, TDataNodeLocation> dataNodeLocationMap =
         configManager.getNodeManager().getRegisteredDataNodeLocations();
     final TPipeHeartbeatReq request = new 
TPipeHeartbeatReq(System.currentTimeMillis());
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/runtime/PipeMetaSyncer.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/runtime/PipeMetaSyncer.java
index 82482aa8c41..c6047e3684f 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/runtime/PipeMetaSyncer.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/runtime/PipeMetaSyncer.java
@@ -86,7 +86,13 @@ public class PipeMetaSyncer {
     }
   }
 
-  private void sync() {
+  private synchronized void sync() {
+    if (configManager.getPipeManager().getPipeTaskCoordinator().isLocked()) {
+      LOGGER.warn(
+          "PipeTaskCoordinatorLock is held by another thread, skip this round 
of sync to avoid procedure and rpc accumulation as much as possible");
+      return;
+    }
+
     final ProcedureManager procedureManager = 
configManager.getProcedureManager();
 
     boolean somePipesNeedRestarting = false;
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/task/PipeTaskCoordinator.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/task/PipeTaskCoordinator.java
index 2e378bb9b7b..25d5911478a 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/task/PipeTaskCoordinator.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/task/PipeTaskCoordinator.java
@@ -66,9 +66,10 @@ public class PipeTaskCoordinator {
   public AtomicReference<PipeTaskInfo> tryLock() {
     if (pipeTaskCoordinatorLock.tryLock()) {
       pipeTaskInfoHolder = new AtomicReference<>(pipeTaskInfo);
+      return pipeTaskInfoHolder;
     }
 
-    return pipeTaskInfoHolder;
+    return null;
   }
 
   /**
@@ -93,6 +94,10 @@ public class PipeTaskCoordinator {
     }
   }
 
+  public boolean isLocked() {
+    return pipeTaskCoordinatorLock.isLocked();
+  }
+
   /** Caller should ensure that the method is called in the lock {@link 
#tryLock()}. */
   public TSStatus createPipe(TCreatePipeReq req) {
     return configManager.getProcedureManager().createPipe(req);
diff --git 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/task/PipeTaskCoordinatorLock.java
 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/task/PipeTaskCoordinatorLock.java
index fff9e6e1029..fcaf98f2024 100644
--- 
a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/task/PipeTaskCoordinatorLock.java
+++ 
b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/task/PipeTaskCoordinatorLock.java
@@ -100,4 +100,8 @@ public class PipeTaskCoordinatorLock {
           Thread.currentThread().getName());
     }
   }
+
+  boolean isLocked() {
+    return !deque.isEmpty();
+  }
 }

Reply via email to