This is an automated email from the ASF dual-hosted git repository.
He-Pin pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/pekko.git
The following commit(s) were added to refs/heads/main by this push:
new 923c13f6c9 Clarify mutable state updates in typed persistence (#3655)
923c13f6c9 is described below
commit 923c13f6c97b74e1ee350a430478180036b85fa7
Author: He-Pin(kerr) <[email protected]>
AuthorDate: Fri Oct 23 21:50:43 2026 +0800
Clarify mutable state updates in typed persistence (#3655)
* Clarify mutable state updates in typed persistence
Motivation:
Mutable state guidance needs to distinguish event handlers from command
handlers and explain that mutating durable state still requires persistence.
Modification:
Document where event-sourced state may be changed, require persist for a
mutated durable-state instance, and explain durability and asynchronous access
constraints around callbacks.
Result:
Scala and Java users receive guidance consistent with the current
persistence lifecycle.
Tests:
- Tests: Not run - docs only.
- Passed: `docs/paradox`.
- Passed: `+headerCheckAll`.
- Full `validatePullRequest` is left to remote CI.
References:
- Refs [akka/akka-core#32108](https://github.com/akka/akka-core/pull/32108).
- Upstream commit:
[15be3cfb2b198cb72da907eeb1059ecae09bff7c](https://github.com/akka/akka-core/commit/15be3cfb2b198cb72da907eeb1059ecae09bff7c).
- Source snapshot: Akka `v2.9.0`
(`bf20e967571574994ccb3cae411401f5a9021ab9`). Its
[license](https://github.com/akka/akka-core/blob/v2.9.0/LICENSE) names
2026-10-23 as the Change Date and Apache License 2.0 as the Change License.
`LICENSE` and `NOTICE` record the source, and original copyright notices are
retained. Headers were generated/checked with the project sbt tasks.
* Keep licensing metadata outside the migration change
Motivation:
Keep PR #3655 scoped to its code or documentation change.
Modification:
Restore the root LICENSE and NOTICE files to main.
Result:
The migration diff retains its source headers without repository-level
licensing edits.
Tests:
- git diff --check passed.
- Verified every other file is unchanged; prior focused validation still
applies.
References:
Refs #3655.
* Clarify durable state updates to preserve empty state
Motivation:
Mutating the initial durable state also mutates the empty state reused by
deletion and recovery.
Modification:
Recommend fresh updated state instances and preserving the initial empty
state, while retaining persistence-success and thread-safety guidance.
Result:
The documentation describes safe state updates supported by the current
implementation.
Tests:
- Not run - docs only
- sbt -batch -Dsbt.supershell=false docs/paradox: passed
- git diff --check: passed
- Qoder stdout follow-up: No must-fix findings
References:
Refs #3655
* Clarify the durable-state mutation rule #3655
Motivation:
The review asked for direct guidance when using a mutable durable-state
class.
Modification:
State that callers must not mutate the current instance in place and must
pass a new instance to persist.
Result:
The update rule is explicit alongside the empty-state and
persistence-success guidance.
Tests:
Validation used a local integration checkout with current main and the
other review fixes.
- Runtime tests: Not run - docs only.
- docs/paradox, headerCreateAll, and +headerCheckAll: passed.
- git diff --check: passed.
- Full validation is delegated to remote CI.
References:
Refs #3655.
---
docs/src/main/paradox/typed/durable-state/persistence.md | 7 +++++++
docs/src/main/paradox/typed/persistence.md | 3 ++-
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/docs/src/main/paradox/typed/durable-state/persistence.md
b/docs/src/main/paradox/typed/durable-state/persistence.md
index d882d716d1..7d139cea7c 100644
--- a/docs/src/main/paradox/typed/durable-state/persistence.md
+++ b/docs/src/main/paradox/typed/durable-state/persistence.md
@@ -96,6 +96,13 @@ The two most commonly used effects are:
* `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
+The state is typically an immutable class, with a new instance passed to the
`persist` effect.
+If using a mutable state class, do not mutate the current state instance in
place; pass a new instance to `persist`.
+Keep the initial `emptyState` instance unchanged, since it is reused after
deletion and when recovery
+finds no stored state. The change is only durable after persistence completes
successfully; use
+`thenRun` for side effects that depend on that success. Do not mutate the
state from asynchronous
+callbacks or other threads, which may race with serialization or subsequent
commands.
+
More effects are explained in @ref:[Effects and Side
Effects](#effects-and-side-effects).
In addition to returning the primary `Effect` for the command,
`DurableStateBehavior`s can also
diff --git a/docs/src/main/paradox/typed/persistence.md
b/docs/src/main/paradox/typed/persistence.md
index a6ab6a046c..ead09afc2c 100644
--- a/docs/src/main/paradox/typed/persistence.md
+++ b/docs/src/main/paradox/typed/persistence.md
@@ -147,7 +147,8 @@ In the case of multiple persisted events, the
`eventHandler` is called with each
The state is typically defined as an immutable class and then the event
handler returns a new instance of the state.
You may choose to use a mutable class for the state, and then the event
handler may update the state instance and
-return the same instance. Both immutable and mutable state is supported.
+return the same instance. Both immutable and mutable state is supported, but
mutable state must only be modified
+in the event handler, never in the command handler.
The same event handler is also used when the entity is started up to recover
its state from the stored events.
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]