-
Notifications
You must be signed in to change notification settings - Fork 684
mempool: Separate reactor from implementation #1043
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
Conversation
This reverts commit d3468a1.
mempool/clist_mempool.go
Outdated
// Concurrent linked-list of valid txs. | ||
// `txsMap`: txKey -> CElement is for quick access to txs. | ||
// Transactions in both `txs` and `txsMap` must to be kept in sync. | ||
txs *clist.CList |
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.
When generating documentation from code, it will be confusing if the comment is related to two fields. I suggest commenting both, even if there is some redundancy.
// Concurrent linked-list of valid txs. | |
// `txsMap`: txKey -> CElement is for quick access to txs. | |
// Transactions in both `txs` and `txsMap` must to be kept in sync. | |
txs *clist.CList | |
// Concurrent linked-list of valid txs. | |
// `txs` must to be kept in sync with `txsMap` . | |
txs *clist.CList | |
// `txsMap`: txKey -> CElement is for quick access to txs. | |
// `txsMap` must to be kept in sync with `txs` . |
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 use here the same arguments I have presented for PR #1010:
#1010 (review)
I think we should define and discuss a new mempool API before implementing the (breaking) changes in multiple PRs.
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 problem in the loop over the transactions, causing transactions to be skipped and violating the expected delivery order.
PR #1153 adds a test to show the problem and addresses the issue by bring the test for nil back.
Co-authored-by: Lasaro <lasaro@informal.systems>
* Adding a test for when peers are late * passes the test added in the previous commit * passes the test added in the previous commit * Fix iteration and typo * Making the sleep dependendant on a smaller sleep we want to wait for
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 believe that changing CheckTx
is necessary. The current interface is not sound: it takes as input a callback cb
which will be later called by another callback. This pattern is non-stanadrd and unecessarily complex. Moreover, cb
necessarily lives outside of the mempool. This should be avoided because changes against the mempool must be thread safe.
The new interface @hvanz is proposing returns a promise. The promise captures the fact that the side effects of CheckTx
are applied asynchronously. This change to the interface is not breaking because mempool#CheckTx
is only used internally.
On the other hand, I am not in favor of the introduction of the CListIterator
, modifying the way the reactor accesses the mempool. As indicated in my review, this change is not trivial because the underlying list is a non-standard concurrent list. I recommend to add these changes in a different commit with proper testing on them. Even better, before doing that, I recommend to actually model and verify the clist
abstraction.
mempool/clist_mempool.go
Outdated
@@ -130,6 +152,18 @@ func (mem *CListMempool) EnableTxsAvailable() { | |||
mem.txsAvailable = make(chan struct{}, 1) | |||
} | |||
|
|||
func (mem *CListMempool) SetTxRemovedCallback(cb func(txKey types.TxKey)) { | |||
mem.removeTxOnReactorCb = cb |
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 a very restrictive interface. Add some defensive programming here, by checking it was not set already. Document what is expected. Visibility would be package level (but this does not exist in golang.)
// Safe for concurrent use by multiple goroutines. | ||
func (mem *CListMempool) TxsWaitChan() <-chan struct{} { | ||
return mem.txs.WaitChan() | ||
func (mem *CListMempool) NewIterator() Iterator { |
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.
Document this. As above, explain that it is a restricted interface.
mem.txsMap.Store(memTx.tx.Key(), e) | ||
atomic.AddInt64(&mem.txsBytes, int64(len(memTx.tx))) | ||
mem.metrics.TxSizeBytes.Observe(float64(len(memTx.tx))) | ||
func (mem *CListMempool) addTx(entry *Entry) { |
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.
Comment this method.
I suggest to pack methods according to their visibility levels, e.g., first public ones then private ones. Mixing things makes understanding the code difficult.
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 agree with sorting the methods by visibility, but this will require to change most of the file, and I don't want to do it in this PR.
lastElement *clist.CElement // last accessed element in the list | ||
nextEntryAvailable chan struct{} // channel to communicate that there is at least one entry available | ||
mtx sync.RWMutex | ||
} |
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 move this code to CList
.
There is no need to for the iterator to be thread safe. Each peer has its own iterator. Hence, the mtx
field can be dropped.
Besides that, I do not understand the logic of the nextEntryAvailable
field.
} | ||
|
||
return iter.lastElement.Value.(*Entry) | ||
} |
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 am concernend about this set of changes, regarding how the reactor iterates over the mempool. They should be part of a different commit.
Overall, this is non-trivial code. The underlying list is a non-standard concurrent list (not à la M. Scott). As a consequence, we should be very cautious when using it. At the very least, this new way to iterate should be tested in unit tests. I would even recommend to actually model the clist with Quint, before adding an iterator atop it.
Depends on #1169 |
Closing this PR as it is not clear that the proposed solution is correct. The main change introduced by this PR is an iterator for In the proposed solution, there is an iterator for each peer that reads from |
Closes #1042
Depends on #1010
In
Mempool
interface:NewIterator
for iterating the mempool entriesSetLogger
Changes in the mempool reactor:
NewReactor
now takes aMempool
interface as a parameter, instead ofCListMempool
,TxsWaitChan
andTxsFront
for iterating throughCListMempool
's entries,NextWaitChan
andNext
frommempoolTx
.PR checklist
.changelog
(we use unclog to manage our changelog)docs/
orspec/
) and code comments