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

maindb v4 #868

Open
wants to merge 56 commits into
base: feat/db
Choose a base branch
from
Open

maindb v4 #868

wants to merge 56 commits into from

Conversation

libotony
Copy link
Member

@libotony libotony commented Oct 29, 2024

Description

This PR upgrades maindb to v4. It greatly reduces the storage space occupied and significantly reduces the This PR upgrades maindb to v4. It greatly reduces the storage space occupied and significantly reduces the synchronization time.

  • For MPT
    • The encoding and storage format of nodes are comprehensively improved, reducing the storage space used to store the state trie by 30%. Moreover, the speed of encoding and decoding nodes has been greatly improved.
    • Introducing the concept of Version to identify nodes, which simplifies the implementation code.
    • Optimize the trie interface to make it easier to maintain.
    • Remove leafbank stuff.
    • Improve generation-based node cache to reduce the pressure of GC.
    • Improve node storage key encoding.
  • Optimize block indexing and storage format.

Check out commits for more detail.

Type of change

Please delete options that are not relevant.

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

Checklist:

  • My code follows the style guidelines of this project
  • I have performed a self-review of my code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes
  • New and existing E2E tests pass locally with my changes
  • Any dependent changes have been merged and published in downstream modules
  • I have not added any vulnerable dependencies to my code

@libotony libotony requested a review from a team as a code owner October 29, 2024 08:20
@libotony libotony changed the title Tony/maindb v4 maindb v4 Oct 29, 2024
@libotony libotony marked this pull request as draft October 29, 2024 08:20
@libotony
Copy link
Member Author

This PR is a successor of PR #635.

@darrenvechain
Copy link
Member

@libotony could you sync the branch with feat/db so that the CI runs? I made that change in the GHA, which got merged into feat/db just now

@libotony
Copy link
Member Author

@libotony could you sync the branch with feat/db so that the CI runs? I made that change in the GHA, which got merged into feat/db just now

Done.

@libotony libotony marked this pull request as ready for review October 29, 2024 08:55
muxdb/cache_test.go Outdated Show resolved Hide resolved
@libotony libotony closed this Nov 1, 2024
@libotony libotony reopened this Nov 1, 2024
@libotony libotony closed this Nov 1, 2024
@libotony libotony reopened this Nov 1, 2024
@libotony
Copy link
Member Author

libotony commented Nov 1, 2024

Wired that tests won't run with this PR only, might be caused by the on-pull-request.yaml wasn't updated in the master branch, but after PR #871 was opened, CI was trigger for targeting master then with the same commit hash of this PR, the workflow was also linked here.

@darrenvechain
Copy link
Member

darrenvechain commented Nov 5, 2024

@libotony looks like GET /block/{revision} is returning null in the transactions field, according to the e2e tests

-   "transactions": Any<Array>,
+   "transactions": null,
image

@libotony
Copy link
Member Author

libotony commented Nov 6, 2024

@libotony looks like GET /block/{revision} is returning null in the transactions field, according to the e2e tests

-   "transactions": Any<Array>,
+   "transactions": null,
image

Should be fixed.

@@ -24,6 +24,7 @@ require (
github.com/prometheus/client_model v0.5.0
github.com/prometheus/common v0.45.0
github.com/qianbin/directcache v0.9.7
github.com/qianbin/drlp v0.0.0-20240102101024-e0e02518b5f9
Copy link
Member

Choose a reason for hiding this comment

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

Seems the dependency is quite small, should we just copy the files over to this repo?

Copy link
Member Author

Choose a reason for hiding this comment

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

wdyt? @qianbin

Copy link
Member

Choose a reason for hiding this comment

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

sure

}

