Skip to content

Commit f2813ed

Browse files
committed
feat: updateTimestampOffset
1 parent 0390b64 commit f2813ed

File tree

2 files changed

+34
-1
lines changed

2 files changed

+34
-1
lines changed

akka-projection-core/src/main/scala/akka/projection/ProjectionBehavior.scala

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
*/
44

55
package akka.projection
6+
import java.time.Instant
7+
68
import akka.actor.typed.scaladsl.LoggerOps
79
import scala.util.Failure
810
import scala.util.Success
@@ -19,6 +21,7 @@ import akka.actor.typed.scaladsl.StashBuffer
1921
import akka.annotation.InternalApi
2022
import akka.projection.internal.ManagementState
2123
import akka.projection.scaladsl.ProjectionManagement
24+
import akka.projection.scaladsl.ProjectionManagement.UpdateTimestampOffset
2225

2326
object ProjectionBehavior {
2427

@@ -48,6 +51,12 @@ object ProjectionBehavior {
4851
extends ProjectionManagementCommand
4952
final case class SetOffsetResult[Offset](replyTo: ActorRef[Done]) extends ProjectionManagementCommand
5053

54+
final case class SetTimestampOffset(
55+
projectionId: ProjectionId,
56+
updates: Set[UpdateTimestampOffset],
57+
replyTo: ActorRef[Done])
58+
extends ProjectionManagementCommand
59+
5160
final case class IsPaused(projectionId: ProjectionId, replyTo: ActorRef[Boolean])
5261
extends ProjectionManagementCommand
5362
final case class SetPaused(projectionId: ProjectionId, paused: Boolean, replyTo: ActorRef[Done])

akka-projection-core/src/main/scala/akka/projection/scaladsl/ProjectionManagement.scala

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@
55
package akka.projection.scaladsl
66

77
import java.util.concurrent.ConcurrentHashMap
8+
89
import scala.concurrent.ExecutionContext
910
import scala.concurrent.Future
1011
import scala.concurrent.TimeoutException
1112
import scala.concurrent.duration.FiniteDuration
13+
1214
import akka.Done
1315
import akka.actor.typed.ActorRef
1416
import akka.actor.typed.ActorSystem
@@ -20,14 +22,18 @@ import akka.projection.ProjectionBehavior
2022
import akka.projection.ProjectionId
2123
import akka.util.JavaDurationConverters._
2224
import akka.util.Timeout
23-
2425
import java.net.URLEncoder
2526
import java.nio.charset.StandardCharsets
27+
import java.time.Instant
28+
29+
import akka.projection.scaladsl.ProjectionManagement.UpdateTimestampOffset
2630

2731
object ProjectionManagement extends ExtensionId[ProjectionManagement] {
2832
def createExtension(system: ActorSystem[_]): ProjectionManagement = new ProjectionManagement(system)
2933

3034
def get(system: ActorSystem[_]): ProjectionManagement = apply(system)
35+
36+
final case class UpdateTimestampOffset(persistenceId: String, seqNr: Long, timestamp: Instant)
3137
}
3238

3339
class ProjectionManagement(system: ActorSystem[_]) extends Extension {
@@ -100,6 +106,24 @@ class ProjectionManagement(system: ActorSystem[_]) extends Extension {
100106
retry(() => askSetOffset())
101107
}
102108

109+
/**
110+
* Update the stored `TimestampOffset` for the `projectionId` and restart the `Projection`.
111+
* This can be useful if the projection was stuck with errors on a specific offset and should skip
112+
* that offset and continue with next.
113+
*
114+
* Another use case is to populate the offset store with know starting points when enabling change events
115+
* for Durable State. In that case the offset sequence number should be set to current Durable State
116+
* revision minus 1.
117+
*/
118+
def updateTimestampOffset(projectionId: ProjectionId, updates: Set[UpdateTimestampOffset]): Future[Done] = {
119+
def askSetTimestampOffset(): Future[Done] = {
120+
topic(projectionId.name)
121+
.ask(replyTo => Topic.Publish(SetTimestampOffset(projectionId, updates, replyTo)))
122+
}
123+
124+
retry(() => askSetTimestampOffset())
125+
}
126+
103127
private def retry[T](operation: () => Future[T]): Future[T] = {
104128
def attempt(remaining: Int): Future[T] = {
105129
operation().recoverWith {

0 commit comments

Comments
 (0)