This is an automated email from the ASF dual-hosted git repository. rmaucher pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/tomcat.git
commit 20a9fdfb4cb94f1197874761890e694a9cb2fea3 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 29e0c69147..ead1263dd8 100644 --- a/java/org/apache/catalina/tribes/tipis/AbstractReplicatedMap.java +++ b/java/org/apache/catalina/tribes/tipis/AbstractReplicatedMap.java @@ -1222,7 +1222,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]
