You need to have a current Lightbend Platform Subscription because this project is taking advantage of Lightbend Telemetry as well as Akka Resilience Enhancements:
- Start Telemetry (Cinnamon) Elasticsearch Sandbox in Docker:
- switch to the local Cinnamon elastic search directory
- issue command: docker-compose up
-
Start a local Cassandra version in Docker: docker-compose -f docker-compose-cassandra.yml up
-
To start the entity cluster: sbt '; set javaOptions += "-Dconfig.resource=cluster-application.conf" ; run'
-
To start the HTTP server: sbt '; set javaOptions += "-Dconfig.resource=endpoint-application.conf" ; run'
-
Wait until the cluster issues "Welcome" to http server, or you see [Up] for the new node. Then all is ready.
curl -d '{"artifactId":1, "userId":"Michael"}' -H "Content-Type: application/json" -X POST http://localhost:8082/artifactState/setArtifactReadByUser
curl -d '{"artifactId":1, "userId":"Michael"}' -H "Content-Type: application/json" -X POST http://localhost:8082/artifactState/isArtifactReadByUser
curl -d '{"artifactId":1, "userId":"Michael"}' -H "Content-Type: application/json" -X POST http://localhost:8082/artifactState/setArtifactAddedToUserFeed
curl -d '{"artifactId":1, "userId":"Michael"}' -H "Content-Type: application/json" -X POST http://localhost:8082/artifactState/isArtifactInUserFeed
curl -d '{"artifactId":1, "userId":"Michael"}' -H "Content-Type: application/json" -X POST http://localhost:8082/artifactState/setArtifactRemovedFromUserFeed
curl -d '{"artifactId":1, "userId":"Michael"}' -H "Content-Type: application/json" -X POST http://localhost:8082/artifactState/getAllStates
curl 'http://localhost:8082/artifactState/getAllStates?artifactId=1&userId=Michael'
https://github.com/fullstorydev/grpcurl
grpcurl -plaintext localhost:8082 list
grpcurl -plaintext -d '{"artifactId":1, "userId":"Michael"}' localhost:8082 ArtifactStateService/SetArtifactReadByUser
grpcurl -plaintext -d '{"artifactId":1, "userId":"Michael"}' localhost:8082 ArtifactStateService/IsArtifactReadByUser
grpcurl -plaintext -d '{"artifactId":1, "userId":"Michael"}' localhost:8082 ArtifactStateService/SetArtifactAddedToUserFeed
grpcurl -plaintext -d '{"artifactId":1, "userId":"Michael"}' localhost:8082 ArtifactStateService/IsArtifactInUserFeed
grpcurl -plaintext -d '{"artifactId":1, "userId":"Michael"}' localhost:8082 ArtifactStateService/SetArtifactRemovedFromUserFeed
grpcurl -plaintext -d '{"artifactId":1, "userId":"Michael"}' localhost:8082 ArtifactStateService/GetAllStates
Testing relies on multi-jvm, and Cassandra for testing of internal cluster api, as well as HTTP end-to-end integration.
From within sbt issue the following command:
multi-jvm:test
These steps create two docker images: akka-typed-state-poc/endpoint, and akka-typed-state-poc/cluster. When run, the endpoint is exposed on localhost:8082 so all the examples above should run properly once the cluster has formed.
Note: this example in Docker is running Cassandra in a local container with default config.
- Run sbt in the project's root directory.
- Build docker images by issuing:
docker:publishLocal
- In a terminal, from the project's root directory, issue the command:
docker-compose up
- The
endpoint
should be available on thehttp://localhost:8082
as it is when running locally.
- From a terminal window, enter:
docker exec -it akka-typed-persistent-state-poc_cassandra_db_1 sh
- From the command prompt, enter:
cqlsh
- In CQLSH, enter:
use akka;
- To see the layout of the messages table, enter:
describe messages;
- To dump message events, enter:
select * from messages;
TODO
Following is a break down of the Pros / Cons of two approaches to solving the problem of building a distributed cache:
- Akka Cluster Sharding w/ Persistent Entities
- Microservice w/ database persistence (without Actor system)
Lightbend believes in it's Akka Framework for building reactive microservices that meet the promise of the Reactive Manafesto. Reactive microservices have the following qualities: Responsive, Resilient, Elastic, and Message Driven.
Akka Cluster provides a fault-tolerant decentralized peer-to-peer based cluster membership service with no single point of failure or single point of bottleneck. It does this using gossip protocols and an automatic failure detector.
Akka's Cluster allows for building distributed applications, where one application or service spans multiple nodes (in practice multiple ActorSystems). See also the discussion in When and where to use Akka Cluster.
Akka's Cluster Sharding leverages the features Akka Cluster for distributed computing while enabling simple to code persistent entities through CQRS / ES. Cluster sharding is useful when you need to distribute actors across several nodes in the cluster and want to be able to interact with them using their logical identifier, but without having to care about their physical location in the cluster, which might also change over time.
- easy to code and maintain
Actors provide:
- Easy to Scale
- Fault Tolerant
- Geographical Distribution
- No Shared State
Akka cluster / sharding provides:
- No single point of failure
- scalability for distribution and persistence of state
- easy projection of events over time to other systems for use cases such as metrics collection
- introduces new concepts that may not be well known
Actors can be:
- susceptible to overflowing mail boxes
Akka cluster / sharding:
- can be difficult for DevOps to deploy
- complete stop / restart when changing configuration of sharding
Building a distributed, highly scalable cache with a persistent backing and recovery is difficult to do.
- uses concepts that are probably well known
- hard to scale while distributing state
- potential for bottlenecks if not distributed
- code must be created to save state
- code must be created to recover state
For deployment to an AWS / ECS production environment we recommend Akka Bootstrap. For an example of the configuration required for the PoC in code on AWS / ECS using Akka Bootstrap please see this Github repository.
Warning: If you’re extending application.conf, please make sure your new configuration file sets akka.cluster.seed-nodes to null as this setting conflicts with Akka Bootstrap. If your configuration is completely in code, then akka.cluster.seed-nodes should not be set at all.
- grpcurl -plaintext localhost:8082 list
- grpcurl -plaintext -d '{"artifactId":1, "userId":"Michael"}' localhost:8082 ArtifactStateService/SetArtifactReadByUser
- grpcurl -plaintext -d '{"artifactId":1, "userId":"Michael"}' localhost:8082 ArtifactStateService/SetArtifactAddedToUserFeed
- grpcurl -plaintext -d '{"artifactId":1, "userId":"Michael"}' localhost:8082 ArtifactStateService/SetArtifactRemovedFromUserFeed
- grpcurl -plaintext -d '{"artifactId":1, "userId":"Michael"}' localhost:8082 ArtifactStateService/IsArtifactReadByUser
- grpcurl -plaintext -d '{"artifactId":1, "userId":"Michael"}' localhost:8082 ArtifactStateService/IsArtifactInUserFeed
- grpcurl -plaintext -d '{"artifactId":1, "userId":"Michael"}' localhost:8082 ArtifactStateService/GetAllStates