-
Notifications
You must be signed in to change notification settings - Fork 37.7k
Use mockable time everywhere in net_processing #20027
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
Conversation
1576f76
to
155a4de
Compare
155a4de
to
1281d7d
Compare
The following sections might be updated with supplementary metadata relevant to reviewers and maintainers. ConflictsReviewers, 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. |
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.
Concept ACK, but I don't like that this patch makes the code more fragile.
Concept ACK |
1281d7d
to
e5c0223
Compare
Concept ACK |
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.
ACK e5c0223
Checked conversion from GetTime()
to count_microsecounds(GetTime<>())
and that variables that are now mockable are only compared with mockable times.
|
||
self.test_tx_requests() | ||
# Run each test against new bitcoind instances, as setting mocktimes has long-term effects on when | ||
# the next trickle relay event happens. |
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'm not sure it's a good idea to link mockable time to network delays in general -- if you bump the mocktime by a hours/days that shouldn't risk triggering "hey this peer hasn't said anything for ages, let's disconnect" conditions, and equally if you backdate the mocktime for some reason, that shouldn't cause network tasks to say "oh, we decided we don't need to do anything until time t, which is apparently years away" and effectively hang. Having to tweak tests feels a bit like a canary...
My intuition for this was we have two sorts of delays -- short ones (<30s) that just prevent us busy looping when there's nothing to actually do and don't generally need to be precisely controlled, and long ones (minutes/hours/days) that are human visible and that we'll obviously need mocktime to test because minutes/hours/days is effectively forever.
But maybe we've really got two different sorts of mocking that we want to do -- "set the time to X" and it stays there until we're ready to set it to some different time "Y" (what we've got now), and "everything that was going to happen in the next 5 minutes, get it done now" (something more like what the mockscheduler
rpc does)? You could implement the latter by bumping the system clock (rather than replacing it), and only allowing the bump amount to increase.
I suspect at a minimum we'd want to switch to the std::chrono::time_point
abstraction and use different clocks so that we could tell these cases apart by type, but that doesn't seem like a good thing to dive into immediately before feature freeze. (I had a sketch I was working on a while back at https://github.com/ajtowns/bitcoin/commits/201908-systime fwiw)
And mocktime is only relevant for regtest, so the downsides of this approach are pretty limited (presumably to just these test case changes)
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 think we'll indeed a number of timers for different purposes, eventually:
- System time (possibly adjusted by peer data), for use in block and wallet transaction timestamps (as they have an absolute UNIX epoch as reference ).
- Steady time, mockable, for timing application level events (tx relay, pinging, addr rebroadcast, ...).
- Steady time, for avoiding busy looping (like select() timeout, message handler 100ms delay, ...).
The last one indeed I think shouldn't be mockable, but things like ping timeouts I think should be. It just becomes too complicated when multiple clocks are mixed within one layer of the code - even if it means using a mockable clock for some delays that are in practice very short.
As for dealing with the fact that making small delays mockable complicates testing, I think the best solution is a "add X to mock time offset, but keep the clock running", if that's what you meant.
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 disagree that system time (used for blocks and wallet txs) should not be mockable. For example, sometimes we want to create a deterministic blockchain, so the block times need to be fixed. I know it is possible to use the python block generator, but I don't see a strong reason to disallow system time to be mocked.
Edit: So it is also important to be able to say "set time to a constant" instead of just "add offset, but keep clock running".
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 think the idea was to have two ways of mocking -- fix the system time to X (and prevent it from advancing, so time goes t, t+1, t+2, t+3, X, X, X, X), or bump the system time by D (so time goes t, t+1, t+2, t+3, t+D+3, t+D+4, t+D+5).
ACK e5c0223 |
e5c0223
to
d7fad16
Compare
d7fad16
to
93fca5f
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.
ACK 93fca5f 🐎
Show signature and timestamp
Signature:
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
ACK 93fca5fbd84ff793625f1c2bd469cfee68aa07f1 🐎
-----BEGIN PGP SIGNATURE-----
iQGzBAEBCgAdFiEE+rVPoUahrI9sLGYTzit1aX5ppUgFAlwqrYAACgkQzit1aX5p
pUg9lwv9Hr28B7UGDp2zRH285YsHrhEiqoQTWbSpuL9dQPRWTAD4ygHN9qW4TbZt
DOsuHRZOAjFBinWH/gYIUUW3F8WvIchHMgNY+p4+IkNbYIAMruDmMvbDTldUq0Hu
n9QzyB6Oz2OW9p8hMFJTbPB+8rVwMwA4H8ZwooghiKNszd/sFVFi5j5Pxgd6XlFi
MDkYpUmS3wQt1Kb2ya4SyOVPdlGQpje9ASjqCNSDz+CJOoJ9fFRZ+txEgn5lgTKI
o/oPoNVd2ZIQamx79+1y4kzLmkBBxbxP96ulC6l1UCMA9kKzX1m6yafSgN4Lc3Wo
01G5qBGawYXtWb48LPQGEYhVVIM0AcukRvpMA1TBpXkVPKDtvO/8/QEqkMZRnmaI
C10s9QP3TNuGhkIY4AUwVQkFmQEbWaePNVVdMd/ee3WO1N2VKuiH/+9/zxpMq9cf
tuoffg+0Y2MhRxVFjey23gjDoea+OQjo2KfRApyhELaqejX48k9A3oUbwW7NrUYb
IznFTsZW
=b0jx
-----END PGP SIGNATURE-----
Timestamp of file with hash 03cb2a91902242d76811cd547cea6874233f1f46c555bd142069e3cbfa6b48fc -
44b17b2
to
b6834e3
Compare
ACK b6834e3 🌶 Show signature and timestampSignature:
Timestamp of file with hash |
utACK b6834e3 |
@@ -582,7 +582,7 @@ static bool MarkBlockAsReceived(const uint256& hash) EXCLUSIVE_LOCKS_REQUIRED(cs | |||
} | |||
if (state->vBlocksInFlight.begin() == itInFlight->second.second) { | |||
// First block on the queue was received, update the start download time for the next one | |||
state->nDownloadingSince = std::max(state->nDownloadingSince, GetTimeMicros()); | |||
state->nDownloadingSince = std::max(state->nDownloadingSince, count_microseconds(GetTime<std::chrono::microseconds>())); |
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.
seems like a backwards step turning neat code into lengthy code - why not use a function for this or change GetTimeMicros() itself?
Summary: ``` The fact that net_processing uses a mix of mockable tand non-mockable time functions made it hard to write functional tests for #19988. I'm opening this as a separate PR as I believe it's independently useful. In some ways this doesn't go quite as far as it could, as there are now several data structures that could be converted to std::chrono types as well now. I haven't done that here, but I'm happy to reconsider that. ``` Backport of [[bitcoin/bitcoin#20027 | core#20027]]. Test Plan: ninja all check-all Reviewers: #bitcoin_abc, deadalnix Reviewed By: #bitcoin_abc, deadalnix Differential Revision: https://reviews.bitcoinabc.org/D9542
The fact that net_processing uses a mix of mockable tand non-mockable time functions made it hard to write functional tests for #19988.
I'm opening this as a separate PR as I believe it's independently useful. In some ways this doesn't go quite as far as it could, as there are now several data structures that could be converted to
std::chrono
types as well now. I haven't done that here, but I'm happy to reconsider that.