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

DaanHoogland pushed a commit to branch 4.20
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/4.20 by this push:
     new 6e4413d59b7 engine: do not stall the host command queue on a failed 
send in AgentAttache.sendNext (#14040)
6e4413d59b7 is described below

commit 6e4413d59b771d770dca6aa74d4454016b65f720
Author: Ramgopal Nagaboina <[email protected]>
AuthorDate: Wed Sep 9 13:38:51 2026 -0400

    engine: do not stall the host command queue on a failed send in 
AgentAttache.sendNext (#14040)
---
 .../java/com/cloud/agent/manager/AgentAttache.java |  3 +-
 .../agent/manager/AgentAttacheSendNextTest.java    | 84 ++++++++++++++++++++++
 2 files changed, 86 insertions(+), 1 deletion(-)

diff --git 
a/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentAttache.java 
b/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentAttache.java
index 3b27007cbff..651bcd6dfea 100644
--- 
a/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentAttache.java
+++ 
b/engine/orchestration/src/main/java/com/cloud/agent/manager/AgentAttache.java
@@ -468,11 +468,12 @@ public abstract class AgentAttache {
         logger.debug(LOG_SEQ_FORMATTED_STRING, req.getSequence(), "Sending 
now.  is current sequence.");
         try {
             send(req);
+            _currentSequence = req.getSequence();
         } catch (AgentUnavailableException e) {
             logger.debug(LOG_SEQ_FORMATTED_STRING, req.getSequence(), "Unable 
to send the next sequence");
             cancel(req.getSequence());
+            sendNext(req.getSequence());
         }
-        _currentSequence = req.getSequence();
     }
 
     public void process(final Answer[] answers) {
diff --git 
a/engine/orchestration/src/test/java/com/cloud/agent/manager/AgentAttacheSendNextTest.java
 
b/engine/orchestration/src/test/java/com/cloud/agent/manager/AgentAttacheSendNextTest.java
new file mode 100644
index 00000000000..967cbd9d60c
--- /dev/null
+++ 
b/engine/orchestration/src/test/java/com/cloud/agent/manager/AgentAttacheSendNextTest.java
@@ -0,0 +1,84 @@
+// 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 com.cloud.agent.manager;
+
+import org.junit.Assert;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import com.cloud.agent.transport.Request;
+import com.cloud.exception.AgentUnavailableException;
+import com.cloud.host.Status;
+
+public class AgentAttacheSendNextTest {
+
+    /**
+     * Minimal concrete AgentAttache: send() fails for one designated sequence 
and succeeds otherwise,
+     * recording the sequence that was actually dispatched.
+     */
+    static class TestAgentAttache extends AgentAttache {
+        Long sentSeq;
+        final long failSeq;
+
+        TestAgentAttache(long failSeq) {
+            super(null, 1L, "uuid-1", "host-1", false);
+            this.failSeq = failSeq;
+        }
+
+        @Override
+        public void send(Request req) throws AgentUnavailableException {
+            if (req.getSequence() == failSeq) {
+                throw new AgentUnavailableException("simulated transient link 
failure", _id);
+            }
+            sentSeq = req.getSequence();
+        }
+
+        @Override
+        public void disconnect(Status state) {
+        }
+
+        @Override
+        protected boolean isClosed() {
+            return false;
+        }
+    }
+
+    @Test
+    public void sendNextAdvancesPastAFailedCommandToTheNextQueued() {
+        long failSeq = 100L;
+        long goodSeq = 200L;
+
+        Request failing = Mockito.mock(Request.class);
+        Mockito.when(failing.getSequence()).thenReturn(failSeq);
+        Request good = Mockito.mock(Request.class);
+        Mockito.when(good.getSequence()).thenReturn(goodSeq);
+
+        TestAgentAttache attache = new TestAgentAttache(failSeq);
+        attache._requests.add(failing);
+        attache._requests.add(good);
+
+        attache.sendNext(1L);
+
+        // A command whose send() failed (and was cancelled) must NOT become 
_currentSequence: no answer
+        // will ever arrive for it, so every later in-sequence command to this 
host would queue behind it
+        // and time out. sendNext must move on and dispatch the next queued 
command instead.
+        Assert.assertEquals("the next queued command should have been 
dispatched", Long.valueOf(goodSeq), attache.sentSeq);
+        Assert.assertEquals("current sequence must be the successfully sent 
command, not the failed one",
+                Long.valueOf(goodSeq), attache._currentSequence);
+        Assert.assertTrue("the request queue should be drained", 
attache._requests.isEmpty());
+    }
+}

Reply via email to