Skip to content

Commit 534b8fa

Browse files
committed
Merge #12653: Allow to optional specify the directory for the blocks storage
a192636 -blocksdir: keep blockindex leveldb database in datadir (Jonas Schnelli) f38e4fd QA: Add -blocksdir test (Jonas Schnelli) 386a6b6 Allow to optional specify the directory for the blocks storage (Jonas Schnelli) Pull request description: Since the actual block files taking up more and more space, it may be desirable to have them stored in a different location then the data directory (use case: SSD for chainstate, etc., HD for blocks). This PR adds a `-blocksdir` option that allows one to keep the blockfiles and the blockindex external from the data directory (instead of creating symlinks). I fist had an option to keep the blockindex within the datadir, but seems to make no sense since accessing the index will (always) lead to access (r/w) the block files. Tree-SHA512: f8b9e1a681679eac25076dc30e45e6e12d4b2d9ac4be907cbea928a75af081dbcb0f1dd3e97169ab975f73d0bd15824c00c2a34638f3b284b39017171fce2409
2 parents b648974 + a192636 commit 534b8fa

File tree

9 files changed

+85
-11
lines changed

9 files changed

+85
-11
lines changed

src/init.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,6 +333,7 @@ std::string HelpMessage(HelpMessageMode mode)
333333
strUsage += HelpMessageOpt("-version", _("Print version and exit"));
334334
strUsage += HelpMessageOpt("-alertnotify=<cmd>", _("Execute command when a relevant alert is received or we see a really long fork (%s in cmd is replaced by message)"));
335335
strUsage +=HelpMessageOpt("-assumevalid=<hex>", strprintf(_("If this block is in the chain assume that it and its ancestors are valid and potentially skip their script verification (0 to verify all, default: %s, testnet: %s)"), defaultChainParams->GetConsensus().defaultAssumeValid.GetHex(), testnetChainParams->GetConsensus().defaultAssumeValid.GetHex()));
336+
strUsage += HelpMessageOpt("-blocksdir=<dir>", _("Specify blocks directory (default: <datadir>/blocks)"));
336337
strUsage += HelpMessageOpt("-blocknotify=<cmd>", _("Execute command when the best block changes (%s in cmd is replaced by block hash)"));
337338
strUsage += HelpMessageOpt("-blockreconstructionextratxn=<n>", strprintf(_("Extra transactions to keep in memory for compact block reconstructions (default: %u)"), DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN));
338339
if (showDebug)
@@ -594,7 +595,7 @@ void CleanupBlockRevFiles()
594595
// Remove the rev files immediately and insert the blk file paths into an
595596
// ordered map keyed by block file index.
596597
LogPrintf("Removing unusable blk?????.dat and rev?????.dat files for -reindex with -prune\n");
597-
fs::path blocksdir = GetDataDir() / "blocks";
598+
fs::path blocksdir = GetBlocksDir();
598599
for (fs::directory_iterator it(blocksdir); it != fs::directory_iterator(); it++) {
599600
if (fs::is_regular_file(*it) &&
600601
it->path().filename().string().length() == 12 &&
@@ -897,6 +898,10 @@ bool AppInitParameterInteraction()
897898

898899
// also see: InitParameterInteraction()
899900

901+
if (!fs::is_directory(GetBlocksDir(false))) {
902+
return InitError(strprintf(_("Specified blocks directory \"%s\" does not exist.\n"), gArgs.GetArg("-blocksdir", "").c_str()));
903+
}
904+
900905
// if using block pruning, then disallow txindex
901906
if (gArgs.GetArg("-prune", 0)) {
902907
if (gArgs.GetBoolArg("-txindex", DEFAULT_TXINDEX))
@@ -1622,7 +1627,7 @@ bool AppInitMain()
16221627

16231628
// ********************************************************* Step 10: import blocks
16241629

1625-
if (!CheckDiskSpace())
1630+
if (!CheckDiskSpace() && !CheckDiskSpace(0, true))
16261631
return false;
16271632

16281633
// Either install a handler to notify us when genesis activates, or set fHaveGenesis directly.

src/qt/bitcoin.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -623,7 +623,7 @@ int main(int argc, char *argv[])
623623
if (!Intro::pickDataDirectory())
624624
return EXIT_SUCCESS;
625625

626-
/// 6. Determine availability of data directory and parse bitcoin.conf
626+
/// 6. Determine availability of data and blocks directory and parse bitcoin.conf
627627
/// - Do not call GetDataDir(true) before this step finishes
628628
if (!fs::is_directory(GetDataDir(false)))
629629
{

src/txdb.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@ size_t CCoinsViewDB::EstimateSize() const
147147
return db.EstimateSize(DB_COIN, (char)(DB_COIN+1));
148148
}
149149

150-
CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CDBWrapper(GetDataDir() / "blocks" / "index", nCacheSize, fMemory, fWipe) {
150+
CBlockTreeDB::CBlockTreeDB(size_t nCacheSize, bool fMemory, bool fWipe) : CDBWrapper(gArgs.IsArgSet("-blocksdir") ? GetDataDir() / "blocks" / "index" : GetBlocksDir() / "index", nCacheSize, fMemory, fWipe) {
151151
}
152152

153153
bool CBlockTreeDB::ReadBlockFileInfo(int nFile, CBlockFileInfo &info) {

src/util.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,10 +613,41 @@ fs::path GetDefaultDataDir()
613613
#endif
614614
}
615615

616+
static fs::path g_blocks_path_cached;
617+
static fs::path g_blocks_path_cache_net_specific;
616618
static fs::path pathCached;
617619
static fs::path pathCachedNetSpecific;
618620
static CCriticalSection csPathCached;
619621

622+
const fs::path &GetBlocksDir(bool fNetSpecific)
623+
{
624+
625+
LOCK(csPathCached);
626+
627+
fs::path &path = fNetSpecific ? g_blocks_path_cache_net_specific : g_blocks_path_cached;
628+
629+
// This can be called during exceptions by LogPrintf(), so we cache the
630+
// value so we don't have to do memory allocations after that.
631+
if (!path.empty())
632+
return path;
633+
634+
if (gArgs.IsArgSet("-blocksdir")) {
635+
path = fs::system_complete(gArgs.GetArg("-blocksdir", ""));
636+
if (!fs::is_directory(path)) {
637+
path = "";
638+
return path;
639+
}
640+
} else {
641+
path = GetDataDir(false);
642+
}
643+
if (fNetSpecific)
644+
path /= BaseParams().DataDir();
645+
646+
path /= "blocks";
647+
fs::create_directories(path);
648+
return path;
649+
}
650+
620651
const fs::path &GetDataDir(bool fNetSpecific)
621652
{
622653

@@ -655,6 +686,8 @@ void ClearDatadirCache()
655686

656687
pathCached = fs::path();
657688
pathCachedNetSpecific = fs::path();
689+
g_blocks_path_cached = fs::path();
690+
g_blocks_path_cache_net_specific = fs::path();
658691
}
659692

660693
fs::path GetConfigFile(const std::string& confPath)

src/util.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ void ReleaseDirectoryLocks();
183183

184184
bool TryCreateDirectories(const fs::path& p);
185185
fs::path GetDefaultDataDir();
186+
const fs::path &GetBlocksDir(bool fNetSpecific = true);
186187
const fs::path &GetDataDir(bool fNetSpecific = true);
187188
void ClearDatadirCache();
188189
fs::path GetConfigFile(const std::string& confPath);

src/validation.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2108,7 +2108,7 @@ bool static FlushStateToDisk(const CChainParams& chainparams, CValidationState &
21082108
// Write blocks and block index to disk.
21092109
if (fDoFullFlush || fPeriodicWrite) {
21102110
// Depend on nMinDiskSpace to ensure we can write block index
2111-
if (!CheckDiskSpace(0))
2111+
if (!CheckDiskSpace(0, true))
21122112
return state.Error("out of disk space");
21132113
// First make sure all block and undo data is flushed to disk.
21142114
FlushBlockFile();
@@ -2953,7 +2953,7 @@ static bool FindBlockPos(CDiskBlockPos &pos, unsigned int nAddSize, unsigned int
29532953
if (nNewChunks > nOldChunks) {
29542954
if (fPruneMode)
29552955
fCheckForPruning = true;
2956-
if (CheckDiskSpace(nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos)) {
2956+
if (CheckDiskSpace(nNewChunks * BLOCKFILE_CHUNK_SIZE - pos.nPos, true)) {
29572957
FILE *file = OpenBlockFile(pos);
29582958
if (file) {
29592959
LogPrintf("Pre-allocating up to position 0x%x in blk%05u.dat\n", nNewChunks * BLOCKFILE_CHUNK_SIZE, pos.nFile);
@@ -2986,7 +2986,7 @@ static bool FindUndoPos(CValidationState &state, int nFile, CDiskBlockPos &pos,
29862986
if (nNewChunks > nOldChunks) {
29872987
if (fPruneMode)
29882988
fCheckForPruning = true;
2989-
if (CheckDiskSpace(nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos)) {
2989+
if (CheckDiskSpace(nNewChunks * UNDOFILE_CHUNK_SIZE - pos.nPos, true)) {
29902990
FILE *file = OpenUndoFile(pos);
29912991
if (file) {
29922992
LogPrintf("Pre-allocating up to position 0x%x in rev%05u.dat\n", nNewChunks * UNDOFILE_CHUNK_SIZE, pos.nFile);
@@ -3661,9 +3661,9 @@ static void FindFilesToPrune(std::set<int>& setFilesToPrune, uint64_t nPruneAfte
36613661
nLastBlockWeCanPrune, count);
36623662
}
36633663

3664-
bool CheckDiskSpace(uint64_t nAdditionalBytes)
3664+
bool CheckDiskSpace(uint64_t nAdditionalBytes, bool blocks_dir)
36653665
{
3666-
uint64_t nFreeBytesAvailable = fs::space(GetDataDir()).available;
3666+
uint64_t nFreeBytesAvailable = fs::space(blocks_dir ? GetBlocksDir() : GetDataDir()).available;
36673667

36683668
// Check for nMinDiskSpace bytes (currently 50MB)
36693669
if (nFreeBytesAvailable < nMinDiskSpace + nAdditionalBytes)
@@ -3706,7 +3706,7 @@ static FILE* OpenUndoFile(const CDiskBlockPos &pos, bool fReadOnly) {
37063706

37073707
fs::path GetBlockPosFilename(const CDiskBlockPos &pos, const char *prefix)
37083708
{
3709-
return GetDataDir() / "blocks" / strprintf("%s%05u.dat", prefix, pos.nFile);
3709+
return GetBlocksDir() / strprintf("%s%05u.dat", prefix, pos.nFile);
37103710
}
37113711

37123712
CBlockIndex * CChainState::InsertBlockIndex(const uint256& hash)

src/validation.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,7 @@ bool ProcessNewBlock(const CChainParams& chainparams, const std::shared_ptr<cons
254254
bool ProcessNewBlockHeaders(const std::vector<CBlockHeader>& block, CValidationState& state, const CChainParams& chainparams, const CBlockIndex** ppindex=nullptr, CBlockHeader *first_invalid=nullptr);
255255

256256
/** Check whether enough disk space is available for an incoming block */
257-
bool CheckDiskSpace(uint64_t nAdditionalBytes = 0);
257+
bool CheckDiskSpace(uint64_t nAdditionalBytes = 0, bool blocks_dir = false);
258258
/** Open a block file (blk?????.dat) */
259259
FILE* OpenBlockFile(const CDiskBlockPos &pos, bool fReadOnly = false);
260260
/** Translation to a filesystem path */

test/functional/feature_blocksdir.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#!/usr/bin/env python3
2+
# Copyright (c) 2018 The Bitcoin Core developers
3+
# Distributed under the MIT software license, see the accompanying
4+
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
"""Test the blocksdir option.
6+
"""
7+
8+
from test_framework.test_framework import BitcoinTestFramework, initialize_datadir
9+
10+
import shutil
11+
import os
12+
13+
class BlocksdirTest(BitcoinTestFramework):
14+
def set_test_params(self):
15+
self.setup_clean_chain = True
16+
self.num_nodes = 1
17+
18+
def run_test(self):
19+
self.stop_node(0)
20+
node0path = os.path.join(self.options.tmpdir, "node0")
21+
shutil.rmtree(node0path)
22+
initialize_datadir(self.options.tmpdir, 0)
23+
self.log.info("Starting with non exiting blocksdir ...")
24+
self.assert_start_raises_init_error(0, ["-blocksdir="+self.options.tmpdir+ "/blocksdir"], "Specified blocks director")
25+
os.mkdir(self.options.tmpdir+ "/blocksdir")
26+
self.log.info("Starting with exiting blocksdir ...")
27+
self.start_node(0, ["-blocksdir="+self.options.tmpdir+ "/blocksdir"])
28+
self.log.info("mining blocks..")
29+
self.nodes[0].generate(10)
30+
assert(os.path.isfile(os.path.join(self.options.tmpdir, "blocksdir", "regtest", "blocks", "blk00000.dat")))
31+
assert(os.path.isdir(os.path.join(self.options.tmpdir, "node0", "regtest", "blocks", "index")))
32+
33+
if __name__ == '__main__':
34+
BlocksdirTest().main()

test/functional/test_runner.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@
136136
'p2p_unrequested_blocks.py',
137137
'feature_logging.py',
138138
'p2p_node_network_limited.py',
139+
'feature_blocksdir.py',
139140
'feature_config_args.py',
140141
# Don't append tests at the end to avoid merge conflicts
141142
# Put them in a random line within the section that fits their approximate run-time

0 commit comments

Comments
 (0)