Skip to content

tests: Create or use existing properly initialized NodeContexts #20323

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

Merged
merged 2 commits into from
Dec 9, 2020

Conversation

dongcarl
Copy link
Contributor

@dongcarl dongcarl commented Nov 5, 2020

This is part 1/n of the effort to de-globalize ChainstateManager

Reviewers: Looking for tested/Code-Review/plain-ACKs

Context

In many of our tests, we manually instantiate NodeContexts or ChainstateManagers in the test code, which is error prone. Instead, we should create or use existing references because:

  1. Before we de-globalize ChainstateManager, much of our code still acts on g_chainman (our global ChainstateManager), sometimes even when you're calling a method on a specific instance of ChainstateManager! This means that we may act on two instances of ChainstateManager, which is most likely not what we want.
  2. Using existing references (initialized by the {Basic,}TestingSetup constructors) means that you're acting on objects which are properly initialized, instead of "just initialized enough for this dang test to pass". Also, they're already there! It's free!
  3. By acting on the right object, we also allow the review-only assertions in future commits of de-globalize ChainstateManager to work and demonstrate correctness.

Some more detailed debugging notes can be found in the first commit, reproduced below:

Previously, the validation_chainstatemanager_tests test suite
instantiated its own duplicate ChainstateManager on which tests were
performed.

This wasn't a problem for the specific actions performed in
that suite. However, the existence of this duplicate ChainstateManager
and the fact that many of our validation static functions reach for
g_chainman, ::Chain(state|)Active means we may end up acting on two
different CChainStates should we write more extensive tests in the
future.

This change adds a new ChainTestingSetup which performs all
initialization previously done by TestingSetup except:

1. Mempool sanity check frequency setting
2. ChainState initialization
3. Genesis Activation
4. {Ban,Conn,Peer}Man initialization

Means that we will no longer need to initialize a duplicate
ChainstateManger in order to test the initialization codepaths of
CChainState and ChainstateManager.

Lastly, this change has the additional benefit of allowing for
review-only assertions meant to show correctness to work in future work
de-globalizing g_chainman.

In the test chainstatemanager_rebalance_caches, an additional
LoadGenesisBlock call is added as MaybeReblanaceCaches eventually calls
FlushBlockFile, which tries to access vinfoBlockFile[nLastBlockFile],
which is out of bounds when LoadGenesisBlock hasn't been called yet.

-----

Note for the future:

In a previous version of this change, I put ChainTestingSetup between
BasicTestingSetup and TestingSetup such that TestingSetup inherited from
ChainTestingSetup.

This was suboptimal, and showed how the class con/destructor inheritance
structure we have for these TestingSetup classes is probably not the
most suitable abstraction. In particular, for both TestingSetup and
ChainTestingSetup, we need to stop the scheduler first before anything
else. Otherwise classes depending on the scheduler may be referenced
by the scheduler after said classes are freed. This means that there's
no clear parallel between our teardown code and C++'s destructuring
order for class hierarchies.

Future work should strive to coalesce (as much as possible) test and
non-test init codepaths and perhaps structure it in a more fail-proof
way.

@jnewbery
Copy link
Contributor

jnewbery commented Nov 5, 2020

Concept ACK. Thanks for moving ChainTestingSetup into validation_chainstatemanager_tests.cpp!

@practicalswift
Copy link
Contributor

Concept ACK

@DrahtBot
Copy link
Contributor

DrahtBot commented Nov 5, 2020

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Conflicts

Reviewers, this pull request conflicts with the following ones:

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

Copy link
Contributor

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

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

Code review ACK 3938763. Obvious ACK. This is just test changes and the first 4 commits from #20158 with commit descriptions tweaked and improved

Copy link
Contributor

@jnewbery jnewbery left a comment

Choose a reason for hiding this comment

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

utACK 3938763

Some suggestions to remove unneeded code inline.

Copy link
Contributor

@promag promag left a comment

Choose a reason for hiding this comment

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

Concept ACK.

@dongcarl dongcarl force-pushed the 2020-10-chainman-fixes branch from 3938763 to 12399f0 Compare November 16, 2020 22:07
@dongcarl
Copy link
Contributor Author

Addressed a few comments.


fuzz CI failing because of #20188 (comment).

@dongcarl dongcarl force-pushed the 2020-10-chainman-fixes branch from 12399f0 to 1319b17 Compare November 24, 2020 15:52
Copy link
Contributor

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

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

Code review ACK 1319b17. Only changes since last review were suggested simplifications to ChainTestingSetup constructor

@ryanofsky
Copy link
Contributor

