-
Notifications
You must be signed in to change notification settings - Fork 1
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
add per-state hook for handling discarded events #25
Comments
How would this be used and what is the difference between this and MachineRunner event |
The audit interface is just for auditing, it has no influence on the machine state. Unhandled events that are the result of a history merge (i.e. previously valid events) can often be useful for accumulating in the state a list of things to undo — a.k.a. decisions that were valid at the time but have become invalid in the meantime. |
Ah, I think I understand. So, things like calling a command, state mutation, when unhandled happen? |
The unhandled events hook still needs to be a pure function computing only the next state, so calling commands is out of the question. This is illustrated by the following event trace:
The |
Thinking more about this, we need to take a step back and evaluate the use-case:
We need an API where the developer can easily formulate this constraint. Also note that the underlying events — let’s call them b and b' — are not enough to trigger compensation: b' might arrive before b, in which case there would be no confusion since the “you should do something” event will never take effect. So the problematic case is where c happened due to b but then b' invalidates b. The log will now be a,b',b,c plus later c',d,… One solution is that the programmer writes code detecting that c — while handled — now needs to be compensated, but will also need to write code for the case that c' is ordered before c, so the compensation is triggered by c being discarded. Another solution is to track the immediately preceding event in every emission (e.g. in a tag) and inform the user code that event c is based on an event history that has been discarded. There may be more solutions, so what do you think @Kelerchian @jmg-duarte ? |
My immediate reaction, there are 3 scenarios:
Takeover and Surrender require taking note of the history of eventId-state. History-taking is costly, therefore it should be presented as a different type of machine-runner. Apart from that, I think we should be careful how to reconcile this and the |
Random related things to look at to build ideas on top of
|
I'm starting to reconsider:
------- Next topic ------- Idea for Event-history-recording// normally is accessible as readonly array
// also comes in other flavors (e.g. readonly Set)
const eventHistory = MachineRunner.createEventStore();
const machine = createMachineRunner(..., {
asyncIterBreakAfterDivergence: false, // the default, does not need to be written this way, just for demoing the API,
eventHistory: {
history: eventHistory,
discarded: null,
all: null
}
});
for await (const state of machine){
...
}
const divergencePoint = machine.divergencePoint(); // null | ZERO | ActyxEvent
const rollbackedEvents = MachineRunner.utils.rollbackedEvents(eventHistory, divergencePoint) // ActyxEvent[]
while (true){
const lastRollbackedEvent = rollbackedEvents.pop();
if (!lastRollbackedEvent) return
switch(lastRollbackedEvent.payload.type){
case ....
}
} |
This opens up some interesting thoughts: we could also surface a time warp by returning it together with the state: for await (const state of machine) {
if (state.timeWarp) {
// inspect states pre- and post-time-warp to see whether compensation is needed
}
} I still think that AsyncIterable is just a more resource-safe way of consuming an observable, their behaviour should be the same. Exiting the loop upon time warp would be confusing because then the state of the |
Another concern is noticing a compensation whose need arose while the local app was not running: seeing the event “I started this mission” after the event “someone else won the auction” should always trigger compensation — which needs to be recorded so that it isn’t done twice … |
That is correct. So ignore my first reconsideration
What do we want to expose with Also, I suddenly remembered something:
|
Ah, this is an interesting thought: we could allow the user to define that a certain state will always be emitted, also when not A state emitted this way during a replay would have its |
What do you mean by persistent compensation? |
What we’re mostly talking about here is compensation based on the ephemeral state of the application: we “saw” the wrong state and now we “see” the corrected state so we must compensate. We need to make this as easy and obvious as possible. In the robot example, the ephemeral compensation consists of telling the robot controller to abort its current mission and then to start looking for a new mission. The permanent state consists only of the event logs, nothing else. In there we might see events that are indicative of a mistake, which may need compensating events to rectify the situation — this is more relevant when considering other consumers of the event stream (later, or remote) so that they don’t get stuck with the mistake. An example here could be an analytics app counting robot missions and their duration. If only the ephemeral compensation happens then the erroneously started mission will be problematic for the analytics. Plus the programmer would likely want to count compensations as well. What I’m saying is that we first only need to tackle the ephemeral part and make it work well. Regarding the permanent part we’ll need more product feedback as input. |
I can't see a clear separation between the ephemeral part and the permanent part. Scenario 1[R1] think it is responsible for [mission] ∴ Repairing the system's state needs [R2] to compare the machine state to its internal
|
In a simple case like this, "emit-on-divergence-detection" should suffice I think.
Or this could be resolved just by comparing payload. |
Any event type that is currently unexpected will be discarded. We add a hook that can be installed via the state designer DSL so that discarded events can be processed. This is important for user code in order to trigger compensating actions after the log merge has invalidated some previous states that were acted upon.
The text was updated successfully, but these errors were encountered: