He-Pin commented on code in PR #3677:
URL: https://github.com/apache/pekko/pull/3677#discussion_r4367348199


##########
cluster-sharding/src/main/scala/org/apache/pekko/cluster/sharding/ShardCoordinator.scala:
##########
@@ -233,13 +243,14 @@ object ShardCoordinator {
      * @param shardId                 the id of the shard to allocate
      * @param currentShardAllocations all actor refs to `ShardRegion` and 
their current allocated shards,
      *                                in the order they were allocated
-     * @return a `Future` of the actor ref of the `ShardRegion` that is to be 
responsible for the shard, must be one of
-     *         the references included in the `currentShardAllocations` 
parameter
+     * @return a `CompletionStage` of the actor ref of the `ShardRegion` that 
is to be responsible for the shard,
+     *         must be one of the references included in the 
`currentShardAllocations` parameter
+     *         (in Pekko 1.x, this was a Scala `Future`)
      */
     def allocateShard(
         requester: ActorRef,
         shardId: String,
-        currentShardAllocations: java.util.Map[ActorRef, 
immutable.IndexedSeq[String]]): Future[ActorRef]
+        currentShardAllocations: java.util.Map[ActorRef, ? <: 
java.lang.Iterable[String]]): CompletionStage[ActorRef]

Review Comment:
   Switched to `java.util.Map<ActorRef, ? extends java.util.List<String>>`.
   
   You were right and my earlier reply answered a different question. 
`java.lang.Iterable` has no `size()`, so a least-shards strategy would have to 
walk every shard id of every region on each `allocateShard` - O(total shards) 
on the coordinator's single-threaded path. That is the same cost the lazy view 
was meant to remove, so the view only fixed half of it.
   
   Bounding the wildcard on `List` rather than dropping it keeps both 
properties:
   
   * implementations reach the `List` API with no downcast - 
`entry.getValue().size()`, `map.get(region).get(0)` and `isEmpty()` all 
compile. `AbstractShardAllocationStrategyTest.exposeAllocationValuesAsLists` 
asserts this;
   * callers can still pass `Map<ActorRef, ArrayList<String>>` or `LinkedList` 
- `acceptArrayListValues` / `acceptLinkedListValues`.
   
   Both directions are verified with javac, not assumed. One consequence is now 
in the Javadoc: a Java override cannot narrow the parameter to `Map<ActorRef, 
List<String>>`, javac rejects it with "have the same erasure, yet neither 
overrides the other", so overrides must keep the wildcard.
   
   `asJavaShardAllocations` already produced `java.util.List` values, so the 
adapter needed no change. Migration guide and the MiMa filter comment updated. 
The filters themselves are unchanged and still sufficient - generic parameters 
do not appear in the descriptor, so `Iterable` -> `List` adds no new problem; 
`+cluster-sharding/mimaReportBinaryIssues` passes on 2.13.18 and 3.3.8 against 
1.0.0.



##########
cluster-sharding/src/main/scala/org/apache/pekko/cluster/sharding/ShardCoordinator.scala:
##########
@@ -212,17 +214,25 @@ object ShardCoordinator {
         requester: ActorRef,
         shardId: ShardId,
         currentShardAllocations: Map[ActorRef, 
immutable.IndexedSeq[ShardId]]): Future[ActorRef] = {
-
-      import scala.jdk.CollectionConverters._
-      allocateShard(requester, shardId, currentShardAllocations.asJava)
+      import scala.jdk.FutureConverters._
+      allocateShard(requester, shardId, 
asJavaShardAllocations(currentShardAllocations)).asScala
     }
 
     override final def rebalance(
         currentShardAllocations: Map[ActorRef, immutable.IndexedSeq[ShardId]],
         rebalanceInProgress: Set[ShardId]): Future[Set[ShardId]] = {
       import scala.jdk.CollectionConverters._
+      import scala.jdk.FutureConverters._
       implicit val ec = ExecutionContext.parasitic
-      rebalance(currentShardAllocations.asJava, 
rebalanceInProgress.asJava).map(_.asScala.toSet)
+      rebalance(asJavaShardAllocations(currentShardAllocations), 
rebalanceInProgress.asJava)
+        .asScala
+        .map(_.asScala.toSet)
+    }
+
+    private def asJavaShardAllocations(currentShardAllocations: Map[ActorRef, 
immutable.IndexedSeq[ShardId]])
+        : java.util.Map[ActorRef, java.util.List[ShardId]] = {
+      import scala.jdk.CollectionConverters._
+      currentShardAllocations.map { case (region, shards) => region -> 
shards.asJava }.asJava
     }

Review Comment:
   Fixed - `asJavaShardAllocations` now returns a `java.util.AbstractMap` that 
