-
Notifications
You must be signed in to change notification settings - Fork 18
Transaction substatus #51
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
Transaction substatus #51
Conversation
4879a6c
to
25ec21f
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.
Hi @matthew1001,
I've made some suggestions on the detail of this new implementation.
I am expecting to see deletion of the old implementation in this one too - and probably we want this to get in before v1.2 cuts, so we don't have a double-migration of the data structures.
pkg/apitypes/managed_tx.go
Outdated
func (mtx *ManagedTX) AddSubStatus(ctx context.Context, subStatus TxSubStatus) { | ||
// See if this status exists in the list already | ||
for _, entry := range mtx.SubStatusHistory { | ||
if entry.Status == subStatus { |
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.
There seems to be a split approach here that I can't reconcile.
The code below suggests we are aiming to limit the number of unique entries to 50 (I guess the most recent 50?). This fits with an approach where (like k8s describe
) we de-duplicate when something is in exactly the same state, but try to provide an accurate a history as possible.
However, the comparison on this line only uses the TxSubStatus
line for comparison bare - without all the interesting things that might make it unique, such as the TX Hash. So it's impossible to get more than 6 entries in the table. In fact the entries don't really contain any detail at all now.
I think we need to choose whether to give detailed information, or not. I believe we should give detailed information - as we do before this PR.
I assert the following are a must, which certainly does introduce the potential for more than 50 records:
- The current Hash of the transaction we are tracking
- The receipt
ProtocolID
The number of confirmations is slightly interesting, as we could choose to update that on an existing record, rather than produce a new record.
I like the approach you've got of adding the raw information on the JSON, and allowing translation of that into human readable form by the consumer of the API (such as a UX, or application).
So I think I'm proposing:
type TxSubStatusEntry struct {
Occurrences int `json:"occurrences"` // updates every time we add a duplicate
FirstOccurrence *fftypes.FFTime `json:"first"` // set when first added to array
LastOccurrence *fftypes.FFTime `json:"last"` // set when first added, and updated on each duplicate
Status TxSubStatus `json:"status"` // part of uniqueness
TX string `json:"tx"` // part of uniqueness
Receipt string `json:"receipt"` // part of uniqueness
GasPrice *fftypes.JSONAny `json:"gasPrice,omitempty"` // part of uniqueness - only present on Status==RetrievedGasPrice
Confirmations int `json:"confirmations"` // updates as more are added
}
func (s *TxSubStatusEntry) Key() string {
return fmt.Sprintf("%s:%s:%s:%s", s.Status, s.TX, s.Receipt, s.GasPrice.String())
}
Maybe a higher level point as to why I feel quite strongly about ^^^: if the system is looping through fundamentally different sub-step, then I think it's really important that the information shows this.
e.g. I think between (A) and (B) I feel like people need (B):
Information preservation A
- ✅ validated (1)
- ✅ estimatedgas (2)
- ✅ submitted (3)
Information preservation B
- ✅ validated (1)
- ✅ estimatedgas (1) GasPrice=12345
- ✅ submitted (1) TXHash=12345
- ✅ estimatedgas (1) GasPrice=12999
- ✅ submitted (2) TXHash=23456
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.
... there's a subtle point here too, that I just realized I'm making.
I think the list of sub-status's is a little inconsistent. For some steps we've got before+after states, for others we've just got after states. I think we should be very consistent on that.
The example above is we just "after" states. I didn't include any "before" states ... apart from one initial state which is actually something that happens before we accept the transaction - I was wondering if we might get stuck in that state and it would be useful to have a count there.
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.
oh, darn - and that suddenly reminded me there might be an error that we're getting preventing us moving to the next state, so I guess there's a decision as to whether we record errors in special "before" states (doubling the length of the array)... or by just updating the previous state with the error preventing us from progressing.
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.
... which maybe means we should only have "before" states, rather than only have "after" states:
Information preservation C
- ✅ estimatingGas (3) LastError="Node unavaialable", LastErrorTime=10:57
- ✅ submitting (1) TXHash=12345, GasPrice=12345
- ✅ estimatingGas (1)
- ✅ submitted (2) TXHash=23456, GasPrice=12999
pkg/apitypes/managed_tx.go
Outdated
|
||
// Prevent crazy run-away situations by limiting the number of unique sub-status | ||
// types we will keep | ||
if len(mtx.SubStatusHistory) >= 50 { |
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.
Assuming we retain a limit on the buffer, I think we need to:
- Make the limit configurable
- Discard the oldest record, not the newest
Thanks for the comments. So I think there are 3 broad categories for discussion perhaps in person if easier:
|
Signed-off-by: Matthew Whitehead <[email protected]>
Signed-off-by: Matthew Whitehead <[email protected]>
Signed-off-by: Matthew Whitehead <[email protected]>
f654308
to
9201251
Compare
…ditional information pertaining to each action Signed-off-by: Matthew Whitehead <[email protected]>
9201251
to
2e5ad24
Compare
Thanks for the comments & discussion @peterbroadhurst. The new commit I think addresses many of the concerns over how we want to structure the data, how much of it to keep, and how much detail to retain etc. There's one last tidy-up thing I could do with a pointer on - passing the configurable I'm also seeing struct fields serialise in alphabetical order, not the order defined in the struct. Suspect I'm missing something simple as it would be nice for most important fields to be near the top when serialising. |
|errorHistoryCount|The number of historical errors to retain in the operation|`int`|`25` | ||
|maxHistoryActions|The number of actions to store per historical status updates|`int`|`50` | ||
|maxHistoryCount|The number of historical status updates to retain in the operation|`int`|`50` | ||
|maxHistorySummaryCount|The number of historical status summary records to retain in the operation|`int`|`50` |
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 wonder if we really need this configuration parameter, as this should be bounded by the number of types of status we have.
Info string `json:"info,omitempty"` | ||
Error string `json:"error,omitempty"` | ||
MappedReason ffcapi.ErrorReason `json:"reason,omitempty"` | ||
// TxSubStatus is an intermediate status a transaction may go through |
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.
Noting this list can be extended by policy engine implementations.
|
||
// This action hasn't been recorded yet in this sub-status. Add a new entry for it. | ||
if len(currentSubStatus.Actions) >= 50 { // TODO - get from config | ||
log.L(ctx).Warn("Number of unique sub-status actions. New action detail will not be recorded.") |
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 needs to be FIFO I think too
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.
Thanks @matthew1001 - this seems like a great step forwards.
I note the PR does not contain a summary of the new architecture / data structure that is proposed, so we'll need to document that separately somewhere (or update this PR).
I do have detail proposals on the TODOs, and some detail thoughts on the code, but I suspect the best way for me to express these efficiently is with code. So I'm going to merge this as an excellent step forwards, then make my suggestions as a PR for you to react to.
Yes agreed re. the documentation of the structures. I'll add something to this PR to describe them. Thanks for the code suggestions via #58. |
Closes #49
A diagram and more detail about the changes under this PR are available in the FireFly Micro board at https://miro.com/app/board/uXjVOWHk_6s=/?moveToWidget=3458764544594470770&cot=14 and in the original issue #49
The PR introduces additional granularity to transaction progress by introducing sub-status types, and actions that can cause a change in sub-status:
These are incorporated into the ManagedTX data structure with a history field and a historySummary field (see Miro board for more detailed description).