From cc1e429c7c7c05999a60477d8607fa3f110b53cc Mon Sep 17 00:00:00 2001 From: lihongyi <642826683@qq.com> Date: Tue, 18 Aug 2026 23:25:24 +0800 Subject: [PATCH] Reset noContextManagerReceived when requesting session state The flag is set when EVT_ALL_SESSION_NOCONTEXTMANAGER is received and is never reset. getAllClusterSessions() resets stateTransferred before requesting session state but leaves noContextManagerReceived set, so waitForSendAllSessions() returns immediately for every subsequent state transfer on the same manager instance, skipping sessions the cluster does send. Reset the flag alongside stateTransferred. --- .../catalina/ha/session/DeltaManager.java | 1 + .../TestDeltaManagerStateTransfer.java | 80 +++++++++++++++++++ webapps/docs/changelog.xml | 7 ++ 3 files changed, 88 insertions(+) create mode 100644 test/org/apache/catalina/ha/session/TestDeltaManagerStateTransfer.java diff --git a/java/org/apache/catalina/ha/session/DeltaManager.java b/java/org/apache/catalina/ha/session/DeltaManager.java index f6a8269735ab..b7a9b2c26e6c 100644 --- a/java/org/apache/catalina/ha/session/DeltaManager.java +++ b/java/org/apache/catalina/ha/session/DeltaManager.java @@ -841,6 +841,7 @@ public synchronized void getAllClusterSessions() { counterSend_EVT_GET_ALL_SESSIONS.incrementAndGet(); } stateTransferred = false; + noContextManagerReceived = false; // FIXME This send call block the deploy thread, when sender waitForAck is enabled try { synchronized (receivedMessageQueue) { diff --git a/test/org/apache/catalina/ha/session/TestDeltaManagerStateTransfer.java b/test/org/apache/catalina/ha/session/TestDeltaManagerStateTransfer.java new file mode 100644 index 000000000000..0c1c0d7f778a --- /dev/null +++ b/test/org/apache/catalina/ha/session/TestDeltaManagerStateTransfer.java @@ -0,0 +1,80 @@ +/* + * 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.catalina.ha.session; + +import org.easymock.EasyMock; +import org.junit.Assert; +import org.junit.Test; + +import org.apache.catalina.ha.CatalinaCluster; +import org.apache.catalina.tribes.Member; + +/** + * Tests that {@link DeltaManager#getAllClusterSessions()} resets the + * {@code noContextManagerReceived} flag before requesting session state. If + * the flag is not reset, a previously received + * {@code EVT_ALL_SESSION_NOCONTEXTMANAGER} causes the state transfer wait to + * exit immediately on every subsequent request, even when the cluster node + * now has a context manager and sends session state. + */ +public class TestDeltaManagerStateTransfer { + + private static CatalinaCluster createClusterWithOneMember() { + // findSessionMasterMember() uses members[0] as the state transfer + // source so the array must contain a non-null member. + Member member = EasyMock.createNiceMock(Member.class); + CatalinaCluster cluster = EasyMock.createNiceMock(CatalinaCluster.class); + EasyMock.expect(cluster.getMembers()).andReturn(new Member[] { member }).anyTimes(); + // send() is a void method: a nice mock performs no action. + EasyMock.replay(cluster); + return cluster; + } + + @Test + public void testNoContextManagerFlagResetOnNewStateRequest() throws Exception { + DeltaManager manager = new DeltaManager(); + manager.setCluster(createClusterWithOneMember()); + // Keep the wait short: the test does not provide a real reply so the + // wait will end via the timeout path. + manager.setStateTransferTimeout(1); + + // Simulate a previously received EVT_ALL_SESSION_NOCONTEXTMANAGER + manager.setNoContextManagerReceived(true); + + manager.getAllClusterSessions(); + + // The new state request must reset the flag so the wait is not + // skipped immediately on this and subsequent transfers. + Assert.assertFalse("noContextManagerReceived should be reset when requesting session state", + manager.isNoContextManagerReceived()); + } + + @Test + public void testStateTransferredResetOnNewStateRequest() throws Exception { + DeltaManager manager = new DeltaManager(); + manager.setCluster(createClusterWithOneMember()); + manager.setStateTransferTimeout(1); + + // Simulate a completed previous transfer + manager.setStateTransferred(true); + + manager.getAllClusterSessions(); + + Assert.assertFalse("stateTransferred should be reset when requesting session state", + manager.getStateTransferred()); + } +} diff --git a/webapps/docs/changelog.xml b/webapps/docs/changelog.xml index a72a483a4ddc..3239c10d24ce 100644 --- a/webapps/docs/changelog.xml +++ b/webapps/docs/changelog.xml @@ -344,6 +344,13 @@ NegativeArraySizeException during session state transfer. Pull request 1042 provided by lihongyi87. (markt) + + Ensure DeltaManager resets the + noContextManagerReceived flag when requesting session + state. A previously received ALL_SESSION_NOCONTEXTMANAGER + event caused all subsequent state transfers to return immediately, + skipping sessions sent by the cluster. (lihongyi87) +