-
Notifications
You must be signed in to change notification settings - Fork 37.7k
Manual block file pruning. #7871
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
Nice! |
What use cases are motivating this mode? |
Concept ACK
This is explained in the original issue, #7365: if you have another application that needs to consume the block data before it's gone. |
Should have read the issue, sorry. Concept ACK |
Concept ACK. Needs rebase. |
Rebased. |
/* This function is called from the RPC code for pruneblockchain */ | ||
void PruneBlockFilesManual(int manualPruneHeight) | ||
{ | ||
// stuff the prune height into a global variable, and flush |
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 usually get told off for using global variables. Curious to know when they are permitted and when they are discouraged.
Needs rebase. |
2695b56
to
518a942
Compare
@@ -1335,7 +1338,7 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) | |||
|
|||
// Check for changed -prune state. What we are concerned about is a user who has pruned blocks | |||
// in the past, but is now trying to run unpruned. | |||
if (fHavePruned && !fPruneMode) { | |||
if (fHavePruned && pruneMode==PRUNE_NONE) { |
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.
Many places, you left (pruneMode) in a boolean context. I don't see a need to change to == NONE here.
@@ -71,13 +71,14 @@ bool fImporting = false; | |||
bool fReindex = false; | |||
bool fTxIndex = false; | |||
bool fHavePruned = false; | |||
bool fPruneMode = false; | |||
PruneMode pruneMode = PRUNE_NONE; |
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.
A bit ugly to have the enum and variable differ only by case.
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 it's fine.
bool fIsBareMultisigStd = DEFAULT_PERMIT_BAREMULTISIG; | ||
bool fRequireStandard = true; | ||
bool fCheckBlockIndex = false; | ||
bool fCheckpointsEnabled = DEFAULT_CHECKPOINTS_ENABLED; | ||
size_t nCoinCacheUsage = 5000 * 300; | ||
uint64_t nPruneTarget = 0; | ||
unsigned int nManualPruneHeight = 0; |
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.
This appears to be used exclusively for passing a value from a caller to a callee, so it shouldn't be a global variable.
@@ -3842,10 +3843,44 @@ void UnlinkPrunedFiles(std::set<int>& setFilesToPrune) | |||
} | |||
} | |||
|
|||
/* This function is called from the RPC code for pruneblockchain */ | |||
void PruneBlockFilesManual(int manualPruneHeight) |
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.
int
is the wrong type here. It is only guaranteed to hold up to 32768, which isn't very useful for block heights.
Lock heights will break before 29 bits are exceeded, so I suggest using either unsigned long
or uint32_t
{ | ||
LOCK2(cs_main, cs_LastBlockFile); | ||
const CChainParams& chainparams = Params(); | ||
uint64_t nPruneAfterHeight = chainparams.PruneAfterHeight(); |
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.
We're trying to reduce calls to global Params() by passing things as arguments. This change has the opposite effect for no clear reason.
|
||
int height = params[0].get_int(); | ||
if (height < 0) { | ||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Negative block height."); |
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.
RPC_INVALID_PARAMETER
seems more appropriate here.
throw JSONRPCError(RPC_INTERNAL_ERROR, "Negative block height."); | ||
} else if ((unsigned int) chainActive.Height() < Params().PruneAfterHeight()) { | ||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Blockchain is too short for pruning."); | ||
} |
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.
Should this also check if the requested height is too close to the tip? Or maybe just return the height it was able to successfully prune up to...
"1. \"height\" (int, required) The block height to prune up to.\n"); | ||
|
||
if (pruneMode != PRUNE_MANUAL) { | ||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Cannot prune via RPC unless in manual prune mode."); |
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.
RPC_METHOD_NOT_FOUND
seems better for this (already used for wallet RPCs when the wallet is not enabled)
Oh, it might make sense to add "prunemode": "auto"/"manual" to |
Concept ACK My OpenTimestamps Server could use this! |
518a942
to
98470f9
Compare
Rebased and feedback addressed. I didn't make a couple of the suggested changes, though:
|
@@ -928,12 +928,15 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) | |||
return InitError(_("Prune cannot be configured with a negative value.")); | |||
} | |||
nPruneTarget = (uint64_t) nSignedPruneTarget; | |||
if (nPruneTarget) { | |||
if (nPruneTarget / 1024 / 1024 == 1) { // manual pruning: -prune=1 | |||
LogPrintf("Manual block pruning enabled. Use RPC call pruneblockchain=<height> to prune block and undo files.\n"); |
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.
Nit: Use RPC call pruneblockchain(height)
- otherwise the syntax can be easily confused with a command line option.
@@ -191,8 +191,10 @@ static const uint64_t nMinDiskSpace = 52428800; | |||
/** Pruning-related variables and constants */ | |||
/** True if any block files have ever been pruned. */ | |||
extern bool fHavePruned; | |||
/** True if we're running in -prune mode. */ | |||
extern bool fPruneMode; | |||
enum PruneMode {PRUNE_NONE = 0, PRUNE_AUTO, PRUNE_MANUAL }; |
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.
Let's use C++11 scoped enums in new code
enum struct PruneMode { NONE=0, AUTO, MANUAL };
Then refer to PruneMode::NONE
etc.
@@ -928,12 +928,15 @@ bool AppInit2(boost::thread_group& threadGroup, CScheduler& scheduler) | |||
return InitError(_("Prune cannot be configured with a negative value.")); | |||
} | |||
nPruneTarget = (uint64_t) nSignedPruneTarget; | |||
if (nPruneTarget) { | |||
if (nPruneTarget / 1024 / 1024 == 1) { // manual pruning: -prune=1 |
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.
Can we please store and compare the option value before multiplication? Doing a division here, though it achieves the correct effect, seems circuitous and unclear.
@@ -3317,7 +3320,7 @@ bool FindBlockPos(CValidationState &state, CDiskBlockPos &pos, unsigned int nAdd | |||
unsigned int nOldChunks = (pos.nPos + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; | |||
unsigned int nNewChunks = (vinfoBlockFile[nFile].nSize + BLOCKFILE_CHUNK_SIZE - 1) / BLOCKFILE_CHUNK_SIZE; | |||
if (nNewChunks > nOldChunks) { | |||
if (fPruneMode) | |||
if (pruneMode == PRUNE_AUTO) |
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.
Are you sure this only needs to be done in auto-pruning mode?
@@ -115,7 +115,7 @@ UniValue importprivkey(const JSONRPCRequest& request) | |||
if (request.params.size() > 2) | |||
fRescan = request.params[2].get_bool(); | |||
|
|||
if (fRescan && fPruneMode) | |||
if (fRescan && pruneMode) |
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.
As you already have to change every line on which the variable occurs anyhow, I'd prefer explicitly comparing the enumeration against a value instead of using it like a boolean: e.g. pruneMode != PruneMode::NONE
. This makes it clear to developers reading the code that this is an enumeration and not a boolean and can avoid them introducing silly bugs.
void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight) | ||
{ | ||
LOCK2(cs_main, cs_LastBlockFile); | ||
if (chainActive.Tip() == NULL || pruneMode != PRUNE_MANUAL) { |
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.
Calling this function with pruneMode != PRUNE_MANUAL should be an assertion error, as it must be a bug in the code.
@@ -273,7 +280,8 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams); | |||
* | |||
* @param[out] setFilesToPrune The set of file indices that can be unlinked will be returned | |||
*/ | |||
void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight); | |||
void FindFilesToPruneAuto(std::set<int>& setFilesToPrune); | |||
void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight); |
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.
Aside: these functions are only used within main.cpp
, why are we exporting them?
(thinking about it, let's just keep it for now, it seems common in main.cpp
to export everything whether necessary or not, and changing will probably interfere with attempts of splitting up main such as #9183)
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.
There are many functions in main that are not exported (see the whole namespace block for the handling of NodeState, for example).
|
||
if (pruneMode != PRUNE_MANUAL) { | ||
throw JSONRPCError(RPC_METHOD_NOT_FOUND, "Cannot prune via RPC unless in manual prune mode."); | ||
return 0; |
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.
No need for a return if you have a throw
throw JSONRPCError(RPC_INVALID_PARAMETER, "Negative block height."); | ||
} else if ((unsigned int) chainActive.Height() < Params().PruneAfterHeight()) { | ||
throw JSONRPCError(RPC_INTERNAL_ERROR, "Blockchain is too short for pruning."); | ||
} |
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.
Do we need any foot-shooting checks: e.g. that the last 144 blocks are being retained to be robust against reorgs?
Edit: apparently the check for MIN_BLOCKS_TO_KEEP
happens deeper in the pruning logic, and it continues in this case by pruning the allowed blocks only. Ok, makes sense I think, although a warning in the log may make it more transparent what happens.
Feedback addressed. Re: the code in FindBlockPos (which reset fCheckForPruning only in auto-prune mode), yes that should only be auto-prune. I designed manual pruning to be a one-time act of pruning to the specified height. (In looking at this code, though, I noticed that nManualPruneHeight should probably be set=0 after we do the manual pruning, so I added that line of code, in a separate commit.) So, there's a commit responding to laanwj's feedback, and another with that one line of code. |
601aaa4
to
342262d
Compare
There was a merge conflict (the declaration of FlushStateToDisk near the top of main.cpp conflicted with my adding an optional parameter), so I rebased and squashed everything. This should work now. |
@@ -4140,7 +4175,7 @@ bool CVerifyDB::VerifyDB(const CChainParams& chainparams, CCoinsView *coinsview, | |||
uiInterface.ShowProgress(_("Verifying blocks..."), percentageDone); | |||
if (pindex->nHeight < chainActive.Height()-nCheckDepth) | |||
break; | |||
if (fPruneMode && !(pindex->nStatus & BLOCK_HAVE_DATA)) { | |||
if (pruneMode != PruneMode::NONE && !(pindex->nStatus & BLOCK_HAVE_DATA)) { |
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.
FindFilesToPruneAuto
is only called when pruneMode == PruneMode::AUTO
, so why do we need this test? Maybe turn it into an assert at the beginning of the function.
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.
Also, if (pruneMode)
is equivalent to if (pruneMode != PruneMode::NONE)
, since PruneMode::NONE is explicitly defined as 0.
(applies to many changed lines in this PR)
@@ -273,7 +280,8 @@ CAmount GetBlockSubsidy(int nHeight, const Consensus::Params& consensusParams); | |||
* | |||
* @param[out] setFilesToPrune The set of file indices that can be unlinked will be returned | |||
*/ | |||
void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfterHeight); | |||
void FindFilesToPruneAuto(std::set<int>& setFilesToPrune); | |||
void FindFilesToPruneManual(std::set<int>& setFilesToPrune, int nManualPruneHeight); |
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.
There are many functions in main that are not exported (see the whole namespace block for the handling of NodeState, for example).
@@ -191,8 +191,10 @@ static const uint64_t nMinDiskSpace = 52428800; | |||
/** Pruning-related variables and constants */ | |||
/** True if any block files have ever been pruned. */ | |||
extern bool fHavePruned; | |||
/** True if we're running in -prune mode. */ | |||
extern bool fPruneMode; | |||
enum struct PruneMode {NONE=0, AUTO, MANUAL}; |
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.
Meta question: why do we need a tristate here? I think we could allow manual pruning even when in 'auto` mode. In that case, manual-only pruning could be requested by setting the limit very high.
utACK. Great feature! |
One question: I view this feature as the perfect compliment to importmulti, that lets you be sure you've imported all your keys before you prune. But importmulti takes its scanning argument as a timestamp, while this takes it's argument as a height. Should we also support a timestamp option here that uses the same criteria as import multi? Edit: I think I will fix this by adding a height based option to importmulti. |
Extend pruneblockchain RPC to accept block timestamps as well as block indices.
168651a
to
afffeea
Compare
utACK afffeea |
else if (height > chainHeight) | ||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Blockchain is shorter than the attempted prune height."); | ||
else if (height > chainHeight - MIN_BLOCKS_TO_KEEP) | ||
LogPrint("rpc", "Attempt to prune blocks close to the tip. Retaining the minimum number of blocks."); |
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.
one post merge nit:
We should probably report the pruned height in case the user given value was overrode.
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.
Done in #9518
Change suggested by Jonas Schnelli <dev@jonasschnelli.ch> in bitcoin#7871 (comment)
Change suggested by Jonas Schnelli <dev@jonasschnelli.ch> in bitcoin#7871 (comment)
…dablock committed on Jan 20, 2018 Use version 2 blocks for miner_tests … @codablock codablock committed on Jan 20, 2018 Merge bitcoin#7871: Manual block file pruning. … @laanwj @codablock laanwj authored and codablock committed on Jan 11, 2017 Merge bitcoin#9507: Fix use-after-free in CTxMemPool::removeConflicts() … @sipa @codablock sipa authored and codablock committed on Jan 11, 2017 Merge bitcoin#9297: Various RPC help outputs updated … @MarcoFalke @codablock MarcoFalke authored and codablock committed on Jan 12, 2017 Merge bitcoin#9416: travis: make distdir before make … @MarcoFalke @codablock MarcoFalke authored and codablock committed on Jan 12, 2017 Merge bitcoin#9520: Deprecate non-txindex getrawtransaction and bette… … @MarcoFalke @codablock MarcoFalke authored and codablock committed on Jan 12, 2017 Merge bitcoin#9518: Return height of last block pruned by pruneblockc… … @MarcoFalke @codablock MarcoFalke authored and codablock committed on Jan 12, 2017 Merge bitcoin#9472: Disentangle progress estimation from checkpoints … … @laanwj @codablock laanwj authored and codablock committed on Jan 12, 2017 Merge bitcoin#8883: Add all standard TXO types to bitcoin-tx … @laanwj @codablock laanwj authored and codablock committed on Jan 12, 2017 Merge bitcoin#9261: Add unstored orphans with rejected parents to rec… … @laanwj @codablock laanwj authored and codablock committed on Jan 12, 2017 Merge bitcoin#9468: [Depends] Dependency updates for 0.14.0 … @laanwj @codablock laanwj authored and codablock committed on Jan 12, 2017 Merge bitcoin#9222: Add 'subtractFeeFromAmount' option to 'fundrawtra… … @laanwj @codablock laanwj authored and codablock committed on Jan 12, 2017 Merge bitcoin#9490: Replace FindLatestBefore used by importmuti with … … @sipa @codablock sipa authored and codablock committed on Jan 13, 2017 Merge bitcoin#9469: [depends] Qt 5.7.1 … @laanwj @codablock laanwj authored and codablock committed on Jan 15, 2017 Merge bitcoin#9380: Separate different uses of minimum fees … @laanwj @codablock laanwj authored and codablock committed on Jan 16, 2017 Remove SegWit related code in dash-tx @codablock codablock committed on Sep 21, 2017 Merge bitcoin#9561: Wake message handling thread when we receive a ne… … @sipa @codablock sipa authored and codablock committed on Jan 17, 2017 Merge bitcoin#9508: Remove unused Python imports … @MarcoFalke @codablock MarcoFalke authored and codablock committed on Jan 18, 2017 Merge bitcoin#9512: Fix various things -fsanitize complains about
This reverts commit 56dc1ce.
Implements #7365.
Now there is auto-prune and manual-prune. The user enables manual pruning on the command line with prune=1 and then uses an RPC command to prune: "pruneblockchain X" to prune up to height X.
Updated the python test (pruning.py).