Add more docs on durable state (#30403)

* Add more docs on durable state and Changes for consistency across event sourcing and durable state documents

* Fixed paradox formatting error

* PR review feedbacks

* Update akka-docs/src/main/paradox/typed/persistence-durable-state.md

* Update akka-docs/src/main/paradox/typed/persistence-durable-state.md

* Added some examples to docs how to get the DurableStateStoreQuery.

* Added some examples to docs how to get the DurableStateStoreQuery.

* Formatting.

* Added example how to get changes from DurableStateStoreQuery.

Co-authored-by: Raymond Roestenburg <raymond.roestenburg@gmail.com>
This commit is contained in:
Debasish Ghosh 2021-07-28 10:21:23 +05:30 committed by GitHub
parent b98c3b2da6
commit 8e932ede37
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 131 additions and 19 deletions

View file

@ -0,0 +1,29 @@
/*
* Copyright (C) 2021 Lightbend Inc. <https://www.lightbend.com>
*/
package jdocs.akka.cluster.sharding.typed;
import akka.NotUsed;
import akka.actor.ActorSystem;
import akka.stream.javadsl.Source;
import akka.persistence.query.DurableStateChange;
import akka.persistence.query.Offset;
import akka.persistence.query.javadsl.DurableStateStoreQuery;
import akka.persistence.state.DurableStateStoreRegistry;
import akka.persistence.state.javadsl.*;
class DurableStateStoreQueryUsageCompileOnlySpec {
public <Record> DurableStateStoreQuery<Record> getQuery(
ActorSystem system, String pluginId, Offset offset) {
// #get-durable-state-store-query-example
DurableStateStoreQuery<Record> durableStateStoreQuery =
DurableStateStoreRegistry.get(system)
.getDurableStateStoreFor(DurableStateStoreQuery.class, pluginId);
Source<DurableStateChange<Record>, NotUsed> source =
durableStateStoreQuery.changes("tag", offset);
// #get-durable-state-store-query-example
return durableStateStoreQuery;
}
}

View file

@ -0,0 +1,25 @@
/*
* Copyright (C) 2019-2021 Lightbend Inc. <https://www.lightbend.com>
*/
package docs.akka.cluster.sharding.typed
import scala.annotation.nowarn
import akka.NotUsed
import akka.stream.scaladsl.Source
import akka.actor.ActorSystem
import akka.persistence.query.DurableStateChange
import akka.persistence.query.Offset
import akka.persistence.query.scaladsl.DurableStateStoreQuery
import akka.persistence.state.DurableStateStoreRegistry
@nowarn
object DurableStateStoreQueryUsageCompileOnlySpec {
def getQuery[Record](system: ActorSystem, pluginId: String, offset: Offset) = {
//#get-durable-state-store-query-example
val durableStateStoreQuery =
DurableStateStoreRegistry(system).durableStateStoreFor[DurableStateStoreQuery[Record]](pluginId)
val source: Source[DurableStateChange[Record], NotUsed] = durableStateStoreQuery.changes("tag", offset)
//#get-durable-state-store-query-example
}
}

View file

@ -0,0 +1,45 @@
---
project.description: Query side to Akka Persistence allowing for building CQRS applications using durable state.
---
# Persistence Query
## Dependency
To use Persistence Query, you must add the following dependency in your project:
@@dependency[sbt,Maven,Gradle] {
bomGroup=com.typesafe.akka bomArtifact=akka-bom_$scala.binary.version$ bomVersionSymbols=AkkaVersion
symbol1=AkkaVersion
value1="$akka.version$"
group=com.typesafe.akka
artifact=akka-persistence-query_$scala.binary.version$
version=AkkaVersion
}
This will also add dependency on the @ref[Akka Persistence](persistence.md) module.
## Introduction
Akka persistence query provides a query interface to @ref:[Durable State Behaviors](typed/persistence-durable-state.md).
These queries are based on asynchronous streams. These streams are similar to the ones offered in the @ref:[Event Sourcing](persistence-query.md)
based implementation. Various state store plugins can implement these interfaces to expose their query capabilities.
One of the rationales behind having a separate query module for Akka Persistence is for implementing the so-called
query side or read side in the popular CQRS architecture pattern - in which the writing side of the
application implemented using Akka persistence, is completely separated from the query side.
## Using query with Akka Projections
Akka Persistence and Akka Projections together can be used to develop a CQRS application. In the application the
durable state is stored in a database and fetched as an asynchronous stream to the user. Currently queries on
durable state, provided by the `DurableStateStoreQuery` interface, is used to implement tag based searches in
Akka Projections.
The example below shows how to get the `DurableStateStoreQuery` from the DurableStateStoreRegistry extension.
Scala
: @@snip [DurableStateStoreQueryUsageCompileOnlySpec.scala](/akka-cluster-sharding-typed/src/test/scala/docs/akka/cluster/sharding/typed/DurableStateStoreQueryUsageCompileOnlySpec.scala) { #get-durable-state-store-query-example }
Java
: @@snip [DurableStateStoreQueryUsageCompileOnlyTest.java](/akka-cluster-sharding-typed/src/test/java/jdocs/akka/cluster/sharding/typed/DurableStateStoreQueryUsageCompileOnlyTest.java) { #get-durable-state-store-query-example }

View file

@ -30,6 +30,9 @@ side of an application, however it can help to migrate data from the write side
simple scenarios Persistence Query may be powerful enough to fulfill the query needs of your app, however we highly
recommend (in the spirit of CQRS) of splitting up the write/read sides into separate datastores as the need arises.
For a similar implementation of query interface to @ref:[Durable State Behaviors](typed/persistence-durable-state.md)
please refer to @ref:[Persistence Query using Durable State](persistence-query-durable-state.md).
The @extref[Microservices with Akka tutorial](platform-guide:microservices-tutorial/) explains how to
implement an Event Sourced CQRS application with Akka Persistence and Akka Projections.

View file

@ -0,0 +1,6 @@
# CQRS
@ref:[DurableStateBehavior](persistence-durable-state.md)s along with [Akka Projections](https://doc.akka.io/docs/akka-projection/current/)
can be used to implement Command Query Responsibility Segregation (CQRS). For implementing CQRS using @ref:[EventSourcedBehavior](persistence.md), please take a look at the corresponding @ref:[CQRS](cqrs.md) documentation.

View file

@ -2,6 +2,5 @@
@ref:[EventSourcedBehavior](persistence.md)s along with [Akka Projections](https://doc.akka.io/docs/akka-projection/current/)
can be used to implement Command Query Responsibility Segregation (CQRS). The @extref[Microservices with Akka tutorial](platform-guide:microservices-tutorial/)
explains how to use Event Sourcing and Projections together.
explains how to use Event Sourcing and Projections together. For implementing CQRS using @ref:[DurableStateBehavior](persistence-durable-state.md), please take a look at the corresponding @ref:[CQRS](cqrs-durable-state.md) documentation.

View file

@ -10,5 +10,7 @@ project.description: Durable state with Akka Persistence enables actors to persi
* [persistence-durable-state](persistence-durable-state.md)
* [persistence-style](persistence-style-durable-state.md)
* [cqrs](cqrs-durable-state.md)
* [persistence-query](../persistence-query-durable-state.md)
@@@

View file

@ -35,6 +35,9 @@ This model of Akka Persistence enables a stateful actor / entity to store the fu
The current state is always stored in the database. Since only the latest state is stored, we don't have access to any of the history of changes, unlike event sourced storage. Akka Persistence would read that state and store it in memory. After processing of the command is finished, the new state will be stored in the database. The processing of the next command will not start until the state has been successfully stored in the database.
Akka Persistence also supports @ref:[Event Sourcing](persistence.md) based implementation, where only the _events_ that are persisted by the actor are stored, but not the actual state of the actor. By storing all events, using this model,
a stateful actor can be recovered by replaying the stored events to the actor, which allows it to rebuild its state.
The database specific implementations can be added to existing Akka Persistence plugin implementations, starting with the JDBC plugin. The plugin would serialize the state and store as a blob with the persistenceId as the primary key.
## Example and core API
@ -64,7 +67,9 @@ The @apidoc[akka.persistence.typed.PersistenceId] is the stable unique identifie
durabe state store.
@ref:[Cluster Sharding](cluster-sharding.md) is typically used together with `DurableStateBehavior` to ensure
that there is only one active entity for each `PersistenceId` (`entityId`).
that there is only one active entity for each `PersistenceId` (`entityId`). There are techniques to ensure this
uniqueness, an example of which can be found in the
@ref:[Persistence example in the Cluster Sharding documentation](cluster-sharding.md#persistence-example). This illustrates how to construct the `PersistenceId` from the `entityTypeKey` and `entityId` provided by the `EntityContext`.
The `entityId` in Cluster Sharding is the business domain identifier which uniquely identifies the instance of
that specific `EntityType`. This means that across the cluster we have a unique combination of (`EntityType`, `EntityId`).
@ -77,10 +82,6 @@ to help with constructing such `PersistenceId` from an `entityTypeHint` and `ent
The default separator when concatenating the `entityTypeHint` and `entityId` is `|`, but a custom separator
is supported.
The @ref:[Persistence example in the Cluster Sharding documentation](cluster-sharding.md#persistence-example)
illustrates how to construct the `PersistenceId` from the `entityTypeKey` and `entityId` provided by the
`EntityContext`.
A custom identifier can be created with `PersistenceId.ofUniqueId`.
### Command handler
@ -92,7 +93,7 @@ Effects are created using @java[a factory that is returned via the `Effect()` me
The two most commonly used effects are:
* `persist` will persist state atomically, i.e. all state will be stored or none of them are stored if there is an error
* `persist` will persist the latest value of the state. No history of state changes will be stored
* `none` no state to be persisted, for example a read-only command
More effects are explained in @ref:[Effects and Side Effects](#effects-and-side-effects).

View file

@ -32,10 +32,14 @@ You also have to select journal plugin and optionally snapshot store plugin, see
Akka Persistence enables stateful actors to persist their state so that it can be recovered when an actor
is either restarted, such as after a JVM crash, by a supervisor or a manual stop-start, or migrated within a cluster. The key concept behind Akka
Persistence is that only the _events_ that are persisted by the actor are stored, not the actual state of the actor
(though actor state snapshot support is also available). The events are persisted by appending to storage (nothing is ever mutated) which
(although actor state snapshot support is available). The events are persisted by appending to storage (nothing is ever mutated) which
allows for very high transaction rates and efficient replication. A stateful actor is recovered by replaying the stored
events to the actor, allowing it to rebuild its state. This can be either the full history of changes
or starting from a checkpoint in a snapshot which can dramatically reduce recovery times.
or starting from a checkpoint in a snapshot, which can dramatically reduce recovery times.
Akka Persistence also supports @ref:[Durable State Behaviors](persistence-durable-state.md), which is based on
persistence of the latest state of the actor. In this implementation, the _latest_ state is persisted, instead of events.
Hence this is more similar to CRUD based applications.
The [Event Sourcing with Akka 2.6 video](https://akka.io/blog/news/2020/01/07/akka-event-sourcing-video)
is a good starting point for learning Event Sourcing, together with the @extref[Microservices with Akka tutorial](platform-guide:microservices-tutorial/)
@ -107,7 +111,9 @@ The @apidoc[akka.persistence.typed.PersistenceId] is the stable unique identifie
event journal and snapshot store.
@ref:[Cluster Sharding](cluster-sharding.md) is typically used together with `EventSourcedBehavior` to ensure
that there is only one active entity for each `PersistenceId` (`entityId`).
that there is only one active entity for each `PersistenceId` (`entityId`). There are techniques to ensure this
uniqueness, an example of which can be found in the
@ref:[Persistence example in the Cluster Sharding documentation](cluster-sharding.md#persistence-example). This illustrates how to construct the `PersistenceId` from the `entityTypeKey` and `entityId` provided by the `EntityContext`.
The `entityId` in Cluster Sharding is the business domain identifier of the entity. The `entityId` might not
be unique enough to be used as the `PersistenceId` by itself. For example two different types of
@ -127,10 +133,6 @@ you should use `""` as the separator.
@@@
The @ref:[Persistence example in the Cluster Sharding documentation](cluster-sharding.md#persistence-example)
illustrates how to construct the `PersistenceId` from the `entityTypeKey` and `entityId` provided by the
`EntityContext`.
A custom identifier can be created with `PersistenceId.ofUniqueId`.
### Command handler
@ -293,10 +295,10 @@ multiple events. This is signalled to an `EventSourcedBehavior` via an `EventRej
## Cluster Sharding and EventSourcedBehavior
In a use case where the number of persistent actors needed is higher than what would fit in the memory of one node or
where resilience is important so that if a node crashes the persistent actors are quickly started on a new node and can
resume operations @ref:[Cluster Sharding](cluster-sharding.md) is an excellent fit to spread persistent actors over a
cluster and address them by id.
@ref:[Cluster Sharding](cluster-sharding.md) is an excellent fit to spread persistent actors over a
cluster, addressing them by id. It makes it possible to have more persistent actors exist in the cluster than what
would fit in the memory of one node. Cluster sharding improves the resilience of the cluster. If a node crashes,
the persistent actors are quickly started on a new node and can resume operations.
The `EventSourcedBehavior` can then be run as with any plain actor as described in @ref:[actors documentation](actors.md),
but since Akka Persistence is based on the single-writer principle the persistent actors are typically used together