Hopefully as a test-only cleanup PR with a lot of other changes building on top it (#20158), this can be merged soon

@maflcko
Copy link
Member

maflcko commented Dec 1, 2020

Reason I didn't merge this is that this overlaps with the test changes here: #19425 (review). Though there have been outstanding questions and those questions also apply to this pull (in the extent that they overlap).

Copy link
Contributor

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

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

Reason I didn't merge this is that this overlaps with the test changes here: #19425 (review). Though there have been outstanding questions and those questions also apply to this pull (in the extent that they overlap).

Thanks. I guess I need to rebase #19425. I'll check out the test changes too there and make updates if needed to make sure they are compatible with this PR.

This PR just takes approach of having fewer duplicate disconnected state objects inside tests, so I don't think there are real questions about it or major drawbacks. But if anyone has concerns or questions (even vague ones), it'd be good to post something so Carl can reply and this work doesn't languish

@@ -122,6 +167,7 @@ BOOST_AUTO_TEST_CASE(chainstatemanager_rebalance_caches)
{
LOCK(::cs_main);
c1.InitCoinsCache(1 << 23);
assert(c1.LoadGenesisBlock(Params()));
Copy link
Contributor

Choose a reason for hiding this comment

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

In commit "test: Add new ChainTestingSetup and use it" (43c880a)

Would suggest using BOOST_REQUIRE instead of assert or splitting as bool loaded = c1.LoadGenesisBlock(Params()); assert(loaded);. Writing code with side effects in an assert adds some ambiguity because behavior will depend on value of NDEBUG

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Oh! Didn't know about NDEBUG... So when NDEBUG is set, the code inside assert(...) may be optimized out? Or is it the case that only the result isn't checked/asserted?

@jnewbery
Copy link
Contributor

jnewbery commented Dec 4, 2020

utACK 1319b17

@dongcarl dongcarl force-pushed the 2020-10-chainman-fixes branch from 1319b17 to 11fac8b Compare December 4, 2020 21:25
@dongcarl
Copy link
Contributor Author

dongcarl commented Dec 4, 2020

Pushed 1319b17 -> 11fac8b:

  1. Rebased over master for refactor: CTxMempool constructor clean up #20222
  2. Changed asserts in tests to BOOST_REQUIRE as recommended: tests: Create or use existing properly initialized NodeContexts #20323 (comment)
  3. Extracted out testing app initialization and destruction logic into TestingAppInitSequence and TestingAppDestructionSequence to improve sync and decrease maintenance burden between ChainTestingSetup and TestingSetup to address: tests: Create or use existing properly initialized NodeContexts #20323 (comment)

In (3), ordering-wise, the script-checking thread initialization was moved up to before the chainstate gets initialized and RPC command registration was moved down. Neither of which should have any tangible effect on the correctness of initialization.

I can also remove the bool chain_testing_only argument to TestingAppDestructionSequence as it's a mostly idempotent sequence, somehow I like the self-documentation and symmetry. Let me know what seems better!

@jnewbery
Copy link
Contributor

jnewbery commented Dec 7, 2020

I've had a quick look through the new branch and find the TestingAppInitSequence() and TestingAppDestructionSequence() less clear than the previous version. In general functions that take a bool argument which substantially changes the logic inside that function (in this case chain_testing_only), suggest that the abstraction may be wrong.

Was there an actual problem with the previous branch, or are you just trying to reduce code duplication?

Copy link
Member

@maflcko maflcko left a comment

Choose a reason for hiding this comment

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

left some comments

Copy link
Contributor

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

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

Code review ACK 11fac8b. Changes since last review are deduping test init functions and replacing asserts (thanks!).

Other review suggestions for splitting up functions instead of using a bool arguments seem reasonable.

Another idea (not sure whether it is good or applicable): an alternative to calling pairs of functions in constructors and destructors is to just add class members. Member constructors will run in containing class's default constructor, and same with destructors. Seems like this would allow getting rid of the 4 one-line constructor/destructors.

Previously, the validation_chainstatemanager_tests test suite
instantiated its own duplicate ChainstateManager on which tests were
performed.

This wasn't a problem for the specific actions performed in
that suite. However, the existence of this duplicate ChainstateManager
and the fact that many of our validation static functions reach for
g_chainman, ::Chain(state|)Active means we may end up acting on two
different CChainStates should we write more extensive tests in the
future.

This change adds a new ChainTestingSetup which performs all
initialization previously done by TestingSetup except:

1. RPC command registration
2. ChainState initialization
3. Genesis Activation
4. {Ban,Conn,Peer}Man initialization

Means that we will no longer need to initialize a duplicate
ChainstateManger in order to test the initialization codepaths of
CChainState and ChainstateManager.

Lastly, this change has the additional benefit of allowing for
review-only assertions meant to show correctness to work in future work
de-globalizing g_chainman.

In the test chainstatemanager_rebalance_caches, an additional
LoadGenesisBlock call is added as MaybeReblanaceCaches eventually calls
FlushBlockFile, which tries to access vinfoBlockFile[nLastBlockFile],
which is out of bounds when LoadGenesisBlock hasn't been called yet.

-----

Note for the future:

The class con/destructor inheritance structure we have for these
TestingSetup classes is probably not the most suitable abstraction. In
particular, for both TestingSetup and ChainTestingSetup, we need to stop
the scheduler first before anything else. Otherwise classes depending on
the scheduler may be referenced by the scheduler after said classes are
freed. This means that there's no clear parallel between our teardown
code and C++'s destructuring order for class hierarchies.

Future work should strive to coalesce (as much as possible) test and
non-test init codepaths and perhaps structure it in a more fail-proof
way.
@dongcarl dongcarl force-pushed the 2020-10-chainman-fixes branch from 11fac8b to 81137c6 Compare December 8, 2020 20:01
@dongcarl
Copy link
Contributor Author

dongcarl commented Dec 8, 2020

Thanks everyone for the reviews. The latest push 81137c6 is now rebased over #19485.

This PR is intended to just move things around a bit for the rest of the ChainstateManager de-globalization to work properly. I believe we've gotten a bit stuck on the intricacies of these TestingSetup abstractions.

Personally, I don't particularly care how the abstraction works here. The TestingSetup classes and their inheritance hierarchy is a somewhat poor abstraction of how our initialization and de-initialization logic work. Therefore, I'd like to just fix this particular problem for now and move on.

I do fully intend on coming back to the initialization codepaths later, most likely after having moved a couple of mutable statics into classes they logically belong in, and having a larger PR that coalesces and cleans all of this up.


In this latest push I:

  1. Reverted back to my original solution of having this inheritance chain: TestingSetup -> ChainTestingSetup -> BasicTestingSetup. This has the advantage of not having to introduce any new functions.
    Compared to my last try at making this work, I now take advantage of the idempotency of our teardown sequence and have ~ChainTestingSetup teardown all application logic while ~TestingSetup inherits from it.
  2. Put ChainTestingSetup back in setup_common so that future tests can use it as requested here. (Sorry John, I knew you weren't the biggest fan of this 😕)

jnewbery: Was there an actual problem with the previous branch, or are you just trying to reduce code duplication?

There wasn't an actual problem with the previous branch, MarcoFalke brought up the concern that it was harder to maintain duplicate codepaths, so this previous push was a (perhaps poorly conceived) attempt at doing so. I agree w/re the bool argument and have since reverted to something similar to how I originally attempted this which avoids using a bool argument.

Copy link
Contributor

@ryanofsky ryanofsky left a comment

Choose a reason for hiding this comment

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

Code review ACK 81137c6. This change is simpler after the rebase because wallet & bench commits are dropped.

Along with the rebase, new test init functions were dropped again in response to #20323 (comment), which is fine. I think in the longer run more init code can be moved into RAII classes, and we won't need these intricate initialization functions and class hierarchies (tests could just state their resource requirements as variable declarations).

@maflcko
Copy link
Member

maflcko commented Dec 9, 2020

Thanks for shuffling this around for over a month

ACK 81137c6 looking excellent now 🐩

Show signature and timestamp

Signature:

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

ACK 81137c60fe looking excellent now 🐩
-----BEGIN PGP SIGNATURE-----

iQGzBAEBCgAdFiEE+rVPoUahrI9sLGYTzit1aX5ppUgFAlwqrYAACgkQzit1aX5p
pUjLNQv9G0SWcw2rzItf1EIttkPPy6CX5BxM3WRp4MY+hW42LikQIcTm236LyD9t
/e4homRxM0DT1q6/JKin3RP4y1O4zpLswFaz8nNH7ScmLGyTNdb/HUDxLLNqnynb
TYZYpZP6Md/EWjCoF2kKEBlVmzUeehagq3L9gQ7udXTMZtMu2z1jP6v7/TLe6XxA
HqABCQn9BJYcD5dqHETg4TYBP2hG5bUnh5rmJWHnn0ifdEmnS4cbExtHCL9nxQpF
1evmfgdk6nXzkkEllJsOm2tYVGhacW5Z483w/jFveBPmVJgduYmAO5mddMreqV/5
/1Gegw1YQnb6LmEyHhsD2POxlidXksNTOEAyghOwylFFPN8467R/LQEXr8ltstQ9
NnhkoCNVgSDBuzkdTB+n8S6gfQyTp2T3klrqr3GHJIPlRikRLGI5bOwtjtZsFkGX
j5508lSIANRoUKGv3nbfWL47l8he+dYMfCvpqkLWxRPqGT+MnIQV9aRKKxLcSFIb
czC392Ra
=hAKu
-----END PGP SIGNATURE-----

Timestamp of file with hash 1fd492318f7f8e7f0f9f62fce5042d824a2c5c3fcb368e2027f02582ea8ed27e -

@maflcko
Copy link
Member

maflcko commented Dec 9, 2020

  1. Put ChainTestingSetup back in setup_common so that future tests can use it as requested here. (Sorry John, I knew you weren't the biggest fan of this confused)

Ok, let's wait for @jnewbery 's green light before merge

@jnewbery
Copy link
Contributor

jnewbery commented Dec 9, 2020

ACK 81137c6

@maflcko maflcko merged commit a3586d5 into bitcoin:master Dec 9, 2020
sidhujag pushed a commit to syscoin/syscoin that referenced this pull request Dec 9, 2020
@bitcoin bitcoin locked as resolved and limited conversation to collaborators Feb 15, 2022
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants