-
Notifications
You must be signed in to change notification settings - Fork 106
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 Tx Indexer Extension #1143
Add Tx Indexer Extension #1143
Conversation
|
||
var _ indexer.SuccessfulTxIndexer = &successfulTxIndexer{} | ||
|
||
type successfulTxIndexer struct { |
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.
IMO these implementing the indexer interface (tokenvm
one as well) seems weird to me because this is not an indexer. I'm wondering if this metrics code should go back to living in the Controller
code.
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.
We could rename it as well. We could more accurately call it an AcceptedSubscriber
.
This would be more accurate to exactly what it's doing and an indexer would be one thing you could build as an AcceptedSubscriber
. Similarly, you could pass in an acceptedActionHandler
or similar, which would be just this for the MorpheusVM and would also maintain the in-memory order book in the case of TokenVM.
if err != nil { | ||
return vm.Config{}, nil, nil, nil, nil, nil, nil, nil, err | ||
} | ||
c.txIndexer = indexer.NewTxIndexer( |
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 prefer if we just had a no-op implementation of the indexer interface that was used instead if GetStoreTransactions
was set here
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 is just keeping the same pattern that was there before. The for loop currently does two things, it iterates and indexes every transaction and then for each successful transaction it performs some action for each of the multi-actions that were included.
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.
Alternatively, if we switch to AcceptedSubscriber
, then the accepted action indexer can just be completely separate.
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.
Yeah I would prefer it be decoupled from each other
extensions/indexer/indexer.go
Outdated
type SuccessfulTxIndexer interface { | ||
Accepted(ctx context.Context, tx *chain.Transaction, result *chain.Result) error | ||
} |
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 if we go with the route of removing the metrics code out of indexer implementations we should be able to remove this interface as well
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'll move the actions to their own accepted subscriber as suggested here #1143 (comment)
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.
Still leaving this extra interface in, so that each controller can pass in its own successful tx handler. The alternative would be for both of them to just implement the full interface. Either is fine imo, lmk if you'd prefer the latter.
extensions/indexer/indexer.go
Outdated
Accepted(ctx context.Context, tx *chain.Transaction, result *chain.Result) error | ||
} | ||
|
||
type TxIndexer struct { |
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.
If we're going to make this an extension that other vms will pull in it probably makes sense to add unit tests for this
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.
Wasn't sure if that should be part of this PR, but can add it
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 we should make adding a unit test a separate PR because you currently need a builder in order to construct a stateless block...
We need to clean a lot of this code up to make writing unit tests more reasonable, so I'd prefer to defer this to a separate PR and keep the scope of this one a little bit smaller.
707dad3 fixes https://github.com/ava-labs/hypersdk/actions/runs/9963000960/job/27528179448?pr=1143#step:5:433 where I did not not invoke the combined subscriber correctly (still called the tx indexer instead) |
Digging into this a bit more, this happens for the TokenVM tests when the I can also fix this test by inserting a sleep statement between marking the block as accepted and invoking the Line 213 in a061629
This race condition is not introduced by this change, but it does appear to happen reliably with this change iff the I think it's reasonable to swap the order, so that this passes CI and add a TODO calling out the race condition in the integration test. |
|
||
var _ indexer.SuccessfulTxSubscriber = (*actionHandler)(nil) | ||
|
||
type actionHandler struct { |
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.
Should we call this something else...? Something related metrics or something?
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.
acceptedActionMetricsHandler? This is a bit too long imo and I slightly prefer actionHandler in this case unless you have an alternative suggestion.
extension/indexer/tx_indexer.go
Outdated
|
||
type noopTxIndexer struct{} | ||
|
||
func NewNoopTxIndexer() *noopTxIndexer { |
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.
nit: NoOp
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.
Done
extension/indexer/tx_indexer.go
Outdated
return true, t, success, d, fee, nil | ||
} | ||
|
||
type noopTxIndexer struct{} |
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.
nit: export type so caller can use this type if it wants to. Applicable to rest of concrete-types in this package
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 disagree with this. This is intentionally hiding that detail assuming callers choose to create this type and don't need to know the details or change anything about it after the fact.
Open to changing my mind on this though
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.
- If you don't want to let the caller know about the type for some reason, then you can just return the interface. Returning an unexported struct makes the type awkward to use. You can't embed, wrap, return a non-exported type from a calling package.
- At the same time what's the disadvantage of exporting the type? Returning a concrete type gives more flexibility to the caller to use it either as the interface or as the concrete type. I think we should let consumers of the package decide how they want to use our api (this way they can use our indexer interface, wrap it with their own, or use the type).
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.
Exported
subscribers []AcceptedSubscriber | ||
} | ||
|
||
func NewCombinedAcceptedSubscriber(subscribers ...AcceptedSubscriber) *CombinedAcceptedSubscriber { |
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.
Can we call this function + this type func NewAcceptedSubscribers(...) *AcceptedSubscribers
?
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 feel like combined is more explicit than adding -s
, which seems easier to miss.
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.
Updated
extension/indexer/tx_indexer.go
Outdated
_ AcceptedSubscriber = (*noopTxIndexer)(nil) | ||
) | ||
|
||
var ( |
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.
nit: combine the two var
blocks
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 generally prefer a separate var
block for interface assertions, but don't see that used elsewhere in this repo and do see a combined var
block with another variable used in one place.
Will update.
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.
Done
|
||
var _ indexer.SuccessfulTxSubscriber = (*actionHandler)(nil) | ||
|
||
type actionHandler struct { |
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.
Similar comment as the corresponding comment in morpheusvm
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.
Renaming
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.
Done
a.c.orderBook.Add(chain.CreateActionID(tx.ID(), uint8(i)), tx.Auth.Actor(), action) | ||
case *actions.FillOrder: | ||
a.c.metrics.fillOrder.Inc() | ||
outputs := result.Outputs[i] |
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.
Wondering if the order-book updates should happen in their own subscriber since they are logically decoupled from the metrics
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 to do more than one thing here.
Doing this async (as it's handled prior to this PR as well) does introduce a race condition because the in-memory order book is not updated during block accept. We may want to support a SyncAcceptedSubscriber
and AsyncAcceptedSubscriber
to fix that.
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.
Yeah I think long term we're gonna try to get rid of the metrics in here and push it up into hypersdk anyways so... not too concerned
extension/indexer/tx_indexer.go
Outdated
GetTransaction(txID ids.ID) (bool, int64, bool, fees.Dimensions, uint64, error) | ||
} | ||
|
||
type txIndexer struct { |
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.
Could probably just call this DiskIndexer
. Feels weird to have the concrete-type just be the name of the interface.
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.
Renamed to txDBIndexer
and renaming the constructor as well
extension/indexer/tx_indexer.go
Outdated
) | ||
|
||
type TxIndexer interface { | ||
Accepted(ctx context.Context, blk *chain.StatelessBlock) error |
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.
Probably should just replace this line w/ an embed for the AcceptedSubscriber
interface it since that's the interface we're using to get access to accepted blocks
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.
Done
|
||
type TxIndexer interface { | ||
Accepted(ctx context.Context, blk *chain.StatelessBlock) error | ||
GetTransaction(txID ids.ID) (bool, int64, bool, fees.Dimensions, uint64, error) |
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.
Should we return a struct here instead? We can define an indexer.Tx
struct type or something that the indexer defines for indexed tx data.
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 is an interface, so we can swap a noop version in if tx indexing is disabled as per #1143 (comment)
return &AcceptedSubscribers{subscribers: subscribers} | ||
} | ||
|
||
func (c *AcceptedSubscribers) Accepted(ctx context.Context, blk *chain.StatelessBlock) error { |
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.
nit: update to a
since we updated this struct name
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.
Done
extension/indexer/tx_indexer.go
Outdated
return &txDBIndexer{db: db} | ||
} | ||
|
||
func (i *txDBIndexer) Accepted(_ context.Context, blk *chain.StatelessBlock) error { |
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.
nit: export the struct + rename the receiver
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.
Done
) | ||
|
||
type AcceptedSubscriber interface { | ||
Accepted(ctx context.Context, blk *chain.StatelessBlock) error |
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 don't think we need this context + in the other tx subscriber interface currently... we could leave it in since I think it's reasonable that you would want a context though (say if you were archiving into s3 or something).
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.
Ya I think this is reasonable to leave in as part of the interface
* Remove rejected from controller interface * Update mocks and remove unused mock_auth from mocks.mockgen.txt * Refactor tx indexing into indexer extension * Close txDB in morpheusvm on shutdown * Update name of unused result param to _ * fix lint * Update license year * Address nits * move action handler to its own accepted subscriber * Add interface check for noopTxIndexer * Fix typo * fix unused receivers * fix linting * invoke combined subscriber to fix integration test * move action handler prior to tx indexer to fix pre-existing race condition * Add TODO to integration test flagging flaky test * Noop -> NoOp * Address comments * Export NoOpTxIndexer * rename combinedAcceptedSubscriber AcceptedSubscribers
* Remove rejected from controller interface * Update mocks and remove unused mock_auth from mocks.mockgen.txt * Refactor tx indexing into indexer extension * Close txDB in morpheusvm on shutdown * Update name of unused result param to _ * fix lint * Update license year * Address nits * move action handler to its own accepted subscriber * Add interface check for noopTxIndexer * Fix typo * fix unused receivers * fix linting * invoke combined subscriber to fix integration test * move action handler prior to tx indexer to fix pre-existing race condition * Add TODO to integration test flagging flaky test * Noop -> NoOp * Address comments * Export NoOpTxIndexer * rename combinedAcceptedSubscriber AcceptedSubscribers
…eful shutdown * keep track of the last accepted block at the end of the `processAcceptedBlocks` func * handle error and panic * Create a new processed key and retrieve it to send to websocket if present on boot * use `lastProcessed` key * update metrics on block retrieval * set processed height at the end of processing and process block on reboot for each unprocessed block * remove processed block function is useless * break the block into a function and add blocks to the chan * add logs, return fatal error, fix cache miss * set bounds on `AcceptorSize` and `AcceptedBlockWindow` * [cli/spam] Incorporate `vryx-poc` Improvements (#1091) * fix withholding spelling * define prompt float * remove useless const * more cleanup * making progress * working on issue loop * turn this into an interface rather than providing this functions * making progress on core loop * code is compiling * bound inflight returns with channel * cleanup struct * remove resolved todo * update transfer to include memo * cleanup morephus cli definition * remove unused variables * cleanup tokenvm client * add issuance limiter * fix errors * don't add tx to block if too big * put more txs in flight * cleanup logging * add fix for fees * track outstanding txs * fix var order * fix returned balance * test fix * linter * add comments * resolve linter for morpheusvm * pass tokenvm linter * add memo to tx size * gci imports * trying again * integration tests now passing * Add TODO to fix race conditions in spam script --------- Co-authored-by: Aaron Buchwald <aaron.buchwald56@gmail.com> * Extract shared scripts (#1161) * chore: rebase on top of master * refactor: shellcheck * fix: update mockgen version to pass ci checks * fix: build script merge * fix: resolve issues in tests.lint.sh * fix: resolve imports and shellcheck issues * fix: shellcheck issues * fix: downgrade golangci-lint to pass ci * fix: separate unit testing scripts * fix: ci issues * fix: ci issues * fix: tokenvm static analysis * fixup: Remove merge conflicts --------- Co-authored-by: vikinatora <vikinatora@gmail.com> * fix comment (#1163) * Add commit signing as instruction in contributing.md (#1164) * Add Tx Indexer Extension (#1143) * Remove rejected from controller interface * Update mocks and remove unused mock_auth from mocks.mockgen.txt * Refactor tx indexing into indexer extension * Close txDB in morpheusvm on shutdown * Update name of unused result param to _ * fix lint * Update license year * Address nits * move action handler to its own accepted subscriber * Add interface check for noopTxIndexer * Fix typo * fix unused receivers * fix linting * invoke combined subscriber to fix integration test * move action handler prior to tx indexer to fix pre-existing race condition * Add TODO to integration test flagging flaky test * Noop -> NoOp * Address comments * Export NoOpTxIndexer * rename combinedAcceptedSubscriber AcceptedSubscribers * [cli/spam] Incorporate `vryx-poc` Improvements (#1091) * fix withholding spelling * define prompt float * remove useless const * more cleanup * making progress * working on issue loop * turn this into an interface rather than providing this functions * making progress on core loop * code is compiling * bound inflight returns with channel * cleanup struct * remove resolved todo * update transfer to include memo * cleanup morephus cli definition * remove unused variables * cleanup tokenvm client * add issuance limiter * fix errors * don't add tx to block if too big * put more txs in flight * cleanup logging * add fix for fees * track outstanding txs * fix var order * fix returned balance * test fix * linter * add comments * resolve linter for morpheusvm * pass tokenvm linter * add memo to tx size * gci imports * trying again * integration tests now passing * Add TODO to fix race conditions in spam script --------- Co-authored-by: Aaron Buchwald <aaron.buchwald56@gmail.com> * Extract shared scripts (#1161) * chore: rebase on top of master * refactor: shellcheck * fix: update mockgen version to pass ci checks * fix: build script merge * fix: resolve issues in tests.lint.sh * fix: resolve imports and shellcheck issues * fix: shellcheck issues * fix: downgrade golangci-lint to pass ci * fix: separate unit testing scripts * fix: ci issues * fix: ci issues * fix: tokenvm static analysis * fixup: Remove merge conflicts --------- Co-authored-by: vikinatora <vikinatora@gmail.com> * fix comment (#1163) * Add commit signing as instruction in contributing.md (#1164) * Add Tx Indexer Extension (#1143) * Remove rejected from controller interface * Update mocks and remove unused mock_auth from mocks.mockgen.txt * Refactor tx indexing into indexer extension * Close txDB in morpheusvm on shutdown * Update name of unused result param to _ * fix lint * Update license year * Address nits * move action handler to its own accepted subscriber * Add interface check for noopTxIndexer * Fix typo * fix unused receivers * fix linting * invoke combined subscriber to fix integration test * move action handler prior to tx indexer to fix pre-existing race condition * Add TODO to integration test flagging flaky test * Noop -> NoOp * Address comments * Export NoOpTxIndexer * rename combinedAcceptedSubscriber AcceptedSubscribers * Update vm/vm.go Co-authored-by: aaronbuchwald <aaron.buchwald56@gmail.com> Signed-off-by: Franfran <51274081+iFrostizz@users.noreply.github.com> * wrap err and fix restoring off-by-one * fix operator inverted * off-by-one on amount of accepted to restore * typo * Update vm/vm.go Co-authored-by: aaronbuchwald <aaron.buchwald56@gmail.com> Signed-off-by: Franfran <51274081+iFrostizz@users.noreply.github.com> * Update vm/vm.go Co-authored-by: aaronbuchwald <aaron.buchwald56@gmail.com> Signed-off-by: Franfran <51274081+iFrostizz@users.noreply.github.com> * use constants for MaxAcceptorSize and MinAcceptedBlockWindow and update error messages * add provided value in error message --------- Signed-off-by: Franfran <51274081+iFrostizz@users.noreply.github.com> Co-authored-by: Patrick O'Grady <prohb125@gmail.com> Co-authored-by: Aaron Buchwald <aaron.buchwald56@gmail.com> Co-authored-by: marun <maru.newby@avalabs.org> Co-authored-by: vikinatora <vikinatora@gmail.com> Co-authored-by: PolyMa <151764357+polymaer@users.noreply.github.com>
This PR refactors the tx indexing from the MorpheusVM/TokenVM controller code into a separate module under
extensions/indexer
This also fixes an apparent bug where the database created internally to the MorpheusVM/TokenVM for tx indexing would never be closed.