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

rmaucher pushed a commit to branch 11.0.x
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 0de06540fa0b547f1175e998429c5e4899fbb932
Author: opencode <[email protected]>
AuthorDate: Sun Oct 11 13:09:09 2026 +0200

    Clamp the backup index to the member snapshot in publishEntryInfo
    
    The backup node selection snapshots the members with getMapMembers
    and then computes the starting index with getNextBackupIndex, which
    clamps against the current member count rather than the snapshot. A
    member added between the two steps lets the returned index point
    beyond the end of the snapshot, and the first array access in the
    selection loop then threw an array index out of bounds exception.
    
    That exception was not caught by put(), which only handles channel
    exceptions, so it escaped before the entry was stored in the inner
    map. With the BackupManager, whose session storage is the replicated
    map, this lost a session that was being created while a node joined
    the cluster.
    
    The selection now falls back to the start of the snapshot when the
    index is out of its range, mirroring the existing guard in
    getNextBackupNode. Add a unit test that pins the diverging snapshot
    and index and drives the selection to completion.
---
 .../catalina/tribes/tipis/LazyReplicatedMap.java   |  8 ++-
 .../tribes/tipis/TestLazyReplicatedMap.java        | 61 ++++++++++++++++++++++
 2 files changed, 68 insertions(+), 1 deletion(-)

diff --git a/java/org/apache/catalina/tribes/tipis/LazyReplicatedMap.java 
b/java/org/apache/catalina/tribes/tipis/LazyReplicatedMap.java
index 8780a50564..b1b9bd207f 100644
--- a/java/org/apache/catalina/tribes/tipis/LazyReplicatedMap.java
+++ b/java/org/apache/catalina/tribes/tipis/LazyReplicatedMap.java
@@ -158,13 +158,19 @@ public class LazyReplicatedMap<K, V> extends 
AbstractReplicatedMap<K,V> {
         }
         Member[] members = getMapMembers();
         int firstIdx = getNextBackupIndex();
-        int nextIdx = firstIdx;
         Member[] backup = new Member[0];
 
         // there are no backups
         if (members.length == 0 || firstIdx == -1) {
             return backup;
         }
+        // A member may have been added between the snapshot of the members 
and the computation of
+        // the index, which then points beyond the end of the snapshot. Fall 
back to its start, as
+        // getNextBackupNode does.
+        if (firstIdx >= members.length) {
+            firstIdx = 0;
+        }
+        int nextIdx = firstIdx;
 
         boolean success = false;
         do {
diff --git a/test/org/apache/catalina/tribes/tipis/TestLazyReplicatedMap.java 
b/test/org/apache/catalina/tribes/tipis/TestLazyReplicatedMap.java
new file mode 100644
index 0000000000..f6cc37fec2
--- /dev/null
+++ b/test/org/apache/catalina/tribes/tipis/TestLazyReplicatedMap.java
@@ -0,0 +1,61 @@
+/*
+ * 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.tribes.tipis;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.group.GroupChannel;
+import org.apache.catalina.tribes.membership.MemberImpl;
+
+public class TestLazyReplicatedMap {
+
+    /*
+     * The backup node selection snapshots the members and computes an index 
against the current
+     * members in two steps. A member added in between lets the index point 
behind the end of the
+     * snapshot, which aborted the publication with an array index out of 
bounds exception, losing
+     * the entry that was being stored. The index must fall back to the start 
of the snapshot.
+     */
+    @Test
+    public void testBackupIndexBeyondMemberSnapshotFallsBack() throws 
Exception {
+        Member member = new MemberImpl("localhost", 4007, -1);
+
+        // A member added between the two steps of the selection, shown 
directly: one member in
+        // the snapshot, an index computed for a member list that has 
meanwhile grown
+        LazyReplicatedMap<String,String> map = new 
LazyReplicatedMap<String,String>(
+                (key, value) -> {
+                    // Not made primary here
+                }, new GroupChannel(), 1000, "TestLazyReplicatedMap", null) {
+
+            @Override
+            public Member[] getMapMembers() {
+                return new Member[] { member };
+            }
+
+            @Override
+            public int getNextBackupIndex() {
+                return 1;
+            }
+        };
+
+        // The channel is not started, so the publication to the backup fails 
as a channel
+        // exception that the selection contains, while the entry survives to 
be stored locally
+        Member[] backup = map.publishEntryInfo("key", "value");
+        Assert.assertEquals(0, backup.length);
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to