-
Notifications
You must be signed in to change notification settings - Fork 88
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
Introduce event-sourcing style in the HeadLogic #999
Conversation
Transactions CostsSizes and execution budgets for Hydra protocol transactions. Note that unlisted parameters are currently using
Script summary
Cost of Init Transaction
Cost of Commit TransactionThis is using ada-only outputs for better comparability.
Cost of CollectCom Transaction
Cost of Close Transaction
Cost of Contest Transaction
Cost of Abort TransactionSome variation because of random mixture of still initial and already committed outputs.
Cost of FanOut TransactionInvolves spending head output and burning head tokens. Uses ada-only UTxO for better comparability.
End-To-End Benchmark ResultsThis page is intended to collect the latest end-to-end benchmarks results produced by Hydra's Continuous Integration system from the latest Please take those results with a grain of salt as they are currently produced from very limited cloud VMs and not controlled hardware. Instead of focusing on the absolute results, the emphasis should be on relative results, eg. how the timings for a scenario evolve as the code changes. Generated at 2023-07-28 09:48:44.126724528 UTC 3-nodes ScenarioA rather typical setup, with 3 nodes forming a Hydra head.
Baseline ScenarioThis scenario represents a minimal case and as such is a good baseline against which to assess the overhead introduced by more complex setups. There is a single hydra-node d with a single client submitting single input and single output transactions with a constant UTxO set of 1.
|
331d761
to
07dd9cf
Compare
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.
Besides missing TODOs to tackle, this LGTM.
Just as a suggestion, I would keep the Spec state changes comments on the aggregate
.
hydra-node/src/Hydra/Node.hs
Outdated
StateChanged sc -> do | ||
-- TODO: We should not need to query the head state here | ||
s <- atomically queryHeadState | ||
save $ aggregate ledger s sc |
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.
idk if here or in processNextEvent
, but I think we should log a warn message in case, after processing an event, the state remains the same.
basically I want to know how many times we fall under the _otherState
case during state aggregate
, as imo those events are invalid.
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 idea.
If your assumption - given an event sequence is valid, we will never not update the state after a call to aggregate
- is true, then we could write a validateEvents
function which does just that and call it on the events whenever we want!
So not only when processing the events one by one, also after the fact or fail startup if that is the case (would be quite restrictive).
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 would like to remove the Ledger tx
parameter from the aggregate
function which introduces complexity and dependency in the interpretation of StateChanged
events which seems undesirable.
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.
Some clean up on comments, imports, and maybe the tick observed event should be emitted in all states.
StateChanged sc -> do | ||
-- TODO: We should not need to query the head state here | ||
s <- atomically queryHeadState | ||
save $ aggregate ledger s sc | ||
Effects _ -> pure () |
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 not related to this PR, but I would have expected the effect processing next to the state change persisting, next to the re-enqueuing. i.e. processEffects to be here and not done in a separate mapM
(on line 110)
hydra-node/src/Hydra/Node.hs
Outdated
StateChanged sc -> do | ||
-- TODO: We should not need to query the head state here | ||
s <- atomically queryHeadState | ||
save $ aggregate ledger s sc |
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 idea.
If your assumption - given an event sequence is valid, we will never not update the state after a call to aggregate
- is true, then we could write a validateEvents
function which does just that and call it on the events whenever we want!
So not only when processing the events one by one, also after the fact or fail startup if that is the case (would be quite restrictive).
hydra-node/src/Hydra/Node.hs
Outdated
@@ -190,6 +185,7 @@ data NodeState tx m = NodeState | |||
} | |||
|
|||
-- | Initialize a new 'NodeState'. | |||
-- TODO: We do not need a TVar to keep the HeadState. REMOVE. |
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 we want to do anything about this now?
I see two alternatives:
- Explicitly handing the
HeadState tx
around between invocations ofstepHydraNode
, changing it's signature tostepHydraNode :: HydraNode tx m -> HydraState tx -> m (HydraState tx)
- Making the monad we run in stateful. In mtl-style that would be
stepHydraNode :: MonadState (HydraState tx) m => HydraNode tx m -> m ()
, which makes us to useStateT (HeadState tx) IO
or aReaderT (TVar (HeadState tx)) IO
(or similar).
Alternative 1 is more explicit, a bit more verbose, and strictly less powerful (maybe good) as it's obviously not concurrently useful.
Alternative 2 would have very similar capabilities and complexity (instead of creating the handle you would need to run the monad transformer stack), but a departure from the handle pattern which we use throughout, and I would prefer consistency.
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.
CC @pgrange for later reference maybe
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.
Nice chunk of work guys! The main concern I have is keeping the ledger out of aggregate
function which would simplify things.
5dcc44e
to
5281614
Compare
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.
With the changes we did in ensemble, I think this is a good step forward now.
We use a dummy StateReplaced to have a backward compatible way to inject a full state update through the new interfaces.
This also does not return a state and instead introduces another getState function to access the state for assertions.
Refactoring the assertNewState into step + getState makes use of aggregate internally and moves the test code a bit away of using 'collectState'.
We don't call the event SnaphotConfirmed because of a name collision with Server. Should probably qualify Server at some point. In AckSn: * we use SnapshotHasBeenConfirmedEvent to notify that a snapshot has been confirmed * we use SnapshotRequestDecided to notify we requested a snapshot * we use SnapshotHasBeenConfirmed when the receiving party is not the leader or its localTxs are null.
…ell. The goal is to keep the ordering constraints of Combined only inside the HeadLogic.
The pruning of the transactions can be done in the events aggregation function instead of in the update function so we do not need to carry transactions and utxo in the event anymore
This really hilight the symetry between events and server outputs.
Make it more clear what the event is about. Move the agregation to the aggregate and not in the update anymore.
…event We want to avoid requiring a ledger to interpret the StateChanged events to allow for simpler consumption downstream (maybe even in non-haskell contexts).
This way it is more clear that this event will always be the outcome of a ReqTx being processed.
This way, any downstream code of the events can decide whether they want to consume that event or not.
The NoOutcome identity is not needed and now all update outcomes must be any of the other data constructors. Also make Combine the mappend of Outcome to make the code a bit easier to read.
5281614
to
8e597d0
Compare
To prepare for event-sourced persistency we first revamp the HeadlLogic to make it event-sourced.