-
Notifications
You must be signed in to change notification settings - Fork 3.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: delete events for snapshotWhen/shouldSnapshot predicate #32231
feat: delete events for snapshotWhen/shouldSnapshot predicate #32231
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looking very good
akka-persistence-typed/src/main/scala/akka/persistence/typed/javadsl/EventSourcedBehavior.scala
Outdated
Show resolved
Hide resolved
Since it seems that the direction of this PR is about right, I am going to add docs, mima ect now. |
@patriknw I think this is ready to be reviewed. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looking good
...tests/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorRetentionSpec.scala
Show resolved
Hide resolved
...tests/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorRetentionSpec.scala
Outdated
Show resolved
Hide resolved
...tests/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorRetentionSpec.scala
Outdated
Show resolved
Hide resolved
...tests/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorRetentionSpec.scala
Outdated
Show resolved
Hide resolved
* | ||
* @return `true` if events should be deleted after `shouldSnapshot` evaluates to true | ||
*/ | ||
def deleteEventsOnSnapshot: Boolean = false |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the Java API this is a bit weirder because it doesn't have context except for the API-docs. I think a user could easily end up missing the shouldSnapshot
and just override just this expecting that it will delete events on snapshot.
public RetentionCriteria retentionCriteria() {
return RetentionCriteria.snapshotEvery(100, 2);
}
public bool deleteEventsOnSnapshot() {
return true;
}
I think that we need to either make either delete flag being true work or try to make this name more clear about being only for predicate based snapshot
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Initially the name of this method was deleteEventsOnShouldSnapshot
which got changed into the current name.
I think because of the current implementation we should separate the two flags and their respective impact.
The main problem here is the api of def shouldSnapshot(@unused state: State, @unused event: Event, @unused sequenceNr: Long): Boolean
which should be evolved to a proper type, similar to withRetention
. Maybe this is the way to go by adding @deprecated
to the api and introducing a new method+type for this? This should then also be made for the scala api.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, tricky to get it right without overdoing it. A type to return also means users has to figure out how to construct that, but maybe that is worth it because it makes what the flag refers to obvious.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe naming it back to deleteEventsOnShouldSnapshot
@patriknw? ;-)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Taking a step back, it's kind of weird to have the snapshot predicate and snapshot via RetentionCriteria as two separate things. We could add the snapshotWhen as another RetentionCriteria, and use the same approach for withDeleteEventsOnSnapshot.
Maybe that would loose the possibility to use both snapshotEvery(numberOfEvents) and snapshotWhen(predicate), but that is probably an unusual combination anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A user could always do the every N as a part of their predicate, by looking at seqnr, if they want both.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea of evolving the predicate api. But is a snapshotWhen
predicate actually something that belongs to a RetentionCriteria
? To me retention refers to the fact if a snapshot should be stored, and if so how many of them. I think we need a more composable api:
- define when a snapshot is triggered (predicate and/or numberOfEvents)
- if triggered, what is the retention of snapshots?
- delete events after a snapshot is triggered
To me this are three different properties that may be composed as needed (with some validation). But this probably goes into "not overdoing it".
For now I will change this PR and put snapshotWhen
into RetentionCriteria
to move it forward, we can see?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it's fine that we add some more for when to snapshot in RetentionCriteria. That is anyway the normal way to define that snapshots should be enabled.
Originally I think retention referred to deletion of events, but we didn't allow deletion unless there are snapshots. Yeah, maybe not perfect name, but that is what it is.
For now I will change this PR and put snapshotWhen into RetentionCriteria to move it forward
Agreed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you agree that the new snapshotWhen
should also receive a keepNSnapshots
param, like def snapshotEvery(numberOfEvents: Int, keepNSnapshots: Int): SnapshotCountRetentionCriteria
?
If not, after this pr, it will not be possible anymore to delete snapshots when using the (new) predicate based api because we remove option to combine snapshotEvery and predicate ways to define snapshot triggering.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or we rely on only-one-snapshot
to clean up snapshots?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added some notes but I guess this is in active dev so maybe you let us know when you want it reviewed again
akka-persistence-typed/src/main/scala/akka/persistence/typed/scaladsl/RetentionCriteria.scala
Outdated
Show resolved
Hide resolved
...ce-typed/src/test/scala/docs/akka/persistence/typed/BasicPersistentBehaviorCompileOnly.scala
Outdated
Show resolved
Hide resolved
...tests/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorRetentionSpec.scala
Show resolved
Hide resolved
akka-persistence-typed/src/main/scala/akka/persistence/typed/internal/Running.scala
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
looking good
...ce-typed/src/test/scala/docs/akka/persistence/typed/BasicPersistentBehaviorCompileOnly.scala
Outdated
Show resolved
Hide resolved
akka-persistence-testkit/src/main/scala/akka/persistence/testkit/internal/Unpersistent.scala
Outdated
Show resolved
Hide resolved
case _ => false | ||
} | ||
.withRetention(RetentionCriteria.snapshotEvery(numberOfEvents = 3, keepNSnapshots = 2)) | ||
.withRetention(RetentionCriteria.snapshotEvery(numberOfEvents = 2, keepNSnapshots = 2)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This had to change because now we do not allow anymore to combine snapshotWhen
and snapshotEvery
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For it to be equivalent with the existing it would be something like snapshotWhen((_, event, currentSeqNr) => event == SnapshotMade || currentSeqNr % 3 == 0)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting, this means we will loose the keepNSnapshots = 2
of the current api usage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure it is important to keep it snapshotting at the exact same points here though, so maybe fine.
case _ => false | ||
} | ||
.withRetention(RetentionCriteria.snapshotEvery(numberOfEvents = 3, keepNSnapshots = 2)) | ||
.withRetention(RetentionCriteria.snapshotEvery(numberOfEvents = 2, keepNSnapshots = 2)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For it to be equivalent with the existing it would be something like snapshotWhen((_, event, currentSeqNr) => event == SnapshotMade || currentSeqNr % 3 == 0)
@@ -51,9 +54,9 @@ object CounterSpec { | |||
(counter, event) => { | |||
eventProbe.foreach(_ ! event) | |||
counter.applyOperation(event) | |||
}).snapshotWhen { (_, _, seqNr) => | |||
}).withRetention(RetentionCriteria.snapshotWhen[Any, Any] { (_, _, seqNr) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The explicit type params are needed now, even with the explicit types on EventSourcedBehavior.apply
above?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. When I remove [Any, Any]
I do get:
akka-persistence-typed-tests/src/test/scala/akka/persistence/typed/crdt/CounterSpec.scala:57:65: missing parameter type
[error] }).withRetention(RetentionCriteria.snapshotWhen { (_, _, seqNr) =>
pid, | ||
snapshotSignalProbe = Some(snapshotSignalProbe.ref), | ||
eventSignalProbe = Some(eventProbe.ref)) | ||
.withRetention(RetentionCriteria.snapshotWhen[State, Event]((_, _, _) => true).withDeleteEventsOnSnapshot) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here we have explicit types on the returned ESB, the compiler doesn't infer those?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same error as above:
akka-persistence-typed-tests/src/test/scala/akka/persistence/typed/scaladsl/EventSourcedBehaviorRetentionSpec.scala:141:60: missing parameter type
[error] .withRetention(RetentionCriteria.snapshotWhen((_, _, _) => true).withDeleteEventsOnSnapshot)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, this is unfortunate. There are no types on the RetentionCriteria base trait, ofc. Therefore the types of the snapshotWhen are unrelated to the types of the EventSourcedBehavior. For example this would be wrong, but compiles fine:
.withRetention(RetentionCriteria.snapshotWhen[String, Int]((_, _, _) => true).withDeleteEventsOnSnapshot)
* | ||
* Can not be combined with the `snapshotEvery` api. | ||
*/ | ||
def snapshotWhen[State, Event](predicate: (State, Event, Long) => Boolean): SnapshotPredicateCriteria = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, now I see why the types cannot be inferred, they aren't tied to anything except the lambda. Hmmm, that's annoying but I don't see an obvious solution, other than introducing type params to RetentionCriteria
or pulling the factory methods up to EventSourcedBehavior
and neither of those seem like a great idea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@patriknw any other ideas how we could get this to align, note that it is not only cumbersome but also somewhat unsafe because with the current PR API you can create a SnapshotPredicate
for other event and state types than your actual ESB uses.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, I think this is a stopper for placing the predicate function in the RetentionCriteria. We can't really add type parameters to RetentionCritiera in a source compatible way.
Sorry, but I think we are back to the old approach. If we see that the SnapshotPredicateCriteria simplifies the internal implementation we could keep that as an internal implementation, but maybe we should just revert back to where we were before we suggested the RetentionCriteria?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reverted this PR to the old approach.
@Override // override retentionCriteria in EventSourcedBehavior | ||
public RetentionCriteria retentionCriteria() { | ||
return RetentionCriteria | ||
.snapshotWhen((state, event, currentSequenceNr) -> event instanceof BookingCompleted) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So here the types end up being (Object, Object, Long)
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
All three seem Object
as this is what intellij infers me as the type. I assume that its only scala that is more picky in having the requirement that all types resolvable in an anonymous lambda.
8903224
to
8ff009f
Compare
Co-authored-by: Johan Andrén <johan@markatta.com>
2c315bc
to
f56e72b
Compare
Resolved the merge conflicts with |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks for exploring the alternative API idea even if it turned out to be a dead end!
javadsl/EventSourcedBehavior.shouldSnapshot
andscaladsl/EventSourcedBehavior.snapshotWhen
both got deprecated and should be used via.withRetention(RetentionCriteria.shouldSnapshot(predicate)
.Disablers the combined usage of retention and predicate based api. This means (like before) snapshot deletion can not be triggered when using the predicate base api.
Fixes #29685.