CLI command to output the total number of (un)opened account versions (#2323)
* CLI command to output the number of (un)opened account versions * Fix build on older gcc versions * Fix failing upgrade tests * Better CLI description
This commit is contained in:
parent
335f783cda
commit
d5fd87c1f7
6 changed files with 134 additions and 9 deletions
|
|
@ -134,6 +134,7 @@ int main (int argc, char * const * argv)
|
||||||
("debug_peers", "Display peer IPv6:port connections")
|
("debug_peers", "Display peer IPv6:port connections")
|
||||||
("debug_cemented_block_count", "Displays the number of cemented (confirmed) blocks")
|
("debug_cemented_block_count", "Displays the number of cemented (confirmed) blocks")
|
||||||
("debug_stacktrace", "Display an example stacktrace")
|
("debug_stacktrace", "Display an example stacktrace")
|
||||||
|
("debug_account_versions", "Display the total counts of each version for all accounts (including unpocketed)")
|
||||||
("platform", boost::program_options::value<std::string> (), "Defines the <platform> for OpenCL commands")
|
("platform", boost::program_options::value<std::string> (), "Defines the <platform> for OpenCL commands")
|
||||||
("device", boost::program_options::value<std::string> (), "Defines <device> for OpenCL command")
|
("device", boost::program_options::value<std::string> (), "Defines <device> for OpenCL command")
|
||||||
("threads", boost::program_options::value<std::string> (), "Defines <threads> count for OpenCL command")
|
("threads", boost::program_options::value<std::string> (), "Defines <threads> count for OpenCL command")
|
||||||
|
|
@ -1096,6 +1097,82 @@ int main (int argc, char * const * argv)
|
||||||
nano::inactive_node node (data_path);
|
nano::inactive_node node (data_path);
|
||||||
node.node->logger.always_log (nano::severity_level::error, "Testing system logger");
|
node.node->logger.always_log (nano::severity_level::error, "Testing system logger");
|
||||||
}
|
}
|
||||||
|
else if (vm.count ("debug_account_versions"))
|
||||||
|
{
|
||||||
|
nano::inactive_node node (data_path);
|
||||||
|
|
||||||
|
auto transaction (node.node->store.tx_begin_read ());
|
||||||
|
std::vector<std::unordered_set<nano::account>> opened_account_versions (nano::normalized_epoch (nano::epoch::max));
|
||||||
|
|
||||||
|
// Cache the accounts in a collection to make searching quicker against unchecked keys. Group by epoch
|
||||||
|
for (auto i (node.node->store.latest_begin (transaction)), n (node.node->store.latest_end ()); i != n; ++i)
|
||||||
|
{
|
||||||
|
auto const & account (i->first);
|
||||||
|
auto const & account_info (i->second);
|
||||||
|
|
||||||
|
// Epoch 0 will be index 0 for instance
|
||||||
|
auto epoch_idx = nano::normalized_epoch (account_info.epoch ());
|
||||||
|
opened_account_versions[epoch_idx].emplace (account);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Iterate all pending blocks and collect the highest version for each unopened account
|
||||||
|
std::unordered_map<nano::account, std::underlying_type_t<nano::epoch>> unopened_highest_pending;
|
||||||
|
for (auto i (node.node->store.pending_begin (transaction)), n (node.node->store.pending_end ()); i != n; ++i)
|
||||||
|
{
|
||||||
|
nano::pending_key const & key (i->first);
|
||||||
|
nano::pending_info const & info (i->second);
|
||||||
|
// clang-format off
|
||||||
|
auto & account = key.account;
|
||||||
|
auto exists = std::any_of (opened_account_versions.begin (), opened_account_versions.end (), [&account](auto const & account_version) {
|
||||||
|
return account_version.find (account) != account_version.end ();
|
||||||
|
});
|
||||||
|
// clang-format on
|
||||||
|
if (!exists)
|
||||||
|
{
|
||||||
|
// This is an unopened account, store the highest pending version
|
||||||
|
auto it = unopened_highest_pending.find (key.account);
|
||||||
|
auto epoch = nano::normalized_epoch (info.epoch);
|
||||||
|
if (it != unopened_highest_pending.cend ())
|
||||||
|
{
|
||||||
|
// Found it, compare against existing value
|
||||||
|
if (epoch > it->second)
|
||||||
|
{
|
||||||
|
it->second = epoch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// New unopened account
|
||||||
|
unopened_highest_pending.emplace (key.account, epoch);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto output_account_version_number = [](auto version, auto num_accounts) {
|
||||||
|
std::cout << "Account version " << version << " num accounts: " << num_accounts << "\n";
|
||||||
|
};
|
||||||
|
|
||||||
|
// Output total version counts for the opened accounts
|
||||||
|
std::cout << "Opened accounts:\n";
|
||||||
|
for (auto i = 0u; i < opened_account_versions.size (); ++i)
|
||||||
|
{
|
||||||
|
output_account_version_number (i, opened_account_versions[i].size ());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accumulate the version numbers for the highest pending epoch for each unopened account.
|
||||||
|
std::vector<size_t> unopened_account_version_totals (nano::normalized_epoch (nano::epoch::max));
|
||||||
|
for (auto & pair : unopened_highest_pending)
|
||||||
|
{
|
||||||
|
++unopened_account_version_totals[pair.second];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Output total version counts for the unopened accounts
|
||||||
|
std::cout << "\nUnopened accounts:\n";
|
||||||
|
for (auto i = 0u; i < unopened_account_version_totals.size (); ++i)
|
||||||
|
{
|
||||||
|
output_account_version_number (i, unopened_account_version_totals[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (vm.count ("version"))
|
else if (vm.count ("version"))
|
||||||
{
|
{
|
||||||
std::cout << "Version " << NANO_VERSION_STRING << "\n"
|
std::cout << "Version " << NANO_VERSION_STRING << "\n"
|
||||||
|
|
|
||||||
|
|
@ -503,22 +503,25 @@ void nano::mdb_store::upgrade_v14_to_v15 (nano::write_transaction const & transa
|
||||||
{
|
{
|
||||||
// Move confirmation height from account_info database to its own table
|
// Move confirmation height from account_info database to its own table
|
||||||
std::vector<std::pair<nano::account, nano::account_info>> account_infos;
|
std::vector<std::pair<nano::account, nano::account_info>> account_infos;
|
||||||
account_infos.reserve (count (transaction_a, accounts_v0) + count (transaction_a, accounts_v1));
|
upgrade_counters account_counters (count (transaction_a, accounts_v0), count (transaction_a, accounts_v1));
|
||||||
|
account_infos.reserve (account_counters.before_v0 + account_counters.before_v1);
|
||||||
|
|
||||||
nano::mdb_merge_iterator<nano::account, nano::account_info_v14> i (transaction_a, accounts_v0, accounts_v1);
|
nano::mdb_merge_iterator<nano::account, nano::account_info_v14> i_account (transaction_a, accounts_v0, accounts_v1);
|
||||||
nano::mdb_merge_iterator<nano::account, nano::account_info_v14> n{};
|
nano::mdb_merge_iterator<nano::account, nano::account_info_v14> n_account{};
|
||||||
for (; i != n; ++i)
|
for (; i_account != n_account; ++i_account)
|
||||||
{
|
{
|
||||||
nano::account account (i->first);
|
nano::account account (i_account->first);
|
||||||
nano::account_info_v14 account_info_v14 (i->second);
|
nano::account_info_v14 account_info_v14 (i_account->second);
|
||||||
|
|
||||||
// Upgrade rep block to representative account
|
// Upgrade rep block to representative account
|
||||||
auto rep_block = block_get_v14 (transaction_a, account_info_v14.rep_block);
|
auto rep_block = block_get_v14 (transaction_a, account_info_v14.rep_block);
|
||||||
release_assert (rep_block != nullptr);
|
release_assert (rep_block != nullptr);
|
||||||
account_infos.emplace_back (account, nano::account_info{ account_info_v14.head, rep_block->representative (), account_info_v14.open_block, account_info_v14.balance, account_info_v14.modified, account_info_v14.block_count, i.from_first_database ? nano::epoch::epoch_0 : nano::epoch::epoch_1 });
|
account_infos.emplace_back (account, nano::account_info{ account_info_v14.head, rep_block->representative (), account_info_v14.open_block, account_info_v14.balance, account_info_v14.modified, account_info_v14.block_count, i_account.from_first_database ? nano::epoch::epoch_0 : nano::epoch::epoch_1 });
|
||||||
confirmation_height_put (transaction_a, account, account_info_v14.confirmation_height);
|
confirmation_height_put (transaction_a, account, account_info_v14.confirmation_height);
|
||||||
|
i_account.from_first_database ? ++account_counters.after_v0 : ++account_counters.after_v1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert (account_counters.are_equal ());
|
||||||
// No longer need accounts_v1, keep v0 but clear it
|
// No longer need accounts_v1, keep v0 but clear it
|
||||||
mdb_drop (env.tx (transaction_a), accounts_v1, 1);
|
mdb_drop (env.tx (transaction_a), accounts_v1, 1);
|
||||||
mdb_drop (env.tx (transaction_a), accounts_v0, 0);
|
mdb_drop (env.tx (transaction_a), accounts_v0, 0);
|
||||||
|
|
@ -537,6 +540,8 @@ void nano::mdb_store::upgrade_v14_to_v15 (nano::write_transaction const & transa
|
||||||
MDB_dbi state_blocks_new;
|
MDB_dbi state_blocks_new;
|
||||||
mdb_dbi_open (env.tx (transaction_a), "state_blocks", MDB_CREATE, &state_blocks_new);
|
mdb_dbi_open (env.tx (transaction_a), "state_blocks", MDB_CREATE, &state_blocks_new);
|
||||||
|
|
||||||
|
upgrade_counters state_counters (count (transaction_a, state_blocks_v0), count (transaction_a, state_blocks_v1));
|
||||||
|
|
||||||
nano::mdb_merge_iterator<nano::block_hash, nano::state_block_w_sideband_v14> i_state (transaction_a, state_blocks_v0, state_blocks_v1);
|
nano::mdb_merge_iterator<nano::block_hash, nano::state_block_w_sideband_v14> i_state (transaction_a, state_blocks_v0, state_blocks_v1);
|
||||||
nano::mdb_merge_iterator<nano::block_hash, nano::state_block_w_sideband_v14> n_state{};
|
nano::mdb_merge_iterator<nano::block_hash, nano::state_block_w_sideband_v14> n_state{};
|
||||||
auto num = 0u;
|
auto num = 0u;
|
||||||
|
|
@ -566,8 +571,10 @@ void nano::mdb_store::upgrade_v14_to_v15 (nano::write_transaction const & transa
|
||||||
{
|
{
|
||||||
logger.always_log (boost::str (boost::format ("Database epoch merge upgrade %1% million state blocks upgraded") % (num / output_cutoff)));
|
logger.always_log (boost::str (boost::format ("Database epoch merge upgrade %1% million state blocks upgraded") % (num / output_cutoff)));
|
||||||
}
|
}
|
||||||
|
i_state.from_first_database ? ++state_counters.after_v0 : ++state_counters.after_v1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert (state_counters.are_equal ());
|
||||||
logger.always_log ("Epoch merge upgrade. Finished state blocks, now doing pending blocks");
|
logger.always_log ("Epoch merge upgrade. Finished state blocks, now doing pending blocks");
|
||||||
|
|
||||||
state_blocks = state_blocks_new;
|
state_blocks = state_blocks_new;
|
||||||
|
|
@ -578,8 +585,9 @@ void nano::mdb_store::upgrade_v14_to_v15 (nano::write_transaction const & transa
|
||||||
|
|
||||||
state_blocks_v0 = state_blocks;
|
state_blocks_v0 = state_blocks;
|
||||||
|
|
||||||
|
upgrade_counters pending_counters (count (transaction_a, pending_v0), count (transaction_a, pending_v1));
|
||||||
std::vector<std::pair<nano::pending_key, nano::pending_info>> pending_infos;
|
std::vector<std::pair<nano::pending_key, nano::pending_info>> pending_infos;
|
||||||
pending_infos.reserve (count (transaction_a, pending_v0) + count (transaction_a, pending_v1));
|
pending_infos.reserve (pending_counters.before_v0 + pending_counters.before_v1);
|
||||||
|
|
||||||
nano::mdb_merge_iterator<nano::pending_key, nano::pending_info_v14> i_pending (transaction_a, pending_v0, pending_v1);
|
nano::mdb_merge_iterator<nano::pending_key, nano::pending_info_v14> i_pending (transaction_a, pending_v0, pending_v1);
|
||||||
nano::mdb_merge_iterator<nano::pending_key, nano::pending_info_v14> n_pending{};
|
nano::mdb_merge_iterator<nano::pending_key, nano::pending_info_v14> n_pending{};
|
||||||
|
|
@ -587,8 +595,11 @@ void nano::mdb_store::upgrade_v14_to_v15 (nano::write_transaction const & transa
|
||||||
{
|
{
|
||||||
nano::pending_info_v14 info (i_pending->second);
|
nano::pending_info_v14 info (i_pending->second);
|
||||||
pending_infos.emplace_back (nano::pending_key (i_pending->first), nano::pending_info{ info.source, info.amount, i_pending.from_first_database ? nano::epoch::epoch_0 : nano::epoch::epoch_1 });
|
pending_infos.emplace_back (nano::pending_key (i_pending->first), nano::pending_info{ info.source, info.amount, i_pending.from_first_database ? nano::epoch::epoch_0 : nano::epoch::epoch_1 });
|
||||||
|
i_pending.from_first_database ? ++pending_counters.after_v0 : ++pending_counters.after_v1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert (pending_counters.are_equal ());
|
||||||
|
|
||||||
// No longer need the pending v1 table
|
// No longer need the pending v1 table
|
||||||
mdb_drop (env.tx (transaction_a), pending_v1, 1);
|
mdb_drop (env.tx (transaction_a), pending_v1, 1);
|
||||||
mdb_drop (env.tx (transaction_a), pending_v0, 0);
|
mdb_drop (env.tx (transaction_a), pending_v0, 0);
|
||||||
|
|
@ -1004,3 +1015,14 @@ std::shared_ptr<nano::block> nano::mdb_store::block_get_v14 (nano::transaction c
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nano::mdb_store::upgrade_counters::upgrade_counters (uint64_t count_before_v0, uint64_t count_before_v1) :
|
||||||
|
before_v0 (count_before_v0),
|
||||||
|
before_v1 (count_before_v1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool nano::mdb_store::upgrade_counters::are_equal () const
|
||||||
|
{
|
||||||
|
return (before_v0 == after_v0) && (before_v1 == after_v1);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -249,6 +249,18 @@ private:
|
||||||
bool txn_tracking_enabled;
|
bool txn_tracking_enabled;
|
||||||
|
|
||||||
size_t count (nano::transaction const & transaction_a, tables table_a) const override;
|
size_t count (nano::transaction const & transaction_a, tables table_a) const override;
|
||||||
|
|
||||||
|
class upgrade_counters
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
upgrade_counters (uint64_t count_before_v0, uint64_t count_before_v1);
|
||||||
|
bool are_equal () const;
|
||||||
|
|
||||||
|
uint64_t before_v0;
|
||||||
|
uint64_t before_v1;
|
||||||
|
uint64_t after_v0{ 0 };
|
||||||
|
uint64_t after_v1{ 0 };
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
|
|
|
||||||
|
|
@ -264,6 +264,7 @@ private:
|
||||||
else if (key_cmp > 0)
|
else if (key_cmp > 0)
|
||||||
{
|
{
|
||||||
result = impl2.get ();
|
result = impl2.get ();
|
||||||
|
from_first_database = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -34,3 +34,12 @@ bool nano::epochs::is_sequential (nano::epoch epoch_a, nano::epoch new_epoch_a)
|
||||||
bool is_valid_epoch (head_epoch >= std::underlying_type_t<nano::epoch> (nano::epoch::epoch_0));
|
bool is_valid_epoch (head_epoch >= std::underlying_type_t<nano::epoch> (nano::epoch::epoch_0));
|
||||||
return is_valid_epoch && (std::underlying_type_t<nano::epoch> (new_epoch_a) == (head_epoch + 1));
|
return is_valid_epoch && (std::underlying_type_t<nano::epoch> (new_epoch_a) == (head_epoch + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::underlying_type_t<nano::epoch> nano::normalized_epoch (nano::epoch epoch_a)
|
||||||
|
{
|
||||||
|
// Currently assumes that the epoch versions in the enum are sequential.
|
||||||
|
auto start = std::underlying_type_t<nano::epoch> (nano::epoch::epoch_0);
|
||||||
|
auto end = std::underlying_type_t<nano::epoch> (epoch_a);
|
||||||
|
assert (end >= start);
|
||||||
|
return end - start;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,12 @@ enum class epoch : uint8_t
|
||||||
epoch_begin = 2,
|
epoch_begin = 2,
|
||||||
epoch_0 = 2,
|
epoch_0 = 2,
|
||||||
epoch_1 = 3,
|
epoch_1 = 3,
|
||||||
epoch_2 = 4
|
epoch_2 = 4,
|
||||||
|
max = epoch_2
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* This turns epoch_0 into 0 for instance */
|
||||||
|
std::underlying_type_t<nano::epoch> normalized_epoch (nano::epoch epoch_a);
|
||||||
}
|
}
|
||||||
namespace std
|
namespace std
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue