From e9b6006d335b6035cc1dfdd7d3a9017dca4bbdce Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Wed, 4 Oct 2023 11:50:06 +0200 Subject: [PATCH 1/3] chore: Harden drone sample * missing CborSerializable in one place * missing restart supervision, especially important on the delivery queue singleton * refactoring of config * application-cluster instead of -kubernetes because we may run single node in kubernetes --- .../main/java/local/drones/ClusteredMain.java | 3 ++ .../java/local/drones/DeliveriesQueue.java | 15 +++++--- .../src/main/java/local/drones/Drone.java | 6 +++- ...bernetes.conf => application-cluster.conf} | 13 ++----- .../src/main/resources/application.conf | 30 +++++++++++----- .../src/main/resources/cluster.conf | 36 ++++++++++++------- .../src/main/resources/local-shared.conf | 2 +- .../{persistence.conf => persistence-h2.conf} | 0 .../main/resources/persistence-postgres.conf} | 0 ...bernetes.conf => application-cluster.conf} | 13 ++----- .../src/main/resources/application.conf | 30 +++++++++++----- .../src/main/resources/cluster.conf | 36 ++++++++++++------- .../src/main/resources/local-shared.conf | 2 +- .../{persistence.conf => persistence-h2.conf} | 0 .../main/resources/persistence-postgres.conf} | 4 ++- .../scala/local/drones/ClusteredMain.scala | 2 ++ .../scala/local/drones/DeliveriesQueue.scala | 24 ++++++++----- .../src/main/scala/local/drones/Drone.scala | 4 +++ .../deliveries/RestaurantDeliveries.java | 6 +++- .../src/main/java/central/drones/Drone.java | 6 +++- .../deliveries/RestaurantDeliveries.scala | 10 ++++-- .../src/main/scala/central/drones/Drone.scala | 27 +++++++++----- .../drones/DroneOverviewServiceImpl.scala | 15 ++++---- 23 files changed, 183 insertions(+), 101 deletions(-) rename samples/grpc/local-drone-control-java/src/main/resources/{application-kubernetes.conf => application-cluster.conf} (53%) rename samples/grpc/local-drone-control-java/src/main/resources/{persistence.conf => persistence-h2.conf} (100%) rename samples/grpc/{local-drone-control-scala/src/main/resources/postgres.conf => local-drone-control-java/src/main/resources/persistence-postgres.conf} (100%) rename samples/grpc/local-drone-control-scala/src/main/resources/{application-kubernetes.conf => application-cluster.conf} (53%) rename samples/grpc/local-drone-control-scala/src/main/resources/{persistence.conf => persistence-h2.conf} (100%) rename samples/grpc/{local-drone-control-java/src/main/resources/local-persistence.conf => local-drone-control-scala/src/main/resources/persistence-postgres.conf} (92%) diff --git a/samples/grpc/local-drone-control-java/src/main/java/local/drones/ClusteredMain.java b/samples/grpc/local-drone-control-java/src/main/java/local/drones/ClusteredMain.java index 87110f35b..48c5f73f8 100644 --- a/samples/grpc/local-drone-control-java/src/main/java/local/drones/ClusteredMain.java +++ b/samples/grpc/local-drone-control-java/src/main/java/local/drones/ClusteredMain.java @@ -13,6 +13,9 @@ * Main for starting the local-drone-control as a cluster rather than a single self-contained node. * Requires a separate database, start with config from local{1,2,3}.conf files for running as * cluster locally. + * + *

