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

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

commit 335757bff3d5e8310c4f69804f0b3b57bb0bd7e5
Author: opencode <[email protected]>
AuthorDate: Sun Oct 11 14:26:19 2026 +0200

    Skip the removal notification for keys that cannot be serialized
    
    The removal broadcast built its message with the key, which threw a
    class cast exception for a key that does not implement Serializable, or
    a runtime exception when a Serializable key still failed to serialize.
    The exception was not caught as a channel exception and escaped the
    removal. Since the put calls the removal first, a put that replaced an
    existing entry under such a key aborted before it stored the new value.
    
    Keys that cannot be serialized only ever exist on the node that stored
    them, as every replication path checks that the key is serializable
    before it sends one, so the notification has no destination that could
    hold the entry. The removal now skips it, matching the put which also
    silently keeps such an entry local.
---
 .../tribes/tipis/AbstractReplicatedMap.java        |  4 ++-
 .../tribes/tipis/TestAbstractReplicatedMap.java    | 33 ++++++++++++++++++++++
 2 files changed, 36 insertions(+), 1 deletion(-)

diff --git a/java/org/apache/catalina/tribes/tipis/AbstractReplicatedMap.java 
b/java/org/apache/catalina/tribes/tipis/AbstractReplicatedMap.java
index a75198c0a9..e79ec1b6ab 100644
--- a/java/org/apache/catalina/tribes/tipis/AbstractReplicatedMap.java
+++ b/java/org/apache/catalina/tribes/tipis/AbstractReplicatedMap.java
@@ -1226,7 +1226,9 @@ public abstract class AbstractReplicatedMap<K, V>
         MapEntry<K,V> entry = innerMap.remove(key);
 
         try {
-            if (getMapMembers().length > 0 && notify && entry != null) {
+            // Skip the notification for a key that cannot be serialized, as 
the message
+            // construction would fail on it, like the replication of put does
+            if (getMapMembers().length > 0 && notify && entry != null && key 
instanceof Serializable) {
                 MapMessage msg = new MapMessage(getMapContextName(), 
MapMessage.MSG_REMOVE, false, (Serializable) key,
                         null, null, null, null);
                 getChannel().send(getMapMembers(), msg, 
getChannelSendOptions());
diff --git 
a/test/org/apache/catalina/tribes/tipis/TestAbstractReplicatedMap.java 
b/test/org/apache/catalina/tribes/tipis/TestAbstractReplicatedMap.java
index 83d933246b..e4da5f26fa 100644
--- a/test/org/apache/catalina/tribes/tipis/TestAbstractReplicatedMap.java
+++ b/test/org/apache/catalina/tribes/tipis/TestAbstractReplicatedMap.java
@@ -31,7 +31,9 @@ import java.util.concurrent.atomic.AtomicReference;
 import org.junit.Assert;
 import org.junit.Test;
 
+import org.apache.catalina.tribes.ErrorHandler;
 import org.apache.catalina.tribes.Member;
+import org.apache.catalina.tribes.UniqueId;
 import org.apache.catalina.tribes.group.GroupChannel;
 import org.apache.catalina.tribes.membership.MemberImpl;
 import org.apache.catalina.tribes.tipis.AbstractReplicatedMap.MapMessage;
@@ -44,10 +46,18 @@ public class TestAbstractReplicatedMap {
      */
     private static final class TestChannel extends GroupChannel {
 
+        private final AtomicInteger sendCalls = new AtomicInteger();
+
         @Override
         public Member getMember(Member mbr) {
             return mbr;
         }
+
+        @Override
+        public UniqueId send(Member[] destination, Serializable msg, int 
options, ErrorHandler handler) {
+            sendCalls.incrementAndGet();
+            return new UniqueId();
+        }
     }
 
     /*
@@ -211,4 +221,27 @@ public class TestAbstractReplicatedMap {
         Assert.assertTrue(((ArrayList<?>) list).get(0) instanceof MapMessage);
         Assert.assertEquals(MapMessage.MSG_COPY, ((MapMessage) ((ArrayList<?>) 
list).get(0)).getMsgType());
     }
+
+    /*
+     * The put silently accepts keys that cannot be replicated, so the remove 
must not fail on such
+     * a key either, but just skip the notification to the other members. The 
removal previously
+     * threw an unchecked exception from the message construction, which also 
escaped a put that
+     * replaced such an entry before the new value was stored.
+     */
+    @Test
+    public void testRemoveNonSerializableKeySkipsNotification() throws 
Exception {
+        TestChannel channel = new TestChannel();
+        LazyReplicatedMap<Object,String> map = new 
LazyReplicatedMap<Object,String>(
+                (key, value) -> {
+                    // Not made primary here
+                }, channel, 1000, "TestAbstractReplicatedMap", null);
+        map.memberAlive(new MemberImpl("localhost", 4016, -1));
+
+        Object key = new Object();
+        map.put(key, "value");
+
+        Assert.assertEquals("value", map.remove(key));
+        Assert.assertNull(map.getInternal(key));
+        Assert.assertEquals(0, channel.sendCalls.get());
+    }
 }


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

Reply via email to