You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@asterixdb.apache.org by AsterixDB Code Review <do...@asterix-gerrit.ics.uci.edu> on 2026/10/13 14:38:42 UTC

[M] Change in asterixdb[master]: [ASTERIXDB-3956][HYR] Don't ack a heartbeat from an unauthorized node

From Michael Blow <mb...@apache.org>:

Michael Blow has uploaded this change for review. ( https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/22083?usp=email )


Change subject: [ASTERIXDB-3956][HYR] Don't ack a heartbeat from an unauthorized node
......................................................................

[ASTERIXDB-3956][HYR] Don't ack a heartbeat from an unauthorized node

- user model changes: no
- storage format changes: no
- interface changes: no

Details: A node deauthorized on its way out of the cluster keeps
heartbeating until it is stopped. The negative ack it got for being
unregistered sent it to re-register, which addNode refuses for an
unauthorized node, and a refused registration halts the node. Leave such
heartbeats unanswered; if the node is authorized again, its next
heartbeat gets the negative ack and it re-registers as before.

Ext-ref: MB-74446
Generated-by: Claude Opus 5.5 <no...@anthropic.com>
Change-Id: Id9745a4144e2568383b74eb2c6d7f4e72d2d3028
---
M hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/work/NodeHeartbeatWork.java
A hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/test/java/org/apache/hyracks/control/cc/work/NodeHeartbeatWorkTest.java
2 files changed, 80 insertions(+), 0 deletions(-)



  git pull ssh://asterix-gerrit.ics.uci.edu:29418/asterixdb refs/changes/83/22083/1

diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/work/NodeHeartbeatWork.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/work/NodeHeartbeatWork.java
index 481c2e0..c59901e 100644
--- a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/work/NodeHeartbeatWork.java
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/main/java/org/apache/hyracks/control/cc/work/NodeHeartbeatWork.java
@@ -49,6 +49,10 @@
         final NodeControllerState ncState = nodeManager.getNodeControllerState(nodeId);
         if (ncState != null) {
             ncState.getNodeController().heartbeatAck(ccs.getCcId(), null);
+        } else if (!ccs.getApplication().getGatekeeper().isAuthorized(nodeId)) {
+            // a node deauthorized on its way out of the cluster keeps heartbeating until it is stopped; a negative
+            // ack would send it to re-register, which addNode refuses, and a refused registration halts the node
+            LOGGER.info("received a heartbeat from unauthorized node {}; not acking", nodeId);
         } else {
             // unregistered nc- let him know
             InetSocketAddress refreshedNcAddress = NetworkUtil.refresh(ncAddress);
diff --git a/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/test/java/org/apache/hyracks/control/cc/work/NodeHeartbeatWorkTest.java b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/test/java/org/apache/hyracks/control/cc/work/NodeHeartbeatWorkTest.java
new file mode 100644
index 0000000..c7f9cb3
--- /dev/null
+++ b/hyracks-fullstack/hyracks/hyracks-control/hyracks-control-cc/src/test/java/org/apache/hyracks/control/cc/work/NodeHeartbeatWorkTest.java
@@ -0,0 +1,76 @@
+/*
+ * 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.hyracks.control.cc.work;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.argThat;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.net.InetSocketAddress;
+
+import org.apache.hyracks.api.application.ICCApplication;
+import org.apache.hyracks.api.control.CcId;
+import org.apache.hyracks.api.exceptions.ErrorCode;
+import org.apache.hyracks.control.cc.ClusterControllerService;
+import org.apache.hyracks.control.cc.cluster.INodeManager;
+import org.apache.hyracks.control.common.ipc.CCNCFunctions;
+import org.apache.hyracks.ipc.api.IIPCHandle;
+import org.apache.hyracks.ipc.impl.IPCSystem;
+import org.junit.Test;
+
+public class NodeHeartbeatWorkTest {
+
+    private static final String NODE = "nc1";
+    private static final InetSocketAddress NC_ADDRESS = new InetSocketAddress("127.0.0.1", 9115);
+
+    private final IPCSystem clusterIpc = mock(IPCSystem.class);
+    private final IIPCHandle ncHandle = mock(IIPCHandle.class);
+
+    @Test
+    public void unregisteredAuthorizedNodeIsToldToReregister() throws Exception {
+        heartbeat(true);
+        verify(ncHandle).send(anyLong(), argThat(
+                f -> ((CCNCFunctions.NodeHeartbeatAckFunction) f).getException().matches(ErrorCode.NO_SUCH_NODE)),
+                any());
+    }
+
+    @Test
+    public void unauthorizedNodeIsNotAcked() throws Exception {
+        heartbeat(false);
+        verify(clusterIpc, never()).getReconnectingHandle(any());
+        verify(ncHandle, never()).send(anyLong(), any(), any());
+    }
+
+    private void heartbeat(boolean authorized) throws Exception {
+        ClusterControllerService ccs = mock(ClusterControllerService.class);
+        INodeManager nodeManager = mock(INodeManager.class);
+        ICCApplication app = mock(ICCApplication.class);
+        when(ccs.getNodeManager()).thenReturn(nodeManager);
+        when(ccs.getApplication()).thenReturn(app);
+        when(app.getGatekeeper()).thenReturn(nodeId -> authorized);
+        when(ccs.getCcId()).thenReturn(CcId.valueOf(0));
+        when(ccs.getClusterIPC()).thenReturn(clusterIpc);
+        when(clusterIpc.getReconnectingHandle(any())).thenReturn(ncHandle);
+        new NodeHeartbeatWork(ccs, NODE, null, NC_ADDRESS).doRun();
+    }
+}

-- 
To view, visit https://asterix-gerrit.ics.uci.edu/c/asterixdb/+/22083?usp=email
To unsubscribe, or for help writing mail filters, visit https://asterix-gerrit.ics.uci.edu/settings?usp=email

Gerrit-MessageType: newchange
Gerrit-Project: asterixdb
Gerrit-Branch: master
Gerrit-Change-Id: Id9745a4144e2568383b74eb2c6d7f4e72d2d3028
Gerrit-Change-Number: 22083
Gerrit-PatchSet: 1
Gerrit-Owner: Michael Blow <mb...@apache.org>