Merge branch 'master' into feature-active-active-event-sourcing
This commit is contained in:
commit
7bf12721c1
48 changed files with 935 additions and 688 deletions
|
|
@ -27,30 +27,30 @@ There are two parts of Akka that need careful consideration when performing an r
|
|||
1. Serialization format of persisted events and snapshots. New nodes must be able to read old data, and
|
||||
during the update old nodes must be able to read data stored by new nodes.
|
||||
|
||||
There are many more application specific aspects for serialization changes during rolling upgrades to consider.
|
||||
There are many more application specific aspects for serialization changes during rolling updates to consider.
|
||||
For example based on the use case and requirements, whether to allow dropped messages or tear down the TCP connection when the manifest is unknown.
|
||||
When some message loss during a rolling upgrade is acceptable versus a full shutdown and restart, assuming the application recovers afterwards
|
||||
When some message loss during a rolling update is acceptable versus a full shutdown and restart, assuming the application recovers afterwards
|
||||
* If a `java.io.NotSerializableException` is thrown in `fromBinary` this is treated as a transient problem, the issue logged and the message is dropped
|
||||
* If other exceptions are thrown it can be an indication of corrupt bytes from the underlying transport, and the connection is broken
|
||||
|
||||
For more zero-impact rolling upgrades, it is important to consider a strategy for serialization that can be evolved.
|
||||
One approach to retiring a serializer without downtime is described in @ref:[two rolling upgrade steps to switch to the new serializer](../serialization.md#rolling-upgrades).
|
||||
For more zero-impact rolling updates, it is important to consider a strategy for serialization that can be evolved.
|
||||
One approach to retiring a serializer without downtime is described in @ref:[two rolling update steps to switch to the new serializer](../serialization.md#rolling-updates).
|
||||
Additionally you can find advice on @ref:[Persistence - Schema Evolution](../persistence-schema-evolution.md) which also applies to remote messages when deploying with rolling updates.
|
||||
|
||||
## Cluster Sharding
|
||||
|
||||
During a rolling upgrade, sharded entities receiving traffic may be moved during @ref:[shard rebalancing](../typed/cluster-sharding-concepts.md#shard-rebalancing),
|
||||
During a rolling update, sharded entities receiving traffic may be moved during @ref:[shard rebalancing](../typed/cluster-sharding-concepts.md#shard-rebalancing),
|
||||
to an old or new node in the cluster, based on the pluggable allocation strategy and settings.
|
||||
When an old node is stopped the shards that were running on it are moved to one of the
|
||||
other old nodes remaining in the cluster. The `ShardCoordinator` is itself a cluster singleton.
|
||||
To minimize downtime of the shard coordinator, see the strategies about @ref[ClusterSingleton](#cluster-singleton) rolling upgrades below.
|
||||
To minimize downtime of the shard coordinator, see the strategies about @ref[ClusterSingleton](#cluster-singleton) rolling updates below.
|
||||
|
||||
A few specific changes to sharding configuration require @ref:[a full cluster restart](#cluster-sharding-configuration-change).
|
||||
|
||||
## Cluster Singleton
|
||||
|
||||
Cluster singletons are always running on the oldest node. To avoid moving cluster singletons more than necessary during a rolling upgrade,
|
||||
it is recommended to upgrade the oldest node last. This way cluster singletons are only moved once during a full rolling upgrade.
|
||||
Cluster singletons are always running on the oldest node. To avoid moving cluster singletons more than necessary during a rolling update,
|
||||
it is recommended to upgrade the oldest node last. This way cluster singletons are only moved once during a full rolling update.
|
||||
Otherwise, in the worst case cluster singletons may be migrated from node to node which requires coordination and initialization
|
||||
overhead several times.
|
||||
|
||||
|
|
@ -160,5 +160,5 @@ Rolling update is not supported when @ref:[changing the remoting transport](../r
|
|||
|
||||
### Migrating from Classic Sharding to Typed Sharding
|
||||
|
||||
If you have been using classic sharding it is possible to do a rolling upgrade to typed sharding using a 3 step procedure.
|
||||
If you have been using classic sharding it is possible to do a rolling update to typed sharding using a 3 step procedure.
|
||||
The steps along with example commits are detailed in [this sample PR](https://github.com/akka/akka-samples/pull/110)
|
||||
|
|
|
|||
|
|
@ -5,9 +5,11 @@
|
|||
Akka offers tiny helpers for use with @scala[@scaladoc[Future](scala.concurrent.Future)s]@java[@javadoc[CompletionStage](java.util.concurrent.CompletionStage)]. These are part of Akka's core module:
|
||||
|
||||
@@dependency[sbt,Maven,Gradle] {
|
||||
symbol1=AkkaVersion
|
||||
value1="$akka.version$"
|
||||
group="com.typesafe.akka"
|
||||
artifact="akka-actor_$scala.binary_version$"
|
||||
version="$akka.version$"
|
||||
artifact="akka-actor_$scala.binary.version$"
|
||||
version=AkkaVersion
|
||||
}
|
||||
|
||||
## After
|
||||
|
|
|
|||
|
|
@ -224,7 +224,7 @@ needs to have an associated code which indicates if it is a window or aisle seat
|
|||
Adding fields is the most common change you'll need to apply to your messages so make sure the serialization format
|
||||
you picked for your payloads can handle it apropriately, i.e. such changes should be *binary compatible*.
|
||||
This is achieved using the right serializer toolkit. In the following examples we will be using protobuf.
|
||||
See also @ref:[how to add fields with Jackson](serialization-jackson.md#add-field).
|
||||
See also @ref:[how to add fields with Jackson](serialization-jackson.md#add-optional-field).
|
||||
|
||||
While being able to read messages with missing fields is half of the solution, you also need to deal with the missing
|
||||
values somehow. This is usually modeled as some kind of default value, or by representing the field as an @scala[`Option[T]`]@java[`Optional<T>`]
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@ reading this migration guide and testing your application thoroughly is recommen
|
|||
|
||||
Rolling updates are possible without shutting down all nodes of the Akka Cluster, but will require
|
||||
configuration adjustments as described in the @ref:[Remoting](#remoting) section of this migration
|
||||
guide.
|
||||
guide. Due to the @ref:[changed serialization of the Cluster messages in Akka 2.6.2](rolling-update.md#2-6-2-clustermessageserializer-manifests-change)
|
||||
a rolling update from 2.5.x must first be made to Akka 2.6.2 and then a second rolling update can change to Akka 2.6.3
|
||||
or later.
|
||||
|
||||
## Scala 2.11 no longer supported
|
||||
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ This means that a rolling update will have to go through at least one of 2.6.2,
|
|||
Issue: [#28918](https://github.com/akka/akka/issues/28918). JacksonCborSerializer was using plain JSON format
|
||||
instead of CBOR.
|
||||
|
||||
If you have `jackson-cbor` in your `serialization-bindings` a rolling upgrade will have to go through 2.6.5 when
|
||||
If you have `jackson-cbor` in your `serialization-bindings` a rolling update will have to go through 2.6.5 when
|
||||
upgrading to 2.6.5 or higher.
|
||||
|
||||
In Akka 2.6.5 the `jackson-cbor` binding will still serialize to JSON format to support rolling update from 2.6.4.
|
||||
|
|
|
|||
|
|
@ -164,11 +164,20 @@ when using polymorphic types.
|
|||
|
||||
### ADT with trait and case object
|
||||
|
||||
In Scala it's common to use a sealed trait and case objects to represent enums. If the values are case classes
|
||||
It's common in Scala to use a sealed trait and case objects to represent enums. If the values are case classes
|
||||
the `@JsonSubTypes` annotation as described above works, but if the values are case objects it will not.
|
||||
The annotation requires a `Class` and there is no way to define that in an annotation for a `case object`.
|
||||
|
||||
This can be solved by implementing a custom serialization for the enums. Annotate the `trait` with
|
||||
The easiest workaround is to define the case objects as case class without any field.
|
||||
|
||||
Alternatively, you can define an intermediate trait for the case object and a custom deserializer for it. The example below builds on the previous `Animal` sample by adding a fictitious, single instance, new animal, an `Unicorn`.
|
||||
|
||||
Scala
|
||||
: @@snip [SerializationDocSpec.scala](/akka-serialization-jackson/src/test/scala/doc/akka/serialization/jackson/SerializationDocSpec.scala) { #polymorphism-case-object }
|
||||
|
||||
The case object `Unicorn` can't be used in a `@JsonSubTypes` annotation, but its trait can. When serializing the case object we need to know which type tag to use, hence the `@JsonTypeName` annotation on the object. When deserializing, Jackson will only know about the trait variant therefore we need a custom deserializer that returns the case object.
|
||||
|
||||
On the other hand, if the ADT only has case objects, you can solve it by implementing a custom serialization for the enums. Annotate the `trait` with
|
||||
`@JsonSerialize` and `@JsonDeserialize` and implement the serialization with `StdSerializer` and
|
||||
`StdDeserializer`.
|
||||
|
||||
|
|
@ -205,7 +214,7 @@ We will look at a few scenarios of how the classes may be evolved.
|
|||
Removing a field can be done without any migration code. The Jackson serializer will ignore properties that does
|
||||
not exist in the class.
|
||||
|
||||
### Add Field
|
||||
### Add Optional Field
|
||||
|
||||
Adding an optional field can be done without any migration code. The default value will be @scala[None]@java[`Optional.empty`].
|
||||
|
||||
|
|
@ -226,6 +235,8 @@ Scala
|
|||
Java
|
||||
: @@snip [ItemAdded.java](/akka-serialization-jackson/src/test/java/jdoc/akka/serialization/jackson/v2a/ItemAdded.java) { #add-optional }
|
||||
|
||||
### Add Mandatory Field
|
||||
|
||||
Let's say we want to have a mandatory `discount` property without default value instead:
|
||||
|
||||
Scala
|
||||
|
|
@ -361,6 +372,63 @@ binding, but it should still be possible to deserialize old data with Jackson.
|
|||
|
||||
It's a list of class names or prefixes of class names.
|
||||
|
||||
## Rolling updates
|
||||
|
||||
When doing a rolling update, for a period of time there are two different binaries running in production. If the schema
|
||||
has evolved requiring a new schema version, the data serialized by the new binary will be unreadable from the old
|
||||
binary. This situation causes transient errors on the processes running the old binary. This service degradation is
|
||||
usually fine since the rolling update will eventually complete and all old processes will be replaced with the new
|
||||
binary. To avoid this service degradation you can also use forward-one support in your schema evolutions.
|
||||
|
||||
To complete a no-degradation rolling update, you need to make two deployments. First, deploy a new binary which can read
|
||||
the new schema but still uses the old schema. Then, deploy a second binary which serializes data using the new schema
|
||||
and drops the downcasting code from the migration.
|
||||
|
||||
Let's take, for example, the case above where we [renamed a field](#rename-field).
|
||||
|
||||
The starting schema is:
|
||||
|
||||
Scala
|
||||
: @@snip [ItemAdded.java](/akka-serialization-jackson/src/test/scala/doc/akka/serialization/jackson/v1/ItemAdded.scala) { #add-optional }
|
||||
|
||||
Java
|
||||
: @@snip [ItemAdded.java](/akka-serialization-jackson/src/test/java/jdoc/akka/serialization/jackson/v1/ItemAdded.java) { #add-optional }
|
||||
|
||||
In a first deployment, we still don't make any change to the event class:
|
||||
|
||||
Scala
|
||||
: @@snip [ItemAdded.scala](/akka-serialization-jackson/src/test/scala/doc/akka/serialization/jackson/v1/ItemAdded.scala) { #forward-one-rename }
|
||||
|
||||
Java
|
||||
: @@snip [ItemAdded.java](/akka-serialization-jackson/src/test/java/jdoc/akka/serialization/jackson/v1/ItemAdded.java) { #forward-one-rename }
|
||||
|
||||
but we introduce a migration that can read the newer schema which is versioned `2`:
|
||||
|
||||
Scala
|
||||
: @@snip [ItemAddedMigration.scala](/akka-serialization-jackson/src/test/scala/doc/akka/serialization/jackson/v1withv2/ItemAddedMigration.scala) { #forward-one-rename }
|
||||
|
||||
Java
|
||||
: @@snip [ItemAddedMigration.java](/akka-serialization-jackson/src/test/java/jdoc/akka/serialization/jackson/v1withv2/ItemAddedMigration.java) { #forward-one-rename }
|
||||
|
||||
Once all running nodes have the new migration code which can read version `2` of `ItemAdded` we can proceed with the
|
||||
second step. So, we deploy the updated event:
|
||||
|
||||
Scala
|
||||
: @@snip [ItemAdded.scala](/akka-serialization-jackson/src/test/scala/doc/akka/serialization/jackson/v2c/ItemAdded.scala) { #rename }
|
||||
|
||||
Java
|
||||
: @@snip [ItemAdded.java](/akka-serialization-jackson/src/test/java/jdoc/akka/serialization/jackson/v2c/ItemAdded.java) { #rename }
|
||||
|
||||
and the final migration code which no longer needs forward-compatibility code:
|
||||
|
||||
Scala
|
||||
: @@snip [ItemAddedMigration.scala](/akka-serialization-jackson/src/test/scala/doc/akka/serialization/jackson/v2c/ItemAddedMigration.scala) { #rename }
|
||||
|
||||
Java
|
||||
: @@snip [ItemAddedMigration.java](/akka-serialization-jackson/src/test/java/jdoc/akka/serialization/jackson/v2c/ItemAddedMigration.java) { #rename }
|
||||
|
||||
|
||||
|
||||
## Jackson Modules
|
||||
|
||||
The following Jackson modules are enabled by default:
|
||||
|
|
|
|||
|
|
@ -180,7 +180,7 @@ should be serialized by it.
|
|||
It's recommended to throw `IllegalArgumentException` or `java.io.NotSerializableException` in
|
||||
`fromBinary` if the manifest is unknown. This makes it possible to introduce new message types and
|
||||
send them to nodes that don't know about them. This is typically needed when performing
|
||||
rolling upgrades, i.e. running a cluster with mixed versions for a while.
|
||||
rolling updates, i.e. running a cluster with mixed versions for a while.
|
||||
Those exceptions are treated as a transient problem in the classic remoting
|
||||
layer. The problem will be logged and the message dropped. Other exceptions will tear down
|
||||
the TCP connection because it can be an indication of corrupt bytes from the underlying
|
||||
|
|
@ -252,24 +252,24 @@ akka.actor.warn-about-java-serializer-usage = off
|
|||
It is not safe to mix major Scala versions when using the Java serialization as Scala does not guarantee compatibility
|
||||
and this could lead to very surprising errors.
|
||||
|
||||
## Rolling upgrades
|
||||
## Rolling updates
|
||||
|
||||
A serialized remote message (or persistent event) consists of serializer-id, the manifest, and the binary payload.
|
||||
When deserializing it is only looking at the serializer-id to pick which `Serializer` to use for `fromBinary`.
|
||||
The message class (the bindings) is not used for deserialization. The manifest is only used within the
|
||||
`Serializer` to decide how to deserialize the payload, so one `Serializer` can handle many classes.
|
||||
|
||||
That means that it is possible to change serialization for a message by performing two rolling upgrade steps to
|
||||
That means that it is possible to change serialization for a message by performing two rolling update steps to
|
||||
switch to the new serializer.
|
||||
|
||||
1. Add the `Serializer` class and define it in `akka.actor.serializers` config section, but not in
|
||||
`akka.actor.serialization-bindings`. Perform a rolling upgrade for this change. This means that the
|
||||
`akka.actor.serialization-bindings`. Perform a rolling update for this change. This means that the
|
||||
serializer class exists on all nodes and is registered, but it is still not used for serializing any
|
||||
messages. That is important because during the rolling upgrade the old nodes still don't know about
|
||||
messages. That is important because during the rolling update the old nodes still don't know about
|
||||
the new serializer and would not be able to deserialize messages with that format.
|
||||
|
||||
1. The second change is to register that the serializer is to be used for certain classes by defining
|
||||
those in the `akka.actor.serialization-bindings` config section. Perform a rolling upgrade for this
|
||||
those in the `akka.actor.serialization-bindings` config section. Perform a rolling update for this
|
||||
change. This means that new nodes will use the new serializer when sending messages and old nodes will
|
||||
be able to deserialize the new format. Old nodes will continue to use the old serializer when sending
|
||||
messages and new nodes will be able to deserialize the old format.
|
||||
|
|
|
|||
|
|
@ -140,7 +140,7 @@ The reason for only using a limited number of nodes is to keep the number of con
|
|||
centers low. The same nodes are also used for the gossip protocol when disseminating the membership
|
||||
information across data centers. Within a data center all nodes are involved in gossip and failure detection.
|
||||
|
||||
This influences how rolling upgrades should be performed. Don't stop all of the oldest that are used for gossip
|
||||
This influences how rolling updates should be performed. Don't stop all of the oldest that are used for gossip
|
||||
at the same time. Stop one or a few at a time so that new nodes can take over the responsibility.
|
||||
It's best to leave the oldest nodes until last.
|
||||
|
||||
|
|
|
|||
|
|
@ -293,7 +293,7 @@ Cluster Sharding uses its own Distributed Data `Replicator` per node.
|
|||
If using roles with sharding there is one `Replicator` per role, which enables a subset of
|
||||
all nodes for some entity types and another subset for other entity types. Each replicator has a name
|
||||
that contains the node role and therefore the role configuration must be the same on all nodes in the
|
||||
cluster, for example you can't change the roles when performing a rolling upgrade.
|
||||
cluster, for example you can't change the roles when performing a rolling update.
|
||||
Changing roles requires @ref:[a full cluster restart](../additional/rolling-updates.md#cluster-sharding-configuration-change).
|
||||
|
||||
The `akka.cluster.sharding.distributed-data` config section configures the settings for Distributed Data.
|
||||
|
|
@ -413,7 +413,7 @@ akka.persistence.cassandra.journal {
|
|||
}
|
||||
```
|
||||
|
||||
Once you have migrated you cannot go back to the old persistence store, a rolling upgrade is therefore not possible.
|
||||
Once you have migrated you cannot go back to the old persistence store, a rolling update is therefore not possible.
|
||||
|
||||
When @ref:[Distributed Data mode](#distributed-data-mode) is used the identifiers of the entities are
|
||||
stored in @ref:[Durable Storage](distributed-data.md#durable-storage) of Distributed Data. You may want to change the
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ avoid blocking APIs. The following solution explains how to handle blocking
|
|||
operations properly.
|
||||
|
||||
Note that the same hints apply to managing blocking operations anywhere in Akka,
|
||||
including Streams, Http and other reactive libraries built on top of it.
|
||||
including Streams, HTTP and other reactive libraries built on top of it.
|
||||
|
||||
@@@
|
||||
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ public class LambdaPersistencePluginDocTest {
|
|||
public MyJournalSpecTest() {
|
||||
super(
|
||||
ConfigFactory.parseString(
|
||||
"persistence.journal.plugin = "
|
||||
"akka.persistence.journal.plugin = "
|
||||
+ "\"akka.persistence.journal.leveldb-shared\""));
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue