-
Notifications
You must be signed in to change notification settings - Fork 296
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
multi: avoid sending spend nftns via goroutines. #2836
Conversation
0785236
to
3cd3187
Compare
5dc3b56
to
962c417
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.
I still have more review to do, but there are already enough issues to address I figured I'd get them posted.
@@ -18,9 +19,9 @@ const ( | |||
) | |||
|
|||
var ( | |||
// spendConsumerDepsBucketName is the name of the bucket used in storing | |||
// spend journal consumer dependencies. | |||
spendConsumerDepsBucketName = []byte("spendconsumerdeps") |
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.
What about the data that is already there in the old bucket? Perhaps there is an upcoming commit that migrates it and then removes the bucket, but I don't see that being done 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.
The old bucket remains, this only changed the variable name: spendConsumerDepsBucketName
-> spendConsumerDependenciesBucketName
.
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.
Right, but that's my point. The actual data in the old bucket is still there and nothing is being done with 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.
the bucket is still being used: the spend pruner is initialized retrieving all the persisted dependencies from the bucket and the the entries in the bucket are updated periodically based on dependencies that can be pruned (see filterPrunableDependents
). See call sites for dbUpdateSpendConsumerDependencies
and dbFetchSpendConsumerDependencies
.
Update: yes you're right, the data in the bucket prior to the spend heights addition will just be sitting there. Working on it.
blockchain/indexers/spendconsumer.go
Outdated
tipHash *chainhash.Hash | ||
mtx sync.Mutex | ||
} | ||
|
||
// NewSpendConsumer initializes a spend consumer. | ||
func NewSpendConsumer(id string, tipHash *chainhash.Hash, queryer ChainQueryer) *SpendConsumer { | ||
func NewSpendConsumer(id string, tipHash *chainhash.Hash, queryer SpendDependencyQueryer) *SpendConsumer { |
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 breaking change to the public API and therefore can't be done without a major module bump to blockchain
.
I was a little bit unsure about if this case could sneak through since the new interface is a strict subset of previous interface and thus anything that satisfied the old one will still satisfy the new one, however, after discussing it in the dev channel, it is indeed a breaking change regardless.
As @jrick noted, a caller might be creating a closure that would break.
Translating it to a concrete example for this change, imagine if a caller had the following code:
var x func(string, *chainhash.Hash, indexers.ChainQueryer) = indexers.NewSpendConsumer
This change would cause the caller's code to fail to compile, which is expressly forbidden without a major module bump.
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.
Noted, will wait for #2903 to be merged and rebase.
|
||
// Persist all spend height map entries. | ||
for blockHash, height := range spendHeights { | ||
var b [8]byte |
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.
Any reason for using 8 when you're encoding a uint32
which is 4 bytes?
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.
Nope, should be 4 bytes there. Updating.
} | ||
|
||
// Persist all spend height map entries. | ||
for blockHash, height := range spendHeights { |
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.
All of the code in this commit is chalk full of incorrect usage. The range
statement overwrites the variables with each iteration and you're slicing them directly and passing them on to db code, so with any type of async behavior, it will be a race and overwrite things incorrectly.
package main
import (
"encoding/binary"
"fmt"
"time"
)
func put(k []byte, v []byte) {
go func(k, v []byte) {
time.Sleep(time.Second)
fmt.Printf("k: %x, v: %x\n", k, v)
}(k, v)
}
func main() {
foo := map[[32]byte]uint32{
[32]byte{0x00}: 0,
[32]byte{0x01}: 1,
[32]byte{0x02}: 2,
}
for hash, height := range foo {
var b [4]byte
binary.LittleEndian.PutUint32(b[:], height)
put(hash[:], b[:])
}
time.Sleep(time.Second * 2)
}
Output:
k: 0200000000000000000000000000000000000000000000000000000000000000, v: 00000000
k: 0200000000000000000000000000000000000000000000000000000000000000, v: 01000000
k: 0200000000000000000000000000000000000000000000000000000000000000, v: 02000000
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.
Noted, thanks. Updating.
|
||
// Persist all spend height map entries. | ||
for _, blockHash := range keys { | ||
err := heightsBucket.Delete(blockHash[:]) |
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.
Incorrect as per previous comments.
Thanks for the review 👍🏾, will start addressing the issues soon. |
684dcb4
to
720304d
Compare
Needs a rebase please. |
This refactors the spend consumer by updating the queryer interface it uses. The chain queryer interface has been replaced by a lighter one: SpendDependencyQueryer.
This adds a spend journal height bucket for tracking the heights of spend journal entries needed by consumers. database related helpers have also been added.
This revers shorthands of the word dependency in function/mthod names and comments to avoid confusion.
This adds a spend consumer to track spend dependencies needed for invalidating and reconsidering blocks.
This updates call sites sending spend journal notifications to the spend journal pruner to avoid sending notifications via goroutines. This should preserve the message ordering and avoid possibly spinning up an unbounded number of goroutines. The spend pruner has also been refactored to batch spend prunes instead of processing single notifications individually.
This updates the chainSetup function to return a startup function. Call sites have been updated accordingly.
This adds generateDependencySpendHeights to generate the associated spend heights for existing spend dependencies before the introduction of spend heights. Associated tests have been added.
720304d
to
66bde17
Compare
I see this was updated, but it doesn't look like it was fully rebased to play nicely with #2961 and hence the latest master. It's touching a lot of spend pruner code that is no longer in master. I'll try to review the updates around it in the mean time, but it does need to be rebased to account for the removal of the spend pruner. |
Noted, will update soon. |
After a closer look the changes made here were focused on the spend pruner, I initially thought the startup func additions made to |
This updates call sites sending spend journal notifications to the spend journal pruner to avoid sending notifications via goroutines. This should preserve the message ordering and avoid possibly spinning up an unbounded number of goroutines.
The spend pruner has also been refactored to batch spend prunes instead of processing single notifications individually.