diff --git a/README.md b/README.md index 79f9bd3..cb6df2b 100644 --- a/README.md +++ b/README.md @@ -64,6 +64,12 @@ If previously the `akka-persistence-mongo` library was used together with Akka a complete stack should be migrated to Pekko, Pekko-Persistence and `pekko-persistence-mongo` as a replacement, the following section describes migration steps to take. +If you are using a feature of Akka that uses persistence under the hood and potentially persists the full Akka class name as a part +of the persistence data (classic PersistentFSM is a known example), then this module will implicitly translate that data when reading +to the Pekko class name to be able to deserialize it and keep compatibility. The opposite is true if you migrate and find an issue with +your Pekko migration and need to roll back, but your application has already persisted entries with the new Pekko class name. The same functionality +has been backported to allow rolling back your application, but you must be on `akka-persistence-mongo` version **4.0.0** to have this translation. + The root configuration key changed from `akka` to `pekko`, so adjust your configuration accordingly, e.g. from: ```hocon diff --git a/scala/src/main/scala/pekko/contrib/persistence/mongodb/MongoDataModel.scala b/scala/src/main/scala/pekko/contrib/persistence/mongodb/MongoDataModel.scala index 1221ad3..1f34c3b 100644 --- a/scala/src/main/scala/pekko/contrib/persistence/mongodb/MongoDataModel.scala +++ b/scala/src/main/scala/pekko/contrib/persistence/mongodb/MongoDataModel.scala @@ -8,7 +8,7 @@ import org.apache.pekko.actor.ActorRef import org.apache.pekko.persistence.journal.Tagged import org.apache.pekko.persistence.query.{EventEnvelope, Offset} import org.apache.pekko.persistence.{AtomicWrite, PersistentRepr} -import org.apache.pekko.serialization.{Serialization, SerializerWithStringManifest} +import org.apache.pekko.serialization.{Serialization, Serializer, SerializerWithStringManifest} import scala.collection.immutable.{Seq => ISeq} import scala.util.{Failure, Success, Try} @@ -48,17 +48,30 @@ case class Serialized[C <: AnyRef](bytes: Array[Byte], val hint = "ser" lazy val content: C = { - val clazz = loadClass.getClassFor[X forSome { type X <: AnyRef }](className) - val tried = (serializedManifest,serializerId,clazz.flatMap(c => Try(ser.serializerFor(c)))) match { + Try(tryDeserialize(clazz, clazz.flatMap(c => Try(ser.serializerFor(c))))) + .recover({ + case _ if className.startsWith("akka.") => + val backwardsCompatClazz = loadClass.getClassFor[X forSome { type X <: AnyRef }](className.replaceFirst("akka", "org.apache.pekko")) + tryDeserialize(backwardsCompatClazz, backwardsCompatClazz.flatMap(c => Try(ser.serializerFor(c)))) + case x => throw x + }) match { + case Failure(x) => throw x + case Success(deser) => deser + } + } + + private def tryDeserialize(clazz: Try[Class[_ <: X forSome {type X <: AnyRef}]], + serializer: Try[Serializer]): C = { + val tried = (serializedManifest, serializerId, serializer) match { // Manifest was serialized, class exists ~ prefer read-time configuration case (Some(manifest), _, Success(clazzSer)) => ser.deserialize(bytes, clazzSer.identifier, manifest) // No manifest id serialized, prefer read-time configuration case (None, _, Success(clazzSer)) => - ser.deserialize[X forSome { type X <: AnyRef }](bytes, clazzSer.identifier, clazz.toOption) + ser.deserialize[X forSome {type X <: AnyRef}](bytes, clazzSer.identifier, clazz.toOption) // Manifest, id were serialized, class doesn't exist - use write-time configuration case (Some(manifest), Some(id), Failure(_)) => @@ -67,11 +80,11 @@ case class Serialized[C <: AnyRef](bytes: Array[Byte], // Below cases very unlikely to succeed // No manifest id serialized, class doesn't exist - use write-time configuration - case (None, Some(id),Failure(_)) => - ser.deserialize[X forSome { type X <: AnyRef }](bytes, id, clazz.toOption) + case (None, Some(id), Failure(_)) => + ser.deserialize[X forSome {type X <: AnyRef}](bytes, id, clazz.toOption) // fall back - case (_,None, Failure(_)) => + case (_, None, Failure(_)) => ser.deserialize(bytes, clazz.get) }