This should be started with -Dconfig.resource=application-cluster.conf or + * `-Dconfig.resource=local1.conf` */ public class ClusteredMain { diff --git a/samples/grpc/local-drone-control-java/src/main/java/local/drones/DeliveriesQueue.java b/samples/grpc/local-drone-control-java/src/main/java/local/drones/DeliveriesQueue.java index 2ab679c38..213733108 100644 --- a/samples/grpc/local-drone-control-java/src/main/java/local/drones/DeliveriesQueue.java +++ b/samples/grpc/local-drone-control-java/src/main/java/local/drones/DeliveriesQueue.java @@ -5,6 +5,7 @@ import akka.Done; import akka.actor.typed.ActorRef; import akka.actor.typed.Behavior; +import akka.actor.typed.SupervisorStrategy; import akka.actor.typed.javadsl.ActorContext; import akka.actor.typed.javadsl.Behaviors; import akka.cluster.sharding.typed.javadsl.EntityTypeKey; @@ -15,6 +16,7 @@ import akka.persistence.typed.state.javadsl.Effect; import akka.serialization.jackson.CborSerializable; import com.fasterxml.jackson.annotation.JsonCreator; +import java.time.Duration; import java.time.Instant; import java.util.*; @@ -151,15 +153,20 @@ public DeliveryInProgress(String deliveryId, String droneId, Instant pickupTime) EntityTypeKey.create(Command.class, "RestaurantDeliveries"); public static Behavior create() { - return Behaviors.setup( - context -> - new DeliveriesQueue(context, PersistenceId.of(EntityKey.name(), "DeliveriesQueue"))); + return Behaviors.supervise( + Behaviors.setup( + context -> + new DeliveriesQueue( + context, PersistenceId.of(EntityKey.name(), "DeliveriesQueue")))) + .onFailure(SupervisorStrategy.restart()); } private final ActorContext context; public DeliveriesQueue(ActorContext context, PersistenceId persistenceId) { - super(persistenceId); + super( + persistenceId, + SupervisorStrategy.restartWithBackoff(Duration.ofMillis(100), Duration.ofSeconds(5), 0.1)); this.context = context; } diff --git a/samples/grpc/local-drone-control-java/src/main/java/local/drones/Drone.java b/samples/grpc/local-drone-control-java/src/main/java/local/drones/Drone.java index b3003b34f..7e77e3c95 100644 --- a/samples/grpc/local-drone-control-java/src/main/java/local/drones/Drone.java +++ b/samples/grpc/local-drone-control-java/src/main/java/local/drones/Drone.java @@ -6,6 +6,7 @@ import akka.actor.typed.ActorRef; import akka.actor.typed.ActorSystem; import akka.actor.typed.Behavior; +import akka.actor.typed.SupervisorStrategy; import akka.cluster.sharding.typed.javadsl.ClusterSharding; import akka.cluster.sharding.typed.javadsl.Entity; import akka.cluster.sharding.typed.javadsl.EntityTypeKey; @@ -17,6 +18,7 @@ import akka.persistence.typed.javadsl.EventSourcedBehavior; import akka.serialization.jackson.CborSerializable; import com.fasterxml.jackson.annotation.JsonCreator; +import java.time.Duration; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -116,7 +118,9 @@ public static Behavior create(String entityId) { } private Drone(String entityId) { - super(PersistenceId.of(ENTITY_KEY.name(), entityId)); + super( + PersistenceId.of(ENTITY_KEY.name(), entityId), + SupervisorStrategy.restartWithBackoff(Duration.ofMillis(100), Duration.ofSeconds(5), 0.1)); } @Override diff --git a/samples/grpc/local-drone-control-java/src/main/resources/application-kubernetes.conf b/samples/grpc/local-drone-control-java/src/main/resources/application-cluster.conf similarity index 53% rename from samples/grpc/local-drone-control-java/src/main/resources/application-kubernetes.conf rename to samples/grpc/local-drone-control-java/src/main/resources/application-cluster.conf index d37646668..db6bf40f6 100644 --- a/samples/grpc/local-drone-control-java/src/main/resources/application-kubernetes.conf +++ b/samples/grpc/local-drone-control-java/src/main/resources/application-cluster.conf @@ -1,14 +1,10 @@ # Production configuration for running the local-drone-control service in Kubernetes, # as a multi-node cluster and with a separate PostgreSQL database. - include "cluster" include "grpc" -include "postgres" +include "persistence-postgres" local-drone-control { - # consider setting this to a specific interface for your environment - grpc.interface = "0.0.0.0" - grpc.interface = ${?GRPC_INTERFACE} nr-of-event-producers = 4 # unique identifier for the instance of local control, must be known up front by the cloud service @@ -18,9 +14,6 @@ local-drone-control { ask-timeout = 3s } -akka.management.cluster.bootstrap.contact-point-discovery { - service-name = "local-drone-control" - discovery-method = kubernetes-api - required-contact-point-nr = 1 - required-contact-point-nr = ${?REQUIRED_CONTACT_POINT_NR} +akka { + loglevel = DEBUG } diff --git a/samples/grpc/local-drone-control-java/src/main/resources/application.conf b/samples/grpc/local-drone-control-java/src/main/resources/application.conf index 7f901465b..eb303c774 100644 --- a/samples/grpc/local-drone-control-java/src/main/resources/application.conf +++ b/samples/grpc/local-drone-control-java/src/main/resources/application.conf @@ -1,14 +1,9 @@ -include "h2-default-projection-schema.conf" -include "grpc" -include "persistence" - # Default config, used for running a single-node cluster that cannot scale out to many nodes, using H2 for # persistence, started through local.drones.Main -akka { - actor.provider = cluster - loglevel = DEBUG -} +include "h2-default-projection-schema.conf" +include "grpc" +include "persistence-h2" local-drone-control { # unique identifier for the instance of local control, must be known up front by the cloud service @@ -17,3 +12,22 @@ local-drone-control { ask-timeout = 3s } + +akka { + loglevel = DEBUG + actor.provider = cluster +} + +akka.remote.artery { + # single node cluster + canonical.hostname = "127.0.0.1" + canonical.port = 2552 + canonical.port = ${?REMOTE_PORT} +} + +akka.cluster.sharding { + passivation { + strategy = default-strategy + active-entity-limit = 1000 + } +} diff --git a/samples/grpc/local-drone-control-java/src/main/resources/cluster.conf b/samples/grpc/local-drone-control-java/src/main/resources/cluster.conf index f9ca9e628..9e1d57973 100644 --- a/samples/grpc/local-drone-control-java/src/main/resources/cluster.conf +++ b/samples/grpc/local-drone-control-java/src/main/resources/cluster.conf @@ -1,24 +1,34 @@ akka { actor.provider = cluster +} - remote.artery { - canonical.port = 2552 - canonical.port = ${?REMOTE_PORT} - } +akka.remote.artery { + canonical.port = 2552 + canonical.port = ${?REMOTE_PORT} +} - cluster { - downing-provider-class = "akka.cluster.sbr.SplitBrainResolverProvider" +akka.cluster { + downing-provider-class = "akka.cluster.sbr.SplitBrainResolverProvider" - shutdown-after-unsuccessful-join-seed-nodes = 120s + shutdown-after-unsuccessful-join-seed-nodes = 120s - sharding { - least-shard-allocation-strategy.rebalance-absolute-limit = 20 - passivation.strategy = default-strategy + sharding { + least-shard-allocation-strategy.rebalance-absolute-limit = 20 + passivation { + strategy = default-strategy + active-entity-limit = 1000 } } +} + +akka.management { + http.port = 8558 + http.port = ${?HTTP_MGMT_PORT} - management { - http.port = 8558 - http.port = ${?HTTP_MGMT_PORT} + cluster.bootstrap.contact-point-discovery { + service-name = "local-drone-control" + discovery-method = kubernetes-api + required-contact-point-nr = 1 + required-contact-point-nr = ${?REQUIRED_CONTACT_POINT_NR} } } diff --git a/samples/grpc/local-drone-control-java/src/main/resources/local-shared.conf b/samples/grpc/local-drone-control-java/src/main/resources/local-shared.conf index fcfb1151c..c505cd51a 100644 --- a/samples/grpc/local-drone-control-java/src/main/resources/local-shared.conf +++ b/samples/grpc/local-drone-control-java/src/main/resources/local-shared.conf @@ -2,7 +2,7 @@ # through the local.drones.ClusteredMain and a separate PostgreSQL database include "cluster" include "grpc" -include "postgres" +include "persistence-postgres" local-drone-control.grpc.interface = "127.0.0.1" akka.remote.artery.canonical.hostname = "127.0.0.1" diff --git a/samples/grpc/local-drone-control-java/src/main/resources/persistence.conf b/samples/grpc/local-drone-control-java/src/main/resources/persistence-h2.conf similarity index 100% rename from samples/grpc/local-drone-control-java/src/main/resources/persistence.conf rename to samples/grpc/local-drone-control-java/src/main/resources/persistence-h2.conf diff --git a/samples/grpc/local-drone-control-scala/src/main/resources/postgres.conf b/samples/grpc/local-drone-control-java/src/main/resources/persistence-postgres.conf similarity index 100% rename from samples/grpc/local-drone-control-scala/src/main/resources/postgres.conf rename to samples/grpc/local-drone-control-java/src/main/resources/persistence-postgres.conf diff --git a/samples/grpc/local-drone-control-scala/src/main/resources/application-kubernetes.conf b/samples/grpc/local-drone-control-scala/src/main/resources/application-cluster.conf similarity index 53% rename from samples/grpc/local-drone-control-scala/src/main/resources/application-kubernetes.conf rename to samples/grpc/local-drone-control-scala/src/main/resources/application-cluster.conf index d37646668..db6bf40f6 100644 --- a/samples/grpc/local-drone-control-scala/src/main/resources/application-kubernetes.conf +++ b/samples/grpc/local-drone-control-scala/src/main/resources/application-cluster.conf @@ -1,14 +1,10 @@ # Production configuration for running the local-drone-control service in Kubernetes, # as a multi-node cluster and with a separate PostgreSQL database. - include "cluster" include "grpc" -include "postgres" +include "persistence-postgres" local-drone-control { - # consider setting this to a specific interface for your environment - grpc.interface = "0.0.0.0" - grpc.interface = ${?GRPC_INTERFACE} nr-of-event-producers = 4 # unique identifier for the instance of local control, must be known up front by the cloud service @@ -18,9 +14,6 @@ local-drone-control { ask-timeout = 3s } -akka.management.cluster.bootstrap.contact-point-discovery { - service-name = "local-drone-control" - discovery-method = kubernetes-api - required-contact-point-nr = 1 - required-contact-point-nr = ${?REQUIRED_CONTACT_POINT_NR} +akka { + loglevel = DEBUG } diff --git a/samples/grpc/local-drone-control-scala/src/main/resources/application.conf b/samples/grpc/local-drone-control-scala/src/main/resources/application.conf index 7f901465b..eb303c774 100644 --- a/samples/grpc/local-drone-control-scala/src/main/resources/application.conf +++ b/samples/grpc/local-drone-control-scala/src/main/resources/application.conf @@ -1,14 +1,9 @@ -include "h2-default-projection-schema.conf" -include "grpc" -include "persistence" - # Default config, used for running a single-node cluster that cannot scale out to many nodes, using H2 for # persistence, started through local.drones.Main -akka { - actor.provider = cluster - loglevel = DEBUG -} +include "h2-default-projection-schema.conf" +include "grpc" +include "persistence-h2" local-drone-control { # unique identifier for the instance of local control, must be known up front by the cloud service @@ -17,3 +12,22 @@ local-drone-control { ask-timeout = 3s } + +akka { + loglevel = DEBUG + actor.provider = cluster +} + +akka.remote.artery { + # single node cluster + canonical.hostname = "127.0.0.1" + canonical.port = 2552 + canonical.port = ${?REMOTE_PORT} +} + +akka.cluster.sharding { + passivation { + strategy = default-strategy + active-entity-limit = 1000 + } +} diff --git a/samples/grpc/local-drone-control-scala/src/main/resources/cluster.conf b/samples/grpc/local-drone-control-scala/src/main/resources/cluster.conf index f9ca9e628..9e1d57973 100644 --- a/samples/grpc/local-drone-control-scala/src/main/resources/cluster.conf +++ b/samples/grpc/local-drone-control-scala/src/main/resources/cluster.conf @@ -1,24 +1,34 @@ akka { actor.provider = cluster +} - remote.artery { - canonical.port = 2552 - canonical.port = ${?REMOTE_PORT} - } +akka.remote.artery { + canonical.port = 2552 + canonical.port = ${?REMOTE_PORT} +} - cluster { - downing-provider-class = "akka.cluster.sbr.SplitBrainResolverProvider" +akka.cluster { + downing-provider-class = "akka.cluster.sbr.SplitBrainResolverProvider" - shutdown-after-unsuccessful-join-seed-nodes = 120s + shutdown-after-unsuccessful-join-seed-nodes = 120s - sharding { - least-shard-allocation-strategy.rebalance-absolute-limit = 20 - passivation.strategy = default-strategy + sharding { + least-shard-allocation-strategy.rebalance-absolute-limit = 20 + passivation { + strategy = default-strategy + active-entity-limit = 1000 } } +} + +akka.management { + http.port = 8558 + http.port = ${?HTTP_MGMT_PORT} - management { - http.port = 8558 - http.port = ${?HTTP_MGMT_PORT} + cluster.bootstrap.contact-point-discovery { + service-name = "local-drone-control" + discovery-method = kubernetes-api + required-contact-point-nr = 1 + required-contact-point-nr = ${?REQUIRED_CONTACT_POINT_NR} } } diff --git a/samples/grpc/local-drone-control-scala/src/main/resources/local-shared.conf b/samples/grpc/local-drone-control-scala/src/main/resources/local-shared.conf index fcfb1151c..c505cd51a 100644 --- a/samples/grpc/local-drone-control-scala/src/main/resources/local-shared.conf +++ b/samples/grpc/local-drone-control-scala/src/main/resources/local-shared.conf @@ -2,7 +2,7 @@ # through the local.drones.ClusteredMain and a separate PostgreSQL database include "cluster" include "grpc" -include "postgres" +include "persistence-postgres" local-drone-control.grpc.interface = "127.0.0.1" akka.remote.artery.canonical.hostname = "127.0.0.1" diff --git a/samples/grpc/local-drone-control-scala/src/main/resources/persistence.conf b/samples/grpc/local-drone-control-scala/src/main/resources/persistence-h2.conf similarity index 100% rename from samples/grpc/local-drone-control-scala/src/main/resources/persistence.conf rename to samples/grpc/local-drone-control-scala/src/main/resources/persistence-h2.conf diff --git a/samples/grpc/local-drone-control-java/src/main/resources/local-persistence.conf b/samples/grpc/local-drone-control-scala/src/main/resources/persistence-postgres.conf similarity index 92% rename from samples/grpc/local-drone-control-java/src/main/resources/local-persistence.conf rename to samples/grpc/local-drone-control-scala/src/main/resources/persistence-postgres.conf index 1ccfe5e1e..694f3ee02 100644 --- a/samples/grpc/local-drone-control-java/src/main/resources/local-persistence.conf +++ b/samples/grpc/local-drone-control-scala/src/main/resources/persistence-postgres.conf @@ -20,7 +20,9 @@ akka { host = ${?DB_HOST} # note: different port for running in parallel with db for restaurant-drone-deliveries-service port = 5433 + port = ${?DB_PORT} database = "postgres" + database = ${?DB_DATABASE} user = "postgres" user = ${?DB_USER} password = "postgres" @@ -28,4 +30,4 @@ akka { } } } -} \ No newline at end of file +} diff --git a/samples/grpc/local-drone-control-scala/src/main/scala/local/drones/ClusteredMain.scala b/samples/grpc/local-drone-control-scala/src/main/scala/local/drones/ClusteredMain.scala index 76cd98da8..24ccc7b3c 100644 --- a/samples/grpc/local-drone-control-scala/src/main/scala/local/drones/ClusteredMain.scala +++ b/samples/grpc/local-drone-control-scala/src/main/scala/local/drones/ClusteredMain.scala @@ -9,6 +9,8 @@ import akka.management.scaladsl.AkkaManagement /** * Main for starting the local-drone-control as a cluster rather than a single self-contained node. Requires * a separate database, start with config from local{1,2,3}.conf files for running as cluster locally. + * + * This should be started with -Dconfig.resource=application-cluster.conf or `-Dconfig.resource=local1.conf` */ object ClusteredMain { diff --git a/samples/grpc/local-drone-control-scala/src/main/scala/local/drones/DeliveriesQueue.scala b/samples/grpc/local-drone-control-scala/src/main/scala/local/drones/DeliveriesQueue.scala index b677be729..e38dca1ef 100644 --- a/samples/grpc/local-drone-control-scala/src/main/scala/local/drones/DeliveriesQueue.scala +++ b/samples/grpc/local-drone-control-scala/src/main/scala/local/drones/DeliveriesQueue.scala @@ -1,5 +1,6 @@ package local.drones +import scala.concurrent.duration._ import akka.Done import akka.actor.typed.scaladsl.{ ActorContext, Behaviors } import akka.actor.typed.{ ActorRef, Behavior } @@ -8,9 +9,10 @@ import akka.pattern.StatusReply import akka.persistence.typed.PersistenceId import akka.persistence.typed.state.scaladsl.{ DurableStateBehavior, Effect } import akka.serialization.jackson.CborSerializable - import java.time.Instant +import akka.actor.typed.SupervisorStrategy + object DeliveriesQueue { // #commands @@ -40,7 +42,8 @@ object DeliveriesQueue { final case class WaitingDelivery( deliveryId: String, from: Coordinates, - to: Coordinates) extends CborSerializable + to: Coordinates) + extends CborSerializable final case class DeliveryInProgress( deliveryId: String, @@ -57,12 +60,17 @@ object DeliveriesQueue { val EntityKey = EntityTypeKey("RestaurantDeliveries") def apply(): Behavior[Command] = { - Behaviors.setup { context => - DurableStateBehavior[Command, State]( - PersistenceId(EntityKey.name, "DeliveriesQueue"), - State(Vector.empty, Vector.empty), - onCommand(context)) - } + Behaviors + .supervise[Command] { + Behaviors.setup { context => + DurableStateBehavior[Command, State]( + PersistenceId(EntityKey.name, "DeliveriesQueue"), + State(Vector.empty, Vector.empty), + onCommand(context)).onPersistFailure( + SupervisorStrategy.restartWithBackoff(100.millis, 5.seconds, 0.1)) + } + } + .onFailure(SupervisorStrategy.restart) } // #commandHandler diff --git a/samples/grpc/local-drone-control-scala/src/main/scala/local/drones/Drone.scala b/samples/grpc/local-drone-control-scala/src/main/scala/local/drones/Drone.scala index dc2dcbaab..00623c60a 100644 --- a/samples/grpc/local-drone-control-scala/src/main/scala/local/drones/Drone.scala +++ b/samples/grpc/local-drone-control-scala/src/main/scala/local/drones/Drone.scala @@ -1,9 +1,12 @@ package local.drones +import scala.concurrent.duration._ import akka.Done import akka.actor.typed.ActorRef import akka.actor.typed.ActorSystem import akka.actor.typed.Behavior +import akka.actor.typed.SupervisorStrategy +import akka.actor.typed.scaladsl.Behaviors import akka.cluster.sharding.typed.scaladsl.ClusterSharding import akka.cluster.sharding.typed.scaladsl.Entity import akka.cluster.sharding.typed.scaladsl.EntityTypeKey @@ -84,6 +87,7 @@ object Drone { emptyState, handleCommand, handleEvent) + .onPersistFailure(SupervisorStrategy.restartWithBackoff(100.millis, 5.seconds, 0.1)) // #commandHandler private def handleCommand( diff --git a/samples/grpc/restaurant-drone-deliveries-service-java/src/main/java/central/deliveries/RestaurantDeliveries.java b/samples/grpc/restaurant-drone-deliveries-service-java/src/main/java/central/deliveries/RestaurantDeliveries.java index 0948bd08a..4ea71ccdd 100644 --- a/samples/grpc/restaurant-drone-deliveries-service-java/src/main/java/central/deliveries/RestaurantDeliveries.java +++ b/samples/grpc/restaurant-drone-deliveries-service-java/src/main/java/central/deliveries/RestaurantDeliveries.java @@ -3,6 +3,7 @@ import akka.Done; import akka.actor.typed.ActorRef; import akka.actor.typed.ActorSystem; +import akka.actor.typed.SupervisorStrategy; import akka.cluster.sharding.typed.javadsl.ClusterSharding; import akka.cluster.sharding.typed.javadsl.Entity; import akka.cluster.sharding.typed.javadsl.EntityTypeKey; @@ -15,6 +16,7 @@ import akka.serialization.jackson.CborSerializable; import central.Coordinates; import com.fasterxml.jackson.annotation.JsonCreator; +import java.time.Duration; import java.time.Instant; import java.util.ArrayList; import java.util.Collections; @@ -155,7 +157,9 @@ public Set tagsFor(State state, Event event) { } public RestaurantDeliveries(String restaurantId) { - super(PersistenceId.of(ENTITY_KEY.name(), restaurantId)); + super( + PersistenceId.of(ENTITY_KEY.name(), restaurantId), + SupervisorStrategy.restartWithBackoff(Duration.ofMillis(100), Duration.ofSeconds(5), 0.1)); } @Override diff --git a/samples/grpc/restaurant-drone-deliveries-service-java/src/main/java/central/drones/Drone.java b/samples/grpc/restaurant-drone-deliveries-service-java/src/main/java/central/drones/Drone.java index 4b0862b53..1d3b2869c 100644 --- a/samples/grpc/restaurant-drone-deliveries-service-java/src/main/java/central/drones/Drone.java +++ b/samples/grpc/restaurant-drone-deliveries-service-java/src/main/java/central/drones/Drone.java @@ -3,6 +3,7 @@ import akka.Done; import akka.actor.typed.ActorRef; import akka.actor.typed.ActorSystem; +import akka.actor.typed.SupervisorStrategy; import akka.actor.typed.javadsl.ActorContext; import akka.actor.typed.javadsl.Behaviors; import akka.cluster.sharding.typed.javadsl.ClusterSharding; @@ -16,6 +17,7 @@ import akka.serialization.jackson.CborSerializable; import central.CoarseGrainedCoordinates; import com.fasterxml.jackson.annotation.JsonCreator; +import java.time.Duration; import java.time.Instant; import java.util.Optional; @@ -83,7 +85,9 @@ public static void init(ActorSystem system) { private final ActorContext context; private Drone(ActorContext context, String entityId) { - super(PersistenceId.of(ENTITY_KEY.name(), entityId)); + super( + PersistenceId.of(ENTITY_KEY.name(), entityId), + SupervisorStrategy.restartWithBackoff(Duration.ofMillis(100), Duration.ofSeconds(5), 0.1)); this.context = context; } diff --git a/samples/grpc/restaurant-drone-deliveries-service-scala/src/main/scala/central/deliveries/RestaurantDeliveries.scala b/samples/grpc/restaurant-drone-deliveries-service-scala/src/main/scala/central/deliveries/RestaurantDeliveries.scala index 7deb0c2c2..7180163b5 100644 --- a/samples/grpc/restaurant-drone-deliveries-service-scala/src/main/scala/central/deliveries/RestaurantDeliveries.scala +++ b/samples/grpc/restaurant-drone-deliveries-service-scala/src/main/scala/central/deliveries/RestaurantDeliveries.scala @@ -1,9 +1,15 @@ package central.deliveries +import java.time.Instant + +import scala.concurrent.duration._ + import akka.Done import akka.actor.typed.ActorRef import akka.actor.typed.ActorSystem import akka.actor.typed.Behavior +import akka.actor.typed.SupervisorStrategy +import akka.actor.typed.scaladsl.Behaviors import akka.cluster.sharding.typed.scaladsl.ClusterSharding import akka.cluster.sharding.typed.scaladsl.Entity import akka.cluster.sharding.typed.scaladsl.EntityTypeKey @@ -14,8 +20,6 @@ import akka.persistence.typed.scaladsl.EventSourcedBehavior import akka.serialization.jackson.CborSerializable import central.Coordinates -import java.time.Instant - /** * Keeps track of registered deliveries for per restaurant */ @@ -84,7 +88,7 @@ object RestaurantDeliveries { // picks them up for drone delivery Set("t:" + state.localControlLocationId) case _ => Set.empty - } + }.onPersistFailure(SupervisorStrategy.restartWithBackoff(100.millis, 5.seconds, 0.1)) // #commandHandler private def onCommand( diff --git a/samples/grpc/restaurant-drone-deliveries-service-scala/src/main/scala/central/drones/Drone.scala b/samples/grpc/restaurant-drone-deliveries-service-scala/src/main/scala/central/drones/Drone.scala index 933924f58..ee65930f7 100644 --- a/samples/grpc/restaurant-drone-deliveries-service-scala/src/main/scala/central/drones/Drone.scala +++ b/samples/grpc/restaurant-drone-deliveries-service-scala/src/main/scala/central/drones/Drone.scala @@ -1,19 +1,27 @@ package central.drones +import java.time.Instant + +import scala.concurrent.duration._ + import akka.Done +import akka.actor.typed.SupervisorStrategy import akka.actor.typed.scaladsl.ActorContext import akka.actor.typed.scaladsl.Behaviors -import akka.actor.typed.{ActorRef, ActorSystem, Behavior} -import akka.cluster.sharding.typed.scaladsl.{ClusterSharding, Entity, EntityTypeKey} +import akka.actor.typed.ActorRef +import akka.actor.typed.ActorSystem +import akka.actor.typed.Behavior +import akka.cluster.sharding.typed.scaladsl.ClusterSharding +import akka.cluster.sharding.typed.scaladsl.Entity +import akka.cluster.sharding.typed.scaladsl.EntityTypeKey import akka.pattern.StatusReply import akka.persistence.r2dbc.state.scaladsl.AdditionalColumn import akka.persistence.typed.PersistenceId -import akka.persistence.typed.state.scaladsl.{DurableStateBehavior, Effect} +import akka.persistence.typed.state.scaladsl.DurableStateBehavior +import akka.persistence.typed.state.scaladsl.Effect import akka.serialization.jackson.CborSerializable import central.CoarseGrainedCoordinates -import java.time.Instant - /** * Durable state entity keeping an overview state of where the drone is and its state, but not the full detail, * that stays in the local control service. @@ -21,7 +29,7 @@ import java.time.Instant object Drone { // #commands - sealed trait Command + sealed trait Command extends CborSerializable final case class UpdateLocation( locationName: String, @@ -56,7 +64,8 @@ object Drone { DurableStateBehavior( PersistenceId(EntityKey.name, droneId), emptyState, - onCommand(context)) + onCommand(context)).onPersistFailure( + SupervisorStrategy.restartWithBackoff(100.millis, 5.seconds, 0.1)) } } @@ -77,8 +86,8 @@ object Drone { currentLocation = Some(coordinates))) .thenReply(replyTo)(_ => StatusReply.ack()) - case GetState(replyTo) => - Effect.reply(replyTo)(state) + case GetState(replyTo) => + Effect.reply(replyTo)(state) } // #commandHandler diff --git a/samples/grpc/restaurant-drone-deliveries-service-scala/src/main/scala/central/drones/DroneOverviewServiceImpl.scala b/samples/grpc/restaurant-drone-deliveries-service-scala/src/main/scala/central/drones/DroneOverviewServiceImpl.scala index 33c4568bb..2bdd7f457 100644 --- a/samples/grpc/restaurant-drone-deliveries-service-scala/src/main/scala/central/drones/DroneOverviewServiceImpl.scala +++ b/samples/grpc/restaurant-drone-deliveries-service-scala/src/main/scala/central/drones/DroneOverviewServiceImpl.scala @@ -1,5 +1,8 @@ package central.drones +import scala.concurrent.ExecutionContext +import scala.concurrent.Future + import akka.actor.typed.ActorSystem import akka.cluster.sharding.typed.scaladsl.ClusterSharding import akka.grpc.GrpcServiceException @@ -8,18 +11,12 @@ import akka.persistence.typed.PersistenceId import akka.serialization.SerializationExtension import akka.util.Timeout import central.DeliveriesSettings -import central.drones.proto.{ - DroneOverviewService, - GetDroneOverviewRequest, - GetDroneOverviewResponse -} +import central.drones.proto.DroneOverviewService +import central.drones.proto.GetDroneOverviewRequest +import central.drones.proto.GetDroneOverviewResponse import io.grpc.Status import org.slf4j.LoggerFactory -import scala.concurrent.ExecutionContext -import scala.concurrent.Future -import scala.jdk.DurationConverters.JavaDurationOps - class DroneOverviewServiceImpl( system: ActorSystem[_], settings: DeliveriesSettings) From 2b052bc74f510aeedff1c69bee31247fbb70bcc5 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Wed, 4 Oct 2023 14:29:44 +0200 Subject: [PATCH 2/3] port 0 (random) for single node local-drone-control * easy way to avoid port conflicts when running more than one locally --- .../src/main/resources/application.conf | 2 +- .../src/main/resources/application.conf | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/samples/grpc/local-drone-control-java/src/main/resources/application.conf b/samples/grpc/local-drone-control-java/src/main/resources/application.conf index eb303c774..4ba6bf384 100644 --- a/samples/grpc/local-drone-control-java/src/main/resources/application.conf +++ b/samples/grpc/local-drone-control-java/src/main/resources/application.conf @@ -21,7 +21,7 @@ akka { akka.remote.artery { # single node cluster canonical.hostname = "127.0.0.1" - canonical.port = 2552 + canonical.port = 0 canonical.port = ${?REMOTE_PORT} } diff --git a/samples/grpc/local-drone-control-scala/src/main/resources/application.conf b/samples/grpc/local-drone-control-scala/src/main/resources/application.conf index eb303c774..4ba6bf384 100644 --- a/samples/grpc/local-drone-control-scala/src/main/resources/application.conf +++ b/samples/grpc/local-drone-control-scala/src/main/resources/application.conf @@ -21,7 +21,7 @@ akka { akka.remote.artery { # single node cluster canonical.hostname = "127.0.0.1" - canonical.port = 2552 + canonical.port = 0 canonical.port = ${?REMOTE_PORT} } From b94a6393258fcc94e5072787bccddbb71e582ebb Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Wed, 4 Oct 2023 15:09:31 +0200 Subject: [PATCH 3/3] persistence-h2.conf in docs --- .../src/main/paradox/guide/1-local-drone-control-service.md | 4 ++-- .../main/paradox/guide/2-drone-location-to-cloud-service.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/akka-edge-docs/src/main/paradox/guide/1-local-drone-control-service.md b/akka-edge-docs/src/main/paradox/guide/1-local-drone-control-service.md index ce79432e3..1225195d4 100644 --- a/akka-edge-docs/src/main/paradox/guide/1-local-drone-control-service.md +++ b/akka-edge-docs/src/main/paradox/guide/1-local-drone-control-service.md @@ -100,10 +100,10 @@ It is of course also possible to instead use a separate standalone database such Config to use H2 looks like this: Scala -: @@snip [persistence.conf](/samples/grpc/local-drone-control-scala/src/main/resources/persistence.conf) { } +: @@snip [persistence-h2.conf](/samples/grpc/local-drone-control-scala/src/main/resources/persistence-h2.conf) { } Java -: @@snip [persistence.conf](/samples/grpc/local-drone-control-java/src/main/resources/persistence.conf) { } +: @@snip [persistence-h2.conf](/samples/grpc/local-drone-control-java/src/main/resources/persistence-h2.conf) { } In addition to the configuration, the following additional dependencies are needed in the project build: diff --git a/akka-edge-docs/src/main/paradox/guide/2-drone-location-to-cloud-service.md b/akka-edge-docs/src/main/paradox/guide/2-drone-location-to-cloud-service.md index 9cae370f6..cc5a6b167 100644 --- a/akka-edge-docs/src/main/paradox/guide/2-drone-location-to-cloud-service.md +++ b/akka-edge-docs/src/main/paradox/guide/2-drone-location-to-cloud-service.md @@ -315,4 +315,4 @@ grpcurl -d '{"drone_id":"drone1"}' -plaintext localhost:8101 central.drones.Dron ## What's next? -* Accept restaurant delivery orders in the restaurant-drone-deliveries-service \ No newline at end of file +* Accept restaurant delivery orders in the restaurant-drone-deliveries-service