Skip to content
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 support for multiple staging areas #1633

Merged
merged 62 commits into from
Mar 29, 2021
Merged

Add support for multiple staging areas #1633

merged 62 commits into from
Mar 29, 2021

Conversation

svarogg
Copy link
Collaborator

@svarogg svarogg commented Mar 25, 2021

This PR enables to address #1516

@codecov
Copy link

codecov bot commented Mar 25, 2021

Codecov Report

Merging #1633 (71afd1d) into v0.10.0-dev (c12e180) will increase coverage by 0.23%.
The diff coverage is 82.32%.

Impacted file tree graph

@@               Coverage Diff               @@
##           v0.10.0-dev    #1633      +/-   ##
===============================================
+ Coverage        59.95%   60.18%   +0.23%     
===============================================
  Files              516      532      +16     
  Lines            20469    20673     +204     
===============================================
+ Hits             12273    12443     +170     
- Misses            6239     6267      +28     
- Partials          1957     1963       +6     
Impacted Files Coverage Δ
...tures/consensusstatestore/consensus_state_store.go 66.66% <0.00%> (ø)
...structures/consensusstatestore/virtual_utxo_set.go 44.11% <ø> (+2.45%) ⬆️
domain/consensus/factory.go 91.17% <ø> (ø)
...ain/consensus/processes/coinbasemanager/payload.go 68.96% <ø> (ø)
domain/consensus/test_consensus.go 65.59% <ø> (-4.22%) ⬇️
domain/consensus/test_consensus_render_to_dot.go 0.00% <0.00%> (ø)
...ersselectedtipstore/headers_selected_tips_store.go 46.66% <46.66%> (ø)
...res/blockheaderstore/block_header_staging_shard.go 51.61% <51.61%> (ø)
...tructures/utxodiffstore/utxo_diff_staging_shard.go 54.54% <54.54%> (ø)
...uctures/daablocksstore/daa_blocks_staging_shard.go 54.83% <54.83%> (ø)
... and 108 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update c12e180...71afd1d. Read the comment docs.


for _, shard := range sa.shards {
if shard == nil {
continue
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would a shard ever be nil? Since this may hide bugs, I'd prefer it if you returned an error here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See response to next comment, it should explain this one as well.


// GetOrCreateShard attempts to retrieve a shard with the given name.
// If it does not exist - a new shard is created using `createFunc`.
func (sa *StagingArea) GetOrCreateShard(shardID StagingShardID, createFunc func() StagingShard) StagingShard {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since GetOrCreateShard is only ever called from within a store, please consider passing a pointer to a store here instead of a shardID. This would:
a. Remove the need to maintain an IDs list
b. Disallow programmers from accessing shards they shouldn't have access to (e.g. in one store, another store's shard)
Of course, make sure that this does not harm performance.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please note sa.shards is an array.
This was done deliberately, because map access is much slower (I've tested this with integers, I'd expect the same to happen with pointers, because those are basically integers as well).

This, as you mentioned, has some worse properties, but using a map adds around 20~30 overhead for kaspad normal operation.

This also explains why in Commit a shard might be nil - we are going over all the array every time we Commit. Some slots in the array could be empty.

I've added some comments explaining this, please LMK if there's anywhere else you think should be augmented, or if you have some ideas to how we can improve on this.

}

func (ads *acceptanceDataStore) stagingShard(stagingArea *model.StagingArea) *acceptanceDataStagingShard {
return stagingArea.GetOrCreateShard(model.StagingShardIDAcceptanceData, func() model.StagingShard {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please extract the create function to a separate method.
I'm pretty sure that currently, every time stagingShard and similar functions are called, Go creates a new function along with a closure over the current scope. I imagine that to be quite costly.
Please do run benchmarks to make sure I'm not wrong though.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BenchmarkStagingShardClosure-8     	467047196	         2.787 ns/op
BenchmarkStagingShardNoClosure-8   	405023798	         2.945 ns/op

They are virtually the same (actually NoClosure is a bit slower, I think I know why, but is miniscule difference anyway).
So seems like there's no point.

For reference, here's implemention of NoClosure:

func (ads *acceptanceDataStore) createStagingShard() model.StagingShard {
	return &acceptanceDataStagingShard{
		store:    ads,
		toAdd:    make(map[externalapi.DomainHash]externalapi.AcceptanceData),
		toDelete: make(map[externalapi.DomainHash]struct{}),
	}
}
func (ads *acceptanceDataStore) stagingShardNoClosure(stagingArea *model.StagingArea) *acceptanceDataStagingShard {
	return stagingArea.GetOrCreateShard(model.StagingShardIDAcceptanceData, ads.createStagingShard).(*acceptanceDataStagingShard)
}

}).(*acceptanceDataStagingShard)
}

func (adss acceptanceDataStagingShard) Commit(dbTx model.DBTransaction) error {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please go over all the shard types and make sure that the method receivers are pointers.

@@ -11,22 +11,18 @@ import (
)

var bucket = database.MakeBucket([]byte("blocks"))
var countKey = database.MakeBucket(nil).Key([]byte("blocks-count"))
var countKey = database.MakeBucket(nil).Key([]byte("blocks-countCached"))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you mean to do this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope :/
Good catch. This would have turned my change to db-breaking.

@@ -72,3 +56,21 @@ func (css *consensusStateStore) deserializeTips(tipsBytes []byte) ([]*externalap

return serialization.DBTipsToTips(dbTips)
}

func (csss *consensusStateStagingShard) commitTips(dbTx model.DBTransaction) error {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please consider moving this to consensus_state_staging_shard.go.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm... this and commitVirtualUTXODiff were deliberately left in their respective files, since consensus-state-store is split to file according to themes, not types.

Thus I felt it makes more sense to leave them here, if you disagree lmk, I don't mind moving it to consensus_state_staging_shard.go too much

}

func (css *consensusStateStore) commitVirtualUTXODiff(dbTx model.DBTransaction) error {
hadStartedImportingPruningPointUTXOSet, err := css.HadStartedImportingPruningPointUTXOSet(dbTx)
func (csss *consensusStateStagingShard) commitVirtualUTXODiff(dbTx model.DBTransaction) error {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please consider moving this to consensus_state_staging_shard.go.


func (hstss headersSelectedTipStagingShard) Commit(dbTx model.DBTransaction) error {
if hstss.newSelectedTip == nil {
return nil
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that not all stores are necessarily committed, please check whether this should return an error instead.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The moment someone does a read from a store it's staging shard is created.
Therefore, a staging shard might exist (and then it's Commit function will be called) while nothing needs to be commited.

// Since Commit happens in a DatabaseTransaction, a StagingArea is atomic.
type StagingArea struct {
shards [StagingShardIDLen]StagingShard
isCommited bool
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Typo: isCommited -> isCommitted

@svarogg svarogg merged commit f145140 into v0.10.0-dev Mar 29, 2021
@svarogg svarogg deleted the 1516-staging-area branch March 29, 2021 07:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants