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

pjfanning pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pekko.git


The following commit(s) were added to refs/heads/main by this push:
     new 718bc072bb filter messages from remember-entities store (#3411)
718bc072bb is described below

commit 718bc072bbaed2c37254a0372521ddb506c59916
Author: PJ Fanning <[email protected]>
AuthorDate: Fri Aug 7 12:57:29 2026 +0100

    filter messages from remember-entities store (#3411)
    
    * filter messages from remember-entities store
    
    * Update ExtractorAdapterSpec.scala
    
    * Update ExtractorAdapterSpec.scala
    
    * Update ExtractorAdapterSpec.scala
    
    * Update ExtractorAdapterSpec.scala
---
 .../typed/internal/ClusterShardingImpl.scala       | 15 +++-
 .../typed/internal/ExtractorAdapterSpec.scala      | 90 ++++++++++++++++++++++
 2 files changed, 101 insertions(+), 4 deletions(-)

diff --git 
a/cluster-sharding-typed/src/main/scala/org/apache/pekko/cluster/sharding/typed/internal/ClusterShardingImpl.scala
 
b/cluster-sharding-typed/src/main/scala/org/apache/pekko/cluster/sharding/typed/internal/ClusterShardingImpl.scala
index 9031d7aaf3..eb2968e6f2 100644
--- 
a/cluster-sharding-typed/src/main/scala/org/apache/pekko/cluster/sharding/typed/internal/ClusterShardingImpl.scala
+++ 
b/cluster-sharding-typed/src/main/scala/org/apache/pekko/cluster/sharding/typed/internal/ClusterShardingImpl.scala
@@ -45,6 +45,7 @@ import pekko.cluster.sharding.ShardCoordinator
 import pekko.cluster.sharding.ShardCoordinator.ShardAllocationStrategy
 import pekko.cluster.sharding.ShardRegion
 import pekko.cluster.sharding.ShardRegion.{ StartEntity => ClassicStartEntity }
+import pekko.cluster.sharding.internal.RememberEntitiesShardStore
 import pekko.cluster.sharding.typed.scaladsl.EntityContext
 import pekko.cluster.typed.Cluster
 import pekko.event.Logging
@@ -59,14 +60,18 @@ import pekko.util.Timeout
  * INTERNAL API
  * Extracts entityId and unwraps ShardingEnvelope and StartEntity messages.
  * Other messages are delegated to the given `ShardingMessageExtractor`.
+ * Internal messages from the remember-entities store are filtered out
+ * and should never reach the user-supplied extractor.
  */
 @InternalApi private[pekko] class ExtractorAdapter[E, M](delegate: 
ShardingMessageExtractor[E, M])
     extends ShardingMessageExtractor[Any, M] {
   override def entityId(message: Any): String = {
     message match {
-      case ShardingEnvelope(entityId, _) => entityId // also covers 
ClassicStartEntity in ShardingEnvelope
-      case ClassicStartEntity(entityId)  => entityId
-      case msg                           => 
delegate.entityId(msg.asInstanceOf[E])
+      case ShardingEnvelope(entityId, _)                    => entityId // 
also covers ClassicStartEntity in ShardingEnvelope
+      case ClassicStartEntity(entityId)                     => entityId
+      case _: RememberEntitiesShardStore.UpdateDone         => null
+      case _: RememberEntitiesShardStore.RememberedEntities => null
+      case msg                                              => 
delegate.entityId(msg.asInstanceOf[E])
     }
   }
 
@@ -80,7 +85,9 @@ import pekko.util.Timeout
       case msg: ClassicStartEntity =>
         // not really of type M, but erased and StartEntity is only handled 
internally, not delivered to the entity
         msg.asInstanceOf[M]
-      case msg =>
+      case _: RememberEntitiesShardStore.UpdateDone         => 
null.asInstanceOf[M]
+      case _: RememberEntitiesShardStore.RememberedEntities => 
null.asInstanceOf[M]
+      case msg                                              =>
         delegate.unwrapMessage(msg.asInstanceOf[E])
     }
   }
diff --git 
a/cluster-sharding-typed/src/test/scala/org/apache/pekko/cluster/sharding/typed/internal/ExtractorAdapterSpec.scala
 
b/cluster-sharding-typed/src/test/scala/org/apache/pekko/cluster/sharding/typed/internal/ExtractorAdapterSpec.scala
new file mode 100644
index 0000000000..b19bb111a0
--- /dev/null
+++ 
b/cluster-sharding-typed/src/test/scala/org/apache/pekko/cluster/sharding/typed/internal/ExtractorAdapterSpec.scala
@@ -0,0 +1,90 @@
+/*
+ * 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.pekko.cluster.sharding.typed.internal
+
+import scala.annotation.nowarn
+
+import org.apache.pekko
+import pekko.cluster.sharding.ShardRegion.{ StartEntity => ClassicStartEntity }
+import pekko.cluster.sharding.internal.RememberEntitiesShardStore
+import pekko.cluster.sharding.typed.ShardingEnvelope
+import pekko.cluster.sharding.typed.ShardingMessageExtractor
+
+import org.scalatest.matchers.should.Matchers
+import org.scalatest.wordspec.AnyWordSpecLike
+
+@nowarn("cat=lint-structural-type")
+class ExtractorAdapterSpec extends AnyWordSpecLike with Matchers {
+
+  private val extractor = new ShardingMessageExtractor[String, String] {
+    override def entityId(message: String): String =
+      if (message.startsWith("entity-")) message.substring(7, 
message.indexOf(':'))
+      else null
+
+    override def shardId(entityId: String): String = 
entityId.hashCode.abs.toString
+
+    override def unwrapMessage(message: String): String = 
message.substring(message.indexOf(':') + 1)
+  }
+
+  private val adapter = new ExtractorAdapter(extractor)
+
+  "ExtractorAdapter" must {
+
+    "extract entity id from ShardingEnvelope" in {
+      adapter.entityId(ShardingEnvelope("entity-1", "hello")) should 
===("entity-1")
+    }
+
+    "extract entity id from ClassicStartEntity" in {
+      adapter.entityId(ClassicStartEntity("entity-1")) should ===("entity-1")
+    }
+
+    "delegate to user extractor for user messages" in {
+      adapter.entityId("entity-1:hello") should ===("1")
+    }
+
+    "return null for RememberEntitiesShardStore.UpdateDone" in {
+      adapter.entityId(RememberEntitiesShardStore.UpdateDone(Set("entity-1"), 
Set.empty)) should be(null)
+    }
+
+    "return null for RememberEntitiesShardStore.RememberedEntities" in {
+      
adapter.entityId(RememberEntitiesShardStore.RememberedEntities(Set("entity-1", 
"entity-2"))) should be(null)
+    }
+
+    "unwrap ShardingEnvelope message" in {
+      assert(adapter.unwrapMessage(ShardingEnvelope("entity-1", "hello")) === 
"hello")
+    }
+
+    "unwrap ClassicStartEntity message" in {
+      val msg = ClassicStartEntity("entity-1")
+      // widen to Any to avoid checkcast to M at the call site 
(ClassicStartEntity is not actually M)
+      assert((adapter.unwrapMessage(msg): Any) === msg)
+    }
+
+    "delegate unwrapMessage to user extractor for user messages" in {
+      assert(adapter.unwrapMessage("entity-1:hello") === "hello")
+    }
+
+    "return null for unwrapMessage on UpdateDone" in {
+      
assert(adapter.unwrapMessage(RememberEntitiesShardStore.UpdateDone(Set("entity-1"),
 Set.empty)) === null)
+    }
+
+    "return null for unwrapMessage on RememberedEntities" in {
+      
assert(adapter.unwrapMessage(RememberEntitiesShardStore.RememberedEntities(Set("entity-1")))
 === null)
+    }
+  }
+}


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

Reply via email to