Progress #17: Lazy load genesis state

This speeds up startup for witness_node when starting on a database
which is already initialized.
This commit is contained in:
Nathan Hourt 2015-07-08 17:39:28 -04:00
parent 9c4ac2e064
commit d64c9154a7
8 changed files with 86 additions and 75 deletions

View file

@ -183,12 +183,14 @@ namespace detail {
bool clean = !fc::exists(_data_dir / "blockchain/dblock"); bool clean = !fc::exists(_data_dir / "blockchain/dblock");
fc::create_directories(_data_dir / "blockchain/dblock"); fc::create_directories(_data_dir / "blockchain/dblock");
genesis_state_type initial_state; auto initial_state = [&] {
ilog("Initializing database...");
if( _options->count("genesis-json") ) if( _options->count("genesis-json") )
initial_state = fc::json::from_file(_options->at("genesis-json").as<boost::filesystem::path>()) return fc::json::from_file(_options->at("genesis-json").as<boost::filesystem::path>())
.as<genesis_state_type>(); .as<genesis_state_type>();
else else
initial_state = create_example_genesis(); return create_example_genesis();
};
if( _options->count("resync-blockchain") ) if( _options->count("resync-blockchain") )
_chain_db->wipe(_data_dir / "blockchain", true); _chain_db->wipe(_data_dir / "blockchain", true);
@ -196,12 +198,12 @@ namespace detail {
if( _options->count("replay-blockchain") ) if( _options->count("replay-blockchain") )
{ {
ilog("Replaying blockchain on user request."); ilog("Replaying blockchain on user request.");
_chain_db->reindex(_data_dir/"blockchain", initial_state); _chain_db->reindex(_data_dir/"blockchain", initial_state());
} else if( clean ) } else if( clean )
_chain_db->open(_data_dir / "blockchain", initial_state); _chain_db->open(_data_dir / "blockchain", initial_state);
else { else {
wlog("Detected unclean shutdown. Replaying blockchain..."); wlog("Detected unclean shutdown. Replaying blockchain...");
_chain_db->reindex(_data_dir / "blockchain", initial_state); _chain_db->reindex(_data_dir / "blockchain", initial_state());
} }
if( _options->count("apiaccess") ) if( _options->count("apiaccess") )

View file

@ -20,6 +20,8 @@
#include <graphene/chain/operation_history_object.hpp> #include <graphene/chain/operation_history_object.hpp>
#include <functional>
namespace graphene { namespace chain { namespace graphene { namespace chain {
database::database() database::database()
@ -33,31 +35,10 @@ database::~database(){
_pending_block_session->commit(); _pending_block_session->commit();
} }
void database::open( const fc::path& data_dir, const genesis_state_type& initial_allocation )
{ try {
object_database::open( data_dir );
_block_id_to_block.open(data_dir / "database" / "block_num_to_block");
if( !find(global_property_id_type()) )
{
// ilog( "Init Genesis State" );
init_genesis(initial_allocation);
}
_pending_block.previous = head_block_id();
_pending_block.timestamp = head_block_time();
auto last_block= _block_id_to_block.last();
if( last_block.valid() )
_fork_db.start_block( *last_block );
} FC_CAPTURE_AND_RETHROW( (data_dir) ) }
void database::reindex(fc::path data_dir, const genesis_state_type& initial_allocation) void database::reindex(fc::path data_dir, const genesis_state_type& initial_allocation)
{ try { { try {
wipe(data_dir, false); wipe(data_dir, false);
open(data_dir, initial_allocation); open(data_dir, [&initial_allocation]{return initial_allocation;});
auto start = fc::time_point::now(); auto start = fc::time_point::now();
auto last_block = _block_id_to_block.last(); auto last_block = _block_id_to_block.last();

View file

@ -93,7 +93,19 @@ namespace graphene { namespace chain {
skip_assert_evaluation = 0x400 ///< used while reindexing skip_assert_evaluation = 0x400 ///< used while reindexing
}; };
void open(const fc::path& data_dir, const genesis_state_type& initial_allocation = genesis_state_type()); /**
* @brief Open a database, creating a new one if necessary
*
* Opens a database in the specified directory. If no initialized database is found, genesis_loader is called
* and its return value is used as the genesis state when initializing the new database
*
* genesis_loader will not be called if an existing database is found.
*
* @param data_dir Path to open or create database in
* @param genesis_loader A callable object which returns the genesis state to initialize new databases on
*/
template<typename F>
void open(const fc::path& data_dir, F&& genesis_loader);
/** /**
* @brief Rebuild object graph from block history and open detabase * @brief Rebuild object graph from block history and open detabase
* *
@ -477,4 +489,24 @@ namespace graphene { namespace chain {
(void)l; (void)l;
} }
} }
template<typename F>
void database::open(const fc::path& data_dir, F&& genesis_loader)
{ try {
object_database::open(data_dir);
_block_id_to_block.open(data_dir / "database" / "block_num_to_block");
if( !find(global_property_id_type()) )
init_genesis(genesis_loader());
_pending_block.previous = head_block_id();
_pending_block.timestamp = head_block_time();
auto last_block= _block_id_to_block.last();
if( last_block.valid() )
_fork_db.start_block( *last_block );
} FC_CAPTURE_AND_RETHROW( (data_dir) ) }
} } } }

View file

@ -286,7 +286,7 @@ void database_fixture::open_database()
{ {
if( !data_dir ) { if( !data_dir ) {
data_dir = fc::temp_directory(); data_dir = fc::temp_directory();
db.open(data_dir->path(), genesis_state); db.open(data_dir->path(), [this]{return genesis_state;});
} }
} }

View file

@ -129,7 +129,7 @@ BOOST_AUTO_TEST_CASE( generate_empty_blocks )
auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) ); auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) );
{ {
database db; database db;
db.open(data_dir.path(), make_genesis() ); db.open(data_dir.path(), make_genesis );
b = db.generate_block(now, db.get_scheduled_witness(1).first, delegate_priv_key, database::skip_nothing); b = db.generate_block(now, db.get_scheduled_witness(1).first, delegate_priv_key, database::skip_nothing);
for( uint32_t i = 1; i < 200; ++i ) for( uint32_t i = 1; i < 200; ++i )
@ -146,7 +146,7 @@ BOOST_AUTO_TEST_CASE( generate_empty_blocks )
} }
{ {
database db; database db;
db.open(data_dir.path() ); db.open(data_dir.path(), []{return genesis_state_type();});
BOOST_CHECK_EQUAL( db.head_block_num(), 200 ); BOOST_CHECK_EQUAL( db.head_block_num(), 200 );
for( uint32_t i = 0; i < 200; ++i ) for( uint32_t i = 0; i < 200; ++i )
{ {
@ -171,7 +171,7 @@ BOOST_AUTO_TEST_CASE( undo_block )
fc::temp_directory data_dir; fc::temp_directory data_dir;
{ {
database db; database db;
db.open(data_dir.path(), make_genesis() ); db.open(data_dir.path(), make_genesis);
fc::time_point_sec now( GRAPHENE_TESTING_GENESIS_TIMESTAMP ); fc::time_point_sec now( GRAPHENE_TESTING_GENESIS_TIMESTAMP );
auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) ); auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) );
@ -211,9 +211,9 @@ BOOST_AUTO_TEST_CASE( fork_blocks )
fc::time_point_sec now( GRAPHENE_TESTING_GENESIS_TIMESTAMP ); fc::time_point_sec now( GRAPHENE_TESTING_GENESIS_TIMESTAMP );
database db1; database db1;
db1.open(data_dir1.path(), make_genesis()); db1.open(data_dir1.path(), make_genesis);
database db2; database db2;
db2.open(data_dir2.path(), make_genesis()); db2.open(data_dir2.path(), make_genesis);
auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) ); auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) );
for( uint32_t i = 0; i < 10; ++i ) for( uint32_t i = 0; i < 10; ++i )
@ -276,7 +276,7 @@ BOOST_AUTO_TEST_CASE( undo_pending )
fc::temp_directory data_dir; fc::temp_directory data_dir;
{ {
database db; database db;
db.open(data_dir.path(), make_genesis()); db.open(data_dir.path(), make_genesis);
auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) ); auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) );
public_key_type delegate_pub_key = delegate_priv_key.get_public_key(); public_key_type delegate_pub_key = delegate_priv_key.get_public_key();
@ -334,8 +334,8 @@ BOOST_AUTO_TEST_CASE( switch_forks_undo_create )
dir2; dir2;
database db1, database db1,
db2; db2;
db1.open(dir1.path(), make_genesis()); db1.open(dir1.path(), make_genesis);
db2.open(dir2.path(), make_genesis()); db2.open(dir2.path(), make_genesis);
fc::time_point_sec now( GRAPHENE_TESTING_GENESIS_TIMESTAMP ); fc::time_point_sec now( GRAPHENE_TESTING_GENESIS_TIMESTAMP );
auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) ); auto delegate_priv_key = fc::ecc::private_key::regenerate(fc::sha256::hash(string("null_key")) );
@ -392,8 +392,8 @@ BOOST_AUTO_TEST_CASE( duplicate_transactions )
dir2; dir2;
database db1, database db1,
db2; db2;
db1.open(dir1.path(), make_genesis()); db1.open(dir1.path(), make_genesis);
db2.open(dir2.path(), make_genesis()); db2.open(dir2.path(), make_genesis);
auto skip_sigs = database::skip_transaction_signatures | database::skip_authority_check; auto skip_sigs = database::skip_transaction_signatures | database::skip_authority_check;
@ -441,8 +441,8 @@ BOOST_AUTO_TEST_CASE( tapos )
dir2; dir2;
database db1, database db1,
db2; db2;
db1.open(dir1.path(), make_genesis()); db1.open(dir1.path(), make_genesis);
db2.open(dir2.path(), make_genesis()); db2.open(dir2.path(), make_genesis);
const account_object& init1 = *db1.get_index_type<account_index>().indices().get<by_name>().find("init1"); const account_object& init1 = *db1.get_index_type<account_index>().indices().get<by_name>().find("init1");
@ -467,10 +467,6 @@ BOOST_AUTO_TEST_CASE( tapos )
db1.push_transaction(trx); db1.push_transaction(trx);
now += db1.block_interval(); now += db1.block_interval();
b = db1.generate_block(now, db1.get_scheduled_witness(1).first, delegate_priv_key, database::skip_nothing); b = db1.generate_block(now, db1.get_scheduled_witness(1).first, delegate_priv_key, database::skip_nothing);
/*
now += db1.block_interval();
b = db1.generate_block(now, db1.get_scheduled_witness(1).first, delegate_priv_key, database::skip_nothing);
*/
trx.clear(); trx.clear();
trx.operations.push_back(transfer_operation({asset(), account_id_type(), nathan_id, asset(50)})); trx.operations.push_back(transfer_operation({asset(), account_id_type(), nathan_id, asset(50)}));

View file

@ -1001,7 +1001,7 @@ BOOST_AUTO_TEST_CASE( balance_object_test )
genesis_state.initial_accounts.emplace_back("n", n_key.get_public_key()); genesis_state.initial_accounts.emplace_back("n", n_key.get_public_key());
db.open(td.path(), genesis_state); db.open(td.path(), [this]{return genesis_state;});
const balance_object& balance = balance_id_type()(db); const balance_object& balance = balance_id_type()(db);
BOOST_CHECK_EQUAL(balance.balance.amount.value, 1); BOOST_CHECK_EQUAL(balance.balance.amount.value, 1);
BOOST_CHECK_EQUAL(balance_id_type(1)(db).balance.amount.value, 1); BOOST_CHECK_EQUAL(balance_id_type(1)(db).balance.amount.value, 1);