wraps values on access, the second of your two suggestions. `get`, 
`containsKey` and `size` are O(1) and only the entries a strategy actually 
iterates allocate a wrapper.
   
   `AbstractShardAllocationStrategySpec` has a regression test for it: it hands 
the adapter a map whose `iterator` throws, so any eager traversal fails the 
suite.



##########
cluster-sharding/src/main/scala/org/apache/pekko/cluster/sharding/ShardCoordinator.scala:
##########
@@ -212,17 +214,25 @@ object ShardCoordinator {
         requester: ActorRef,
         shardId: ShardId,
         currentShardAllocations: Map[ActorRef, 
immutable.IndexedSeq[ShardId]]): Future[ActorRef] = {
-
-      import scala.jdk.CollectionConverters._
-      allocateShard(requester, shardId, currentShardAllocations.asJava)
+      import scala.jdk.FutureConverters._
+      allocateShard(requester, shardId, 
asJavaShardAllocations(currentShardAllocations)).asScala
     }
 
     override final def rebalance(
         currentShardAllocations: Map[ActorRef, immutable.IndexedSeq[ShardId]],
         rebalanceInProgress: Set[ShardId]): Future[Set[ShardId]] = {
       import scala.jdk.CollectionConverters._
+      import scala.jdk.FutureConverters._
       implicit val ec = ExecutionContext.parasitic
-      rebalance(currentShardAllocations.asJava, 
rebalanceInProgress.asJava).map(_.asScala.toSet)
+      rebalance(asJavaShardAllocations(currentShardAllocations), 
rebalanceInProgress.asJava)
+        .asScala
+        .map(_.asScala.toSet)
+    }
+
+    private def asJavaShardAllocations(currentShardAllocations: Map[ActorRef, 
immutable.IndexedSeq[ShardId]])
+        : java.util.Map[ActorRef, java.util.List[ShardId]] = {
+      import scala.jdk.CollectionConverters._
+      currentShardAllocations.map { case (region, shards) => region -> 
shards.asJava }.asJava

Review Comment:
   Done - both converters are imported once at the top of 
`AbstractShardAllocationStrategy` instead of three times inside the methods.



##########
cluster-sharding/src/main/scala/org/apache/pekko/cluster/sharding/ShardCoordinator.scala:
##########
@@ -233,13 +243,14 @@ object ShardCoordinator {
      * @param shardId                 the id of the shard to allocate
      * @param currentShardAllocations all actor refs to `ShardRegion` and 
their current allocated shards,
      *                                in the order they were allocated
-     * @return a `Future` of the actor ref of the `ShardRegion` that is to be 
responsible for the shard, must be one of
-     *         the references included in the `currentShardAllocations` 
parameter
+     * @return a `CompletionStage` of the actor ref of the `ShardRegion` that 
is to be responsible for the shard,
+     *         must be one of the references included in the 
`currentShardAllocations` parameter
+     *         (in Pekko 1.x, this was a Scala `Future`)
      */
     def allocateShard(

Review Comment:
   Documented rather than adding a parallel deprecated class. The migration 
guide now states explicitly that a subclass compiled against Pekko 1.x does not 
implement the new callbacks and fails at runtime with `AbstractMethodError` 
when the coordinator invokes the strategy, preventing allocation and 
rebalancing, and that Java subclasses must be recompiled against Pekko 2.x. 
That matches how the other Java API cleanups for 2.0 were handled.



##########
docs/src/main/paradox/migration/migration-guide-1.x-2.x.md:
##########
@@ -65,6 +65,13 @@ by Akka 2.3.x. The `-2.3` program argument and the 
`remove2dot3Data` parameter o
 `Sequence`. Code that cast these offsets to `Sequence` needs updating; the 
per-persistence-id sequence number is
 still available as `envelope.sequenceNr`. These queries also now accept a 
`TimestampOffset` to resume from.
 ([PR3615](https://github.com/apache/pekko/pull/3615))
+* `ShardCoordinator.AbstractShardAllocationStrategy`, the Java API for custom 
shard allocation strategies, no longer
+leaks Scala types. Its `allocateShard` and `rebalance` methods now return a 
`java.util.concurrent.CompletionStage`
+instead of a Scala `Future`, and the current shard allocations are passed as a
+`java.util.Map<ActorRef, ? extends Iterable<String>>` instead of a 
`java.util.Map` of
+`scala.collection.immutable.IndexedSeq<String>`. Java implementations of these 
two methods need to be updated
+accordingly; Scala implementations of 
`ShardCoordinator.ShardAllocationStrategy` are unaffected.

Review Comment:
   Done - the entry now says the failure is a runtime `AbstractMethodError` 
when the coordinator calls the strategy, not a compile or link time error, and 
the parameter type reads `java.util.Map<ActorRef, ? extends List<String>>`.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


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

Reply via email to