Message Reliability - At-Least-Once-Delivery - Sending message in TodoList pattern

How do I actually send a message to the TodoService actor in the Message Reliability example?

 EntityTypeKey<TodoService.Command> entityTypeKey1 =
          EntityTypeKey.create(TodoService.Command.class, "todo");

      EntityRef<TodoService.Command> entityRef1 =
          ClusterSharding.get(system).entityRefFor(entityTypeKey1, "123");
      CompletionStage<Response> r = entityRef1.ask(replyTo -> new UpdateTodo("123", "tire", false, replyTo),
                                                   Duration.ofSeconds(3));

I get the below error:

java.lang.ClassCastException: com.agapic.TodoService$UpdateTodo cannot be cast to akka.actor.typed.delivery.ConsumerController$Command

Source: https://github.com/akka/akka/pull/28155/files#r477031068

@patriknw: you mentioned the below: could you explain what I was trying to do vs. how it is intended to be used?

“However, I think you misunderstand how it is intended to be used. You try to use it with two levels of Sharding, one for the TodoList and one for the TodoService. The docs say " A single ShardingProducerController per ActorSystem (node) can be shared for sending to all entities of a certain entity type.” https://doc.akka.io/docs/akka/current/typed/reliable-delivery.html#sharding"

That is the example shown in Reliable delivery • Akka Documentation

That example is using an EntityTypeKey with identifier “todo” for the TodoList.

EntityTypeKey<ConsumerController.SequencedMessage<TodoList.Command>> entityTypeKey =
    EntityTypeKey.create(ShardingConsumerController.entityTypeKeyClass(), "todo");

The identifier of the EntityTypeKey must be unique so you can’t reuse “todo” for TodoService. The EntityTypeKey for the TodoService should be:

EntityTypeKey<TodoService.Command> entityTypeKey1 =
          EntityTypeKey.create(TodoService.Command.class, "todoService");

I think that is the reason for the ClassCastException.

However, I don’t think you should access the TodoService via ClusterSharding.

The documentation says:

The producer and ShardingProducerController actors are supposed to be local so that these messages are fast and not lost. This is enforced by a runtime check.

At the example that would be:

// Start a single instance of the TodoService actor
ActorRef<TodoService.Command> todoService = context.spawn(TodoService.create(producerController), "producer");

That ActorRef can then be used for sending messages to the TodoService. For example, from a HTTP route or similar

import akka.actor.typed.scaladsl.AskPattern;

CompletionStage<TodoService.Response> response = AskPattern.ask(todoService, 
  (ActorRef<TodoService.Response> replyTo) -> new TodoService.UpdateTodo("123", "tire", false, replyTo), 
  Duration.ofSeconds(3), system.scheduler());
1 Like