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:
parent
b98c3b2da6
commit
8e932ede37
9 changed files with 131 additions and 19 deletions
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 }
|
||||||
|
|
||||||
|
|
@ -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
|
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.
|
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
|
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.
|
implement an Event Sourced CQRS application with Akka Persistence and Akka Projections.
|
||||||
|
|
||||||
|
|
|
||||||
6
akka-docs/src/main/paradox/typed/cqrs-durable-state.md
Normal file
6
akka-docs/src/main/paradox/typed/cqrs-durable-state.md
Normal 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.
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -2,6 +2,5 @@
|
||||||
|
|
||||||
@ref:[EventSourcedBehavior](persistence.md)s along with [Akka Projections](https://doc.akka.io/docs/akka-projection/current/)
|
@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/)
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,5 +10,7 @@ project.description: Durable state with Akka Persistence enables actors to persi
|
||||||
|
|
||||||
* [persistence-durable-state](persistence-durable-state.md)
|
* [persistence-durable-state](persistence-durable-state.md)
|
||||||
* [persistence-style](persistence-style-durable-state.md)
|
* [persistence-style](persistence-style-durable-state.md)
|
||||||
|
* [cqrs](cqrs-durable-state.md)
|
||||||
|
* [persistence-query](../persistence-query-durable-state.md)
|
||||||
|
|
||||||
@@@
|
@@@
|
||||||
|
|
|
||||||
|
|
@ -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.
|
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.
|
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
|
## Example and core API
|
||||||
|
|
@ -64,7 +67,9 @@ The @apidoc[akka.persistence.typed.PersistenceId] is the stable unique identifie
|
||||||
durabe state store.
|
durabe state store.
|
||||||
|
|
||||||
@ref:[Cluster Sharding](cluster-sharding.md) is typically used together with `DurableStateBehavior` to ensure
|
@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
|
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`).
|
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
|
The default separator when concatenating the `entityTypeHint` and `entityId` is `|`, but a custom separator
|
||||||
is supported.
|
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`.
|
A custom identifier can be created with `PersistenceId.ofUniqueId`.
|
||||||
|
|
||||||
### Command handler
|
### 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:
|
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
|
* `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).
|
More effects are explained in @ref:[Effects and Side Effects](#effects-and-side-effects).
|
||||||
|
|
|
||||||
|
|
@ -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
|
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
|
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
|
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
|
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
|
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)
|
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/)
|
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.
|
event journal and snapshot store.
|
||||||
|
|
||||||
@ref:[Cluster Sharding](cluster-sharding.md) is typically used together with `EventSourcedBehavior` to ensure
|
@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
|
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
|
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`.
|
A custom identifier can be created with `PersistenceId.ofUniqueId`.
|
||||||
|
|
||||||
### Command handler
|
### Command handler
|
||||||
|
|
@ -293,10 +295,10 @@ multiple events. This is signalled to an `EventSourcedBehavior` via an `EventRej
|
||||||
|
|
||||||
## Cluster Sharding and EventSourcedBehavior
|
## 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
|
@ref:[Cluster Sharding](cluster-sharding.md) is an excellent fit to spread persistent actors over a
|
||||||
where resilience is important so that if a node crashes the persistent actors are quickly started on a new node and can
|
cluster, addressing them by id. It makes it possible to have more persistent actors exist in the cluster than what
|
||||||
resume operations @ref:[Cluster Sharding](cluster-sharding.md) is an excellent fit to spread persistent actors over a
|
would fit in the memory of one node. Cluster sharding improves the resilience of the cluster. If a node crashes,
|
||||||
cluster and address them by id.
|
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),
|
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
|
but since Akka Persistence is based on the single-writer principle the persistent actors are typically used together
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue