JackieTien97 commented on code in PR #18265:
URL: https://github.com/apache/iotdb/pull/18265#discussion_r3627176818


##########
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/template/ClusterTemplateManager.java:
##########
@@ -273,6 +274,7 @@ public List<PartialPath> getPathsSetTemplate(String name, 
PathPatternTree scope)
   public Template getTemplate(int id) {
     readWriteLock.readLock().lock();
     try {
+      failIfMetadataLeaseFenced();

Review Comment:
   [P1] Keep lease fencing out of SchemaRegion apply
   
   `getTemplate(int)` is also called from 
`SchemaExecutionVisitor#visitActivateTemplate` and `visitBatchActivateTemplate` 
while applying committed SchemaRegion consensus entries on every replica. A 
replica can lose ConfigNode heartbeats and become fenced while it is still 
receiving SchemaRegion logs, so this unchecked `MetadataLeaseFencedException` 
escapes `SchemaRegionStateMachine#write` and makes apply depend on 
replica-local lease state. Please enforce the lease before consensus 
submission, but make state-machine apply independent of the fenced request 
cache—for example, carry the template definition/version in the plan or use a 
non-fenced durable lookup during apply.



##########
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/template/ClusterTemplateManager.java:
##########
@@ -678,11 +699,30 @@ public void putTemplate(Template template) {
   }
 
   public void clear() {
-    templateIdMap.clear();
-    templateNameMap.clear();
-    pathSetTemplateMap.clear();
-    templateSetOnPathsMap.clear();
-    pathPreSetTemplateMap.clear();
-    templatePreSetOnPathsMap.clear();
+    readWriteLock.writeLock().lock();
+    try {
+      templateIdMap.clear();
+      templateNameMap.clear();
+      pathSetTemplateMap.clear();
+      templateSetOnPathsMap.clear();
+      pathPreSetTemplateMap.clear();
+      templatePreSetOnPathsMap.clear();
+    } finally {
+      readWriteLock.writeLock().unlock();
+    }
+  }
+
+  public void reloadTemplateCacheAfterLeaseRecovery(byte[] templateSetInfo) {
+    readWriteLock.writeLock().lock();
+    try {
+      clear();
+      initTemplateSetInfo(templateSetInfo);

Review Comment:
   [P1] Reconcile active-template counts during recovery
   
   This reload replaces only the template maps. If a template with active 
devices is ALTERed while this DataNode is fenced, it misses 
`UPDATE_TEMPLATE_INFO`—the only path that computes the old/new measurement 
delta and calls `SchemaEngine#updateSubtreeMeasurementCountForTemplate`. After 
recovery, Memory SchemaRegions therefore retain the old 
`subtreeMeasurementCount` while the template has the new schema, which can make 
OFFSET-based traversal prune incorrectly and later deactivation subtract the 
wrong count. Please rebuild these derived counts, or apply the per-template 
deltas, before returning the lease to NORMAL.



##########
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/template/ClusterTemplateManager.java:
##########
@@ -678,11 +699,30 @@ public void putTemplate(Template template) {
   }
 
   public void clear() {
-    templateIdMap.clear();
-    templateNameMap.clear();
-    pathSetTemplateMap.clear();
-    templateSetOnPathsMap.clear();
-    pathPreSetTemplateMap.clear();
-    templatePreSetOnPathsMap.clear();
+    readWriteLock.writeLock().lock();
+    try {
+      templateIdMap.clear();
+      templateNameMap.clear();
+      pathSetTemplateMap.clear();
+      templateSetOnPathsMap.clear();
+      pathPreSetTemplateMap.clear();
+      templatePreSetOnPathsMap.clear();
+    } finally {
+      readWriteLock.writeLock().unlock();
+    }
+  }
+
+  public void reloadTemplateCacheAfterLeaseRecovery(byte[] templateSetInfo) {
+    readWriteLock.writeLock().lock();

Review Comment:
   [P1] Serialize recovered template sets with in-flight CREATEs
   
   Normal `ADD_TEMPLATE_PRE_SET_INFO` takes the global `TIMESERIES_VS_TEMPLATE` 
write lock because a CREATE that passed the analyzer check holds the matching 
read lock through SchemaRegion apply. This recovery path installs SET/PRE_SET 
entries under only the manager-local lock, so it can publish a template while 
such a CREATE is still in flight. SchemaRegion create/apply does not repeat the 
template compatibility check, allowing an ordinary measurement to be committed 
under the newly set template path. Please take the same global write lock 
around the recovered template swap, or provide equivalent serialization.



##########
iotdb-core/datanode/src/main/java/org/apache/iotdb/db/schemaengine/lease/MetadataLeaseManager.java:
##########
@@ -222,20 +223,38 @@ private void pullMetaDataAndInit() {
       
LOGGER.error(DataNodeSchemaMessages.FAILED_TO_MARK_METADATA_STATE_AS_PULLING, 
metadataState);
       return;
     }
-
-    for (final MetadataAction action : pullMetaList) {
-      try {
-        action.execute();
-      } catch (final Throwable t) {
-        metadataStateRef.set(MetadataState.PULL_OR_INIT_FAILED, 
metadataStateRef.getStamp() + 1);
-        LOGGER.error(DataNodeSchemaMessages.FAILED_TO_PULL_OR_INIT_METADATA, 
t);
-        rethrowUnchecked(t);
-      }
+    try {
+      reloadRelatedCache();
+    } catch (final Throwable t) {
+      metadataStateRef.set(MetadataState.PULL_OR_INIT_FAILED, 
metadataStateRef.getStamp() + 1);
+      LOGGER.error(DataNodeSchemaMessages.FAILED_TO_PULL_OR_INIT_METADATA, t);
+      rethrowUnchecked(t);
     }
+
     this.lastConfigNodeHeartbeatNanos = nanoClock.getAsLong();
     metadataStateRef.set(MetadataState.NORMAL, metadataStateRef.getStamp() + 
1);
   }
 
+  void reloadRelatedCache() {
+    try (ConfigNodeClient configNodeClient =
+        
ConfigNodeClientManager.getInstance().borrowClient(ConfigNodeInfo.CONFIG_REGION_ID))
 {
+
+      final TDataNodeLeaseRecoveryResp resp = 
configNodeClient.reloadCacheAfterLeaseRecovery();

Review Comment:
   [P1] Add a revision barrier to the recovery snapshot
   
   This RPC returns only a point-in-time snapshot. A concurrent SET/UNSET/ALTER 
procedure can select its broadcast targets while this DataNode is still 
regarded as fenced, commit after the snapshot was read, and therefore 
legitimately omit this node. Recovery then installs the older snapshot and 
marks the lease NORMAL, with no revision check or delta catch-up, so the node 
can remain permanently stale. Please keep the node fenced until a ConfigRegion 
revision covering concurrent mutations has been applied, or otherwise linearize 
snapshot generation with update propagation.



##########
iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/schema/ClusterSchemaManager.java:
##########
@@ -1314,23 +1314,17 @@ public synchronized TSStatus extendSchemaTemplate(
     Map<Integer, TDataNodeLocation> dataNodeLocationMap =
         configManager.getNodeManager().getRegisteredDataNodeLocations();
 
-    DataNodeAsyncRequestContext<TUpdateTemplateReq, TSStatus> clientHandler =
-        new DataNodeAsyncRequestContext<>(
-            CnToDnAsyncRequestType.UPDATE_TEMPLATE, updateTemplateReq, 
dataNodeLocationMap);
-    
CnToDnInternalServiceAsyncRequestManager.getInstance().sendAsyncRequestWithRetry(clientHandler);
-    Map<Integer, TSStatus> statusMap = clientHandler.getResponseMap();
-    for (Map.Entry<Integer, TSStatus> entry : statusMap.entrySet()) {
-      if (entry.getValue().getCode() != 
TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
-        LOGGER.warn(
-            ManagerMessages.FAILED_TO_SYNC_TEMPLATE_EXTENSION_INFO_TO_DATANODE,
-            template.getName(),
-            dataNodeLocationMap.get(entry.getKey()));
-        return RpcUtils.getStatus(
-            TSStatusCode.EXECUTE_STATEMENT_ERROR,
-            String.format(
-                "Failed to sync template %s extension info to DataNode %s",
-                template.getName(), dataNodeLocationMap.get(entry.getKey())));
-      }
+    // The template extension is already committed and cannot be rolled back.
+    // Unexpected DataNode internal failures cannot be handled here.
+    final boolean proceed =
+        new ClusterCachePropagator(dataNodeLocationMap)
+            .propagate(targets -> broadcastTemplateUpdate(updateTemplateReq, 
targets));

Review Comment:
   [P1] Prevent stale ALTER retries after a leader change
   
   This retry keeps sending the serialized full template captured above, 
without a ConfigNode term or template version. If this ConfigNode loses 
leadership while `propagate` is waiting, the new leader can commit and 
broadcast a later extension before a retry from the old leader arrives. 
`DataNodeInternalRPCServiceImpl#updateTemplate` then unconditionally replaces 
the cached template and applies the measurement-count delta in arrival order, 
so the late retry regresses both the schema and subtree counts. Please abort or 
reconcile retries on leadership loss, or version these updates so DataNodes 
reject older snapshots.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to