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

core: generic test suite for AncientStore #23707

Closed
wants to merge 6 commits into from

Conversation

MichaelMure
Copy link

Refactor the freezer test suite into one that can be used for other AncientStore implementation. Ultimately that simplify greatly having an alternative implementation.

To achieve that, a subset of previously private errors have been promoted as part of the AncientStore interface.

Untangle some code in the process: I believe freezer can now be extracted from rawdb if that's
deemed beneficial.

CC @holiman, I believe you are the one to bug about that.

@holiman
Copy link
Contributor

holiman commented Oct 12, 2021

I'd like to get #23566 merged before this one.
Aside from that, during the triage , the discussion came up: what is the point of this PR? We're a bit hesitant to commit to a specific API for the ancients. Are you writing an alternative implementation for the ancientstore?

@@ -190,15 +182,15 @@ func (f *freezer) HasAncient(kind string, number uint64) (bool, error) {
if table := f.tables[kind]; table != nil {
return table.has(number), nil
}
return false, nil
return false, ethdb.ErrAncientUnknownKind
Copy link
Author

Choose a reason for hiding this comment

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

Please notice this change. It looked like a mistake to me so I fixed it.

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually, looks like this method can be deleted from the codebase now

Copy link
Author

Choose a reason for hiding this comment

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

I removed it in ba1cee60f69bb2e3b6788f5ef4ffb0de923b5731

@MichaelMure
Copy link
Author

Are you writing an alternative implementation for the ancientstore?

I am, but I don't expect a stable interface. I'm just trying to smooth things from a development perspective:

  • have a test suite able to validate both the canonical implementation and an alternative
  • allow to have an alternative implementation returning the same errors in the same conditions (which means having those errors public)
  • untangle a bit the current code:
    • avoid assumptions in the test code about freezer (there was some tiny ones, irrelevant IMHO) and test only the public API
    • make the storage concerns (freezer) more orthogonal regarding the higher level concerns (rawdb and up). Or rather the other way around: rawdb should not care about freezer's internals.

@MichaelMure
Copy link
Author

Speaking of AncientStore's interface, I'd like to bring something to your attention.

One thing I noticed is that it does pretty much force the implementation to have a separate storage for each kind, the same way freezer does. I've been considering storing all the kinds together per block with the assumption that if one is accessed the others are likely to be accessed after that (so some form of prefetching/caching), but that's not something that looks doable reasonably with this interface.

That said, I don't have enough insight at the moment to say if that approach would be valuable so that might be entirely irrelevant.

@holiman
Copy link
Contributor

holiman commented Oct 12, 2021

with the assumption that if one is accessed the others are likely to be accessed after that (so some form of prefetching/caching), but that's not something that looks doable reasonably with this interface.

I'm not sure that's a valid assumption. When someone is syncing from you, they might first get header in ranges from you, then spread out the body download among a set of peers, and you'll serve bodies here and there which may or may not line up with what you delivered earlier.

The PR I linked above, however, with the batched reads, is more in line with your proposed schema, however. It gives the reader a consistent interface, meaning that when doing a batched read, the individual fields are guaranteed to be in lock-step with one-another (but not forcing the fields to all be loaded from disk when one is read)

@MichaelMure
Copy link
Author

Following on this idea, I made two small changes for the KeyValueStore:

  • return a common error on Get when not found. This allow to distinguish between not found and an actual read error. Also allow for fancy layering on top of a KeyValueStore.
  • document Delete as idempotent (it's already the case in the current implementations).

@holiman
Copy link
Contributor

holiman commented Oct 25, 2021

The #23566 PR is now merged, I guess this will need to be updated?

@MichaelMure
Copy link
Author

@holiman I rebased and removed HasAncient. It should be good now.

@MichaelMure
Copy link
Author

Note: it would make sense to extend TestAncientStoreSuite for the new API introduced in #23566.

@MichaelMure
Copy link
Author

I completed the test suite to cover those. I'm really done this time! (maybe?)

@MichaelMure
Copy link
Author

@holiman would it be time to merge this?

@holiman
Copy link
Contributor

holiman commented Nov 10, 2021

We discussed this yesteday on our PR triage, and there remains some hesitancy about this, again. Same as I wrote earlier:

Aside from that, during the triage , the discussion came up: what is the point of this PR? We're a bit hesitant to commit to a specific API for the ancients.

Merging this change makes it harder for us to change the API, and we're not sure that we've arrived at the Perfect API yet. Things may (and do) still change, and having to worry about the third-part impact another public API is cumbersome.

@MichaelMure
Copy link
Author

I promise I really don't expect this API to be stable. As a maintainer myself I know it's uncomfortable to expose internals but that's not what I'm trying to do here.

I'm merely trying to make this API easier to use, maintain and build on. This PR document some existing behavior, allow to distinguish some failure mode (not found vs actual error) and generalize/clean the test suite.

I believe those changes are useful, regardless if I'm using the API as a third party or not.

@MichaelMure
Copy link
Author

If that helps, those interfaces could be marked as internal/unstable.

@egalano
Copy link

egalano commented Dec 1, 2021

Hi @holiman. What would it take to get this PR accepted? Michael is on the Infura team and as he mentioned, we're working on an alternative ancient store experiment that we'd like to share if the experiment works out after load testing. We are fully content to accept that we are building on an internal API which will change in the future and we don't expect this to be maintained as a public API. If there are additional changes you'd like us to make to this PR before it can be merged please let us know.

@holiman
Copy link
Contributor

holiman commented Dec 2, 2021

@egalano we'll discuss it internally again next triage

@holiman
Copy link
Contributor

holiman commented Dec 7, 2021

Triage discussion: we'll do another round of review on this PR

@karalabe
Copy link
Member

We've discussed to get this PR in, but we need to finish and merge a prerequisite first (#23954) as that also changes the code and interfaces a bit.

@holiman
Copy link
Contributor

holiman commented Jan 11, 2022

We're tentatively agreeing that this PR could be an ok addition, but as said earlier: the freezer API is still not stable. So this PR has bitrotted a bit and needs a rebase, and further, we have more changes in the pipeline, such as #23954

@MichaelMure
Copy link
Author

@holiman should I rebase this PR before or after #23954 get merged?

@MichaelMure
Copy link
Author

@holiman I rebased and updated my PR now that #23954 is merged. Can we move towards merging and avoid bitrot?

Copy link
Member

@rjl493456442 rjl493456442 left a comment

Choose a reason for hiding this comment

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

Left a few comments.

ethdb/database.go Show resolved Hide resolved
core/rawdb/freezer_batch.go Outdated Show resolved Hide resolved
core/rawdb/freezer_table_test.go Outdated Show resolved Hide resolved
@@ -129,9 +154,11 @@ type AncientWriter interface {
// AncientWriteOp is given to the function argument of ModifyAncients.
type AncientWriteOp interface {
// Append adds an RLP-encoded item.
// Returns ErrAncientOutOrderInsertion if attempting to insert items out of order.
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 kind should also be checked and return the ErrAncientUnknownKind if necessary?

Copy link
Author

Choose a reason for hiding this comment

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

Hmmm. freezerBatch currently panic if an unknown kind is used. While that would make sense to return ErrAncientUnknownKind, it also would be a programming error to provide an unknown kind rather than a runtime problem, so a panic seems more appropriate.
Which one do you prefer?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think it's fine to panic, if that's what we already do. Writing wrong kind is definitely a huge programming error.

Copy link
Author

Choose a reason for hiding this comment

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

It's ready to merge then.

ethdb/database.go Outdated Show resolved Hide resolved
ethdb/leveldb/leveldb.go Show resolved Hide resolved
ethdb/leveldb/leveldb.go Show resolved Hide resolved
ethdb/snapshot.go Outdated Show resolved Hide resolved
@rjl493456442
Copy link
Member

btw, I just want you know that we may change the database interface again which introduces the ancient store routing. #24481 My PR is not reviewed right now, but it's kind of necessary.

Refactor the freezer test suite into one that can be used for other AncientStore implementation.

To achieve that, a subset of previously private errors have been promoted as part of the AncientStore interface.

Untangle some code in the process: I believe freezer can now be extracted from rawdb if that's
deemed beneficial.
This allow to distinguish between not found and an actual read error. Also allow for
fancy layering on top of a KeyValueStore.

Also, document KeyValueWriter.Delete as idempotent.
@MichaelMure
Copy link
Author

I think it's good to go now. I can still adjust #23707 (comment) if I get an answer.

btw, I just want you know that we may change the database interface again which introduces the ancient store routing.

That's completely fine. Again, I'm not expecting to have a stable interface, I'm just trying to make things easier to work with for all of us: well defined interfaces and reusable test suite.

@fjl
Copy link
Contributor

fjl commented Jun 25, 2024

A similar test suite was finally added by #29135, in package core/rawdb/ancienttest

@fjl fjl closed this Jun 25, 2024
@MichaelMure
Copy link
Author

Thanks for letting me know, but also, quite some wasted effort and time ...

@fjl
Copy link
Contributor

fjl commented Jun 25, 2024

I don't necessarily see it that way. We sometimes leave PRs open to be reminded that there is demand for a feature. At the time this was originally proposed, we weren't sure where we'd be going with the ancients interface. Now we now better, and have refactored the interface to be usable for more things than just storing historical blocks.

At the same time, I can understand it's frustrating to see us coming to our senses way late :)

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.

7 participants