// AddNodeBlob adds encoded node blob into the cache.
func (c *cache) AddNodeBlob(keyBuf *[]byte, name string, path []byte, ver trie.Version, blob []byte, isCommitting bool) {
Copy link
Member

Choose a reason for hiding this comment

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

Looks like most of these methods are exported but not used outside the package. Maybe I'm wrong ? Probably doesn't matter too much anyhow

Copy link
Member Author

Choose a reason for hiding this comment

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

Itself can be treated a standalone "class" to other ones, even it's inside the same package. IMHO, it's a good practice to define public/private functions even it's internal only.

Copy link
Member

Choose a reason for hiding this comment

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

I'm good with either, but what if the other structs used an interface (like Cache) instead of *cache. Would that help, or make will it make it into future-proving land ?

chain/repository.go Show resolved Hide resolved
chain/repository.go Show resolved Hide resolved
Comment on lines 317 to 320
if err := repo.AddBlock(b, receipts, 0, false); err != nil {
t.Fatal(err)
}
if err := repo.SetBestBlockID(b.Header().ID()); err != nil {
Copy link
Member

Choose a reason for hiding this comment

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

Likely worth removing the repo.SetBestBlockID and setting repo.AddBlock(b, receipts, 0, true)

Copy link
Member Author

Choose a reason for hiding this comment

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

Removed repo.SetBestBlockID from most test files in commit ebed07a, but there is still a usage for it in https://github.com/vechain/thor/blob/tony/maindb-v4/bft/engine_test.go#L164, which I think is not avoidable.

Copy link
Member

Choose a reason for hiding this comment

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

Agreed 👍

There's a tester pattern that can be applied in such cases where

type RepositoryTest struct {
Repository
}

func (r *RepositoryTest) SetBestBlockID(...) {}

I don't think there are a lot of other testing methods no point in using this, but wanted to highlight it if needed in the future!

api/debug/debug_test.go Outdated Show resolved Hide resolved
api/transactions/transactions.go Show resolved Hide resolved
chain/chain.go Show resolved Hide resolved
chain/chain.go Show resolved Hide resolved
chain/chain.go Show resolved Hide resolved
chain/persist.go Outdated Show resolved Hide resolved
chain/repository.go Outdated Show resolved Hide resolved
Comment on lines +59 to +72
{
tx, meta, err := c.GetTransaction(tx1.ID())
assert.Nil(t, err)
assert.Equal(t, tx1Meta, meta)
assert.Equal(t, tx1.ID(), tx.ID())
}
{
r, err := c.GetTransactionReceipt(tx1.ID())
assert.Nil(t, err)
got, _ := rlp.EncodeToBytes(r)
want, _ := rlp.EncodeToBytes(tx1Receipt)
assert.Equal(t, want, got)
}

Copy link
Member

Choose a reason for hiding this comment

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

Nit - Perhaps the implode func M can be reused here ? Or changed the M so it's kinda the same type test approach ?

Copy link
Member

Choose a reason for hiding this comment

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

I think it might be worth changing the error check with errors.Is

func (ldb *levelEngine) IsNotFound(err error) bool {
	return err == leveldb.ErrNotFound
}

Copy link
Member Author

Choose a reason for hiding this comment

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

My understanding here is comparing to the specific error and errors.Is will have more checks including wrapped errors.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, it's comparing to the instance of leveldb.ErrNotFound.

return err == leveldb.ErrNotFound will fail iferr := fmt.Errorf("this be error: %w", errNotFound)

errors.Is is meant to unwrap errors until it finds that type. So if for any reason the error is wrapped upstream, this won't be caught.

Comment on lines +220 to +228
if err := bulk.Write(); err != nil {
return nil, err
}
r.caches.summaries.Add(id, &summary)
return &summary, bulk.Write()
if asBest {
r.bestSummary.Store(&summary)
r.tick.Broadcast()
}
return &summary, nil
Copy link
Member

Choose a reason for hiding this comment

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

It looks like the saveBlock is also used during bootstrap - Could it be detrimental to cache those summaries at bootstrap time ?

Copy link
Member Author

Choose a reason for hiding this comment

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

Bootstrapping can be ignored here, as it's not a common case. And the behavior is not detrimental as LRU will evict the oldest entry.

Copy link
Member

Choose a reason for hiding this comment

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

Yes, I was considering if it could be an improvement to the bootstrap phase.
It looks like it's adding stuff to cache that will get (almost) immediately booted off ( given it bootstraps a lot of blocks).

Perhaps it's of negligible impact. I wish we had a benchmark test for bootstrapping

Comment on lines -79 to -80
repo.caches.txs = newCache(2048)
repo.caches.receipts = newCache(2048)
Copy link
Member

Choose a reason for hiding this comment

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

I probably missed the why, but it's no longer caching txs and receipts ?

Copy link
Member Author

Choose a reason for hiding this comment

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

You probably are right, based on the perf test results.

Comment on lines +306 to +308
func (r *Repository) getTransaction(key []byte) (*tx.Transaction, error) {
var tx tx.Transaction
if err := loadRLP(r.bodyStore, key, &tx); err != nil {
Copy link
Member

Choose a reason for hiding this comment

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

Did we miss the caching, or is it upstream ?

muxdb/cache.go Outdated
Comment on lines 53 to 60
if now-last > int64(time.Second*20) {
log1, ok1 := c.nodeStats.ShouldLog("node cache stats")
log2, ok2 := c.rootStats.ShouldLog("root cache stats")

if ok1 || ok2 {
log1()
log2()
}
Copy link
Member

Choose a reason for hiding this comment

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

I think this is fine, but can be simplified to just log at 20 seconds interval.
Looks like ShouldLog only logs when the hitrate has changed and in this case both nodeStats and rootStats should have changed their hitrate.

I might be missing it, but I don't see the added value of checking if it should log or not. I'm starting to think it could be simpler to log in demand ?

muxdb/cache.go Outdated Show resolved Hide resolved
muxdb/trie.go Show resolved Hide resolved
muxdb/trie.go Show resolved Hide resolved
muxdb/backend.go Outdated Show resolved Hide resolved
paologalligit and others added 4 commits November 12, 2024 15:19
* feat: add thorclient

* refactor: remove roundTripper

* refactor: change null check

* clean: remove commented code

* feat: add account revision and pending tx

* fix: add licence headers and fix linter issue

* refactor: rename package

* refactor: change revision type to string

* refactor: rename GetLogs and GetTransfers to FilterEvents and FilterTransfers

* refactor: change FilterEvents and FilterTransactions request type to EventFilter

* Adding common.EventWrapper to handle channel errors

* tweak

* update rawclient + update account tests

* tidy up names

* update tests

* pr comments

* adding raw tx

* Tidy up method names and calls

* options client

* tweaks

* pr comments

* Update thorclient/common/common.go

Co-authored-by: libotony <[email protected]>

* pr comments

* Adding Subscriptions

* Pr comments

* adjust func orders

* pr comments

* changing subscribe to use the channel close vs multiple channels

* adding go-doc

* no error after unsubscribe

* pr comments

* checking status code is 2xx

* fix: change FilterTransfers argument

---------

Co-authored-by: otherview <[email protected]>
Co-authored-by: libotony <[email protected]>
* Refactor thor node

* thorchain allows insertion of blocks

* remove thorNode, added testchain

* clean up + comments

* adding license headers

* adding templating tests for thorclient

* Remove test event hacks

* remove types

* removed chain_builder + added logdb to testchain

* pr comments

* Update test/testchain/chain.go

Co-authored-by: libotony <[email protected]>

---------

Co-authored-by: libotony <[email protected]>
@libotony
Copy link
Member Author

Rebased branch feat/db with master then rebased tony/maindb-v4 with feat/db.

muxdb/trie.go Show resolved Hide resolved
Comment on lines 317 to 320
if err := repo.AddBlock(b, receipts, 0, false); err != nil {
t.Fatal(err)
}
if err := repo.SetBestBlockID(b.Header().ID()); err != nil {
Copy link
Member

Choose a reason for hiding this comment

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

Agreed 👍

There's a tester pattern that can be applied in such cases where

type RepositoryTest struct {
Repository
}

func (r *RepositoryTest) SetBestBlockID(...) {}

I don't think there are a lot of other testing methods no point in using this, but wanted to highlight it if needed in the future!

chain/chain.go Show resolved Hide resolved
chain/chain.go Show resolved Hide resolved
chain/chain.go Show resolved Hide resolved
chain/chain.go Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

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

Yes, it's comparing to the instance of leveldb.ErrNotFound.

return err == leveldb.ErrNotFound will fail iferr := fmt.Errorf("this be error: %w", errNotFound)

errors.Is is meant to unwrap errors until it finds that type. So if for any reason the error is wrapped upstream, this won't be caught.

Comment on lines +220 to +228
if err := bulk.Write(); err != nil {
return nil, err
}
r.caches.summaries.Add(id, &summary)
return &summary, bulk.Write()
if asBest {
r.bestSummary.Store(&summary)
r.tick.Broadcast()
}
return &summary, nil
Copy link
Member

Choose a reason for hiding this comment

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

Yes, I was considering if it could be an improvement to the bootstrap phase.
It looks like it's adding stuff to cache that will get (almost) immediately booted off ( given it bootstraps a lot of blocks).

Perhaps it's of negligible impact. I wish we had a benchmark test for bootstrapping

muxdb/cache.go Outdated Show resolved Hide resolved
muxdb/trie.go Show resolved Hide resolved
@otherview
Copy link
Member

Sorry, not sure what I did for the above comments to not stay in the thread 🙏

otherview and others added 6 commits November 18, 2024 16:16
* change

* reverted to previous format
* Add empty cache for inmem ops

* changing name to dummyCache
* changes

* removed log

* sleeping any way

* pr review
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants