-
Notifications
You must be signed in to change notification settings - Fork 47
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
refactor account/watcher
and add mocks/tests
#316
Conversation
5c32556
to
c17d06b
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.
Very nice to see the new mocks in action. The refactor looks pretty good to me, just a few suggestions for the naming and how to simplify it even more.
c17d06b
to
1727f64
Compare
1727f64
to
6d47746
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.
Looking good! Way better with the EventHandler
interface IMO. See my comment about getting rid of the Handlers
struct as well.
After that we're good to go 🎉
account/watcher/interfaces.go
Outdated
|
||
// Controller is the interface used by other components to communicate with the | ||
// watcher. | ||
|
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: extra newline.
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, it's removed in a later commit. Can move it here.
account/manager.go
Outdated
@@ -153,13 +153,17 @@ func NewManager(cfg *ManagerConfig) *Manager { | |||
quit: make(chan struct{}), | |||
} | |||
|
|||
m.watcherCtrl = watcher.NewController(&watcher.Config{ | |||
ChainNotifier: cfg.ChainNotifier, | |||
eventHandler := watcher.NewEventHandler(&watcher.Handlers{ |
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 just export those handle*
methods on the manager and just pass m
here instead of adding this wrapper struct.
@guggero I don't know why I cannot reply under every comment so I will reply to all of them here:
yes, I would avoid both: return a non exported struct type and return an interface. However, given that this is only used in
(Y) |
6d47746
to
ad06024
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.
Great initiative on this 💯 Some final nits, otherwise LGTM 🎉
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 refactors, and tests! My only major comment is about the expiry watcher, other than that looks good!
account/watcher/watcher.go
Outdated
} | ||
|
||
// OverdueExpirations handles the expirations for the given block. | ||
func (w *expiryWatcher) OverdueExpirations(blockHeight uint32) { |
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 a better design could be to have a priority queue of accounts where the priority is defined by the block height. This would allow us to expire everything before and at the specified block height.
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 alternatively make newblock/expiry atomic.
continue | ||
} | ||
|
||
err := w.handlers.HandleAccountExpiry( |
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 need to lock for HandleAccountExpiry
? An alternative could be to collect expiring accounts in the lock then expire them outside the lock.
account/watcher/watcher.go
Outdated
|
||
// NewBlock updates the current bestHeight. | ||
func (w *expiryWatcher) NewBlock(bestHeight uint32) { | ||
w.bestHeight = bestHeight |
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.
Since expiry happens when the height is changed, it'd be possible to simplify the interface a bit and do the height change and the expiry handling atomically under the same lock. By only expiring the current block if two blocks come right after each other in succession we may miss an expiration (almost no chance in practice but still).
Feedback updated 👍 For some of the question:
Totally agree. I think it is something we can add later though. Do you know any good implementation that allows update the priority of the elements in it?
Yes I thought about this but it cannot happen because the controller was calling the two funcs (new block + overdue expirations) before being able to receive another block (iterate the select). I made them atomic, better put it together so we do not get confused and use the API wrong |
ad06024
to
9a8fa5e
Compare
9a8fa5e
to
f45fa65
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.
Just did a high level pass since there are already 2 reviewers. Great work on interface refactors + mock :)
// that is confirmation or spend. | ||
type Watcher struct { | ||
// controller implements the Controller interface | ||
type controller 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.
Add compile time assertion that we satisfy the controller interface?
var _ Controller = (*controller)(nil)
account/watcher/interfaces.go
Outdated
HandleAccountExpiry(*btcec.PublicKey, uint32) error | ||
} | ||
|
||
// ExpiryWatcher is the interface for the component in charge of the accounts' expiration. |
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: wrap at 80?
These look a bit over
expectedErr string | ||
}{{ | ||
name: "Watch account spend happy path", | ||
// TODO (positiveblue): add tests for `cancel` logic |
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.
happening in this PR or a follow up?
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 💯
} | ||
|
||
delete(expirationsPerHeight, bestHeight) | ||
c.watcher.NewBlock(uint32(newBlock)) |
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.
One thing to keep in mind is that if HandleAccountExpiry
blocks for some time, then the blockChan
might get clogged.
Use the new watcher interface in the account manager instead of of a specific struct type.
Split watcher logic in three pices: - Controller: API + message dispatching - ExpiryWatcher: handle account expirations - EventHandler: implementation for each handler
f45fa65
to
1856f79
Compare
account/manager
take an interface for the watcher instead of a explicit type.