@@ -394,10 +394,10 @@ void rai::frontier_req_client::next (rai::transaction const & transaction_a)
394
394
395
395
rai::bulk_pull_client::bulk_pull_client (std::shared_ptr<rai::bootstrap_client> connection_a, rai::pull_info const & pull_a) :
396
396
connection (connection_a),
397
+ total_blocks (0 ),
397
398
pull (pull_a)
398
399
{
399
400
std::lock_guard<std::mutex> mutex (connection->attempt ->mutex );
400
- ++connection->attempt ->pulling ;
401
401
connection->attempt ->condition .notify_all ();
402
402
}
403
403
@@ -407,6 +407,10 @@ rai::bulk_pull_client::~bulk_pull_client ()
407
407
if (expected != pull.end )
408
408
{
409
409
pull.head = expected;
410
+ if (connection->attempt ->lazy_mode )
411
+ {
412
+ pull.account = expected;
413
+ }
410
414
connection->attempt ->requeue_pull (pull);
411
415
if (connection->node ->config .logging .bulk_pull_logging ())
412
416
{
@@ -552,25 +556,32 @@ void rai::bulk_pull_client::received_block (boost::system::error_code const & ec
552
556
block->serialize_json (block_l);
553
557
BOOST_LOG (connection->node ->log ) << boost::str (boost::format (" Pulled block %1% %2%" ) % hash.to_string () % block_l);
554
558
}
559
+ bool block_expected (false );
555
560
if (hash == expected)
556
561
{
557
562
expected = block->previous ();
563
+ block_expected = true ;
558
564
}
559
565
if (connection->block_count ++ == 0 )
560
566
{
561
567
connection->start_time = std::chrono::steady_clock::now ();
562
568
}
563
569
connection->attempt ->total_blocks ++;
564
- bool stop_pull (connection->attempt ->process_block (block));
570
+ total_blocks++;
571
+ bool stop_pull (connection->attempt ->process_block (block, total_blocks, block_expected));
565
572
if (!stop_pull && !connection->hard_stop .load ())
566
573
{
567
574
receive_block ();
568
575
}
569
- else if (stop_pull && expected == block-> previous () )
576
+ else if (stop_pull && block_expected )
570
577
{
571
578
expected = pull.end ;
572
579
connection->attempt ->pool_connection (connection);
573
580
}
581
+ if (stop_pull)
582
+ {
583
+ connection->attempt ->lazy_stopped ++;
584
+ }
574
585
}
575
586
else
576
587
{
@@ -738,17 +749,11 @@ pulling (0),
738
749
node (node_a),
739
750
account_count (0 ),
740
751
total_blocks (0 ),
752
+ lazy_stopped (0 ),
741
753
stopped (false ),
742
754
lazy_mode (false )
743
755
{
744
- if (lazy_mode)
745
- {
746
- BOOST_LOG (node->log ) << " Starting lazy-bootstrap attempt" ;
747
- }
748
- else
749
- {
750
- BOOST_LOG (node->log ) << " Starting bootstrap attempt" ;
751
- }
756
+ BOOST_LOG (node->log ) << " Starting bootstrap attempt" ;
752
757
node->bootstrap_initiator .notify_listeners (true );
753
758
}
754
759
@@ -814,6 +819,17 @@ void rai::bootstrap_attempt::request_pull (std::unique_lock<std::mutex> & lock_a
814
819
{
815
820
auto pull (pulls.front ());
816
821
pulls.pop_front ();
822
+ if (lazy_mode)
823
+ {
824
+ // Check if pull is obsolete (head was processed)
825
+ std::unique_lock<std::mutex> lock (lazy_mutex);
826
+ while (!pulls.empty () && !pull.head .is_zero () && lazy_blocks.find (pull.head ) != lazy_blocks.end ())
827
+ {
828
+ pull = pulls.front ();
829
+ pulls.pop_front ();
830
+ }
831
+ }
832
+ ++pulling;
817
833
// The bulk_pull_client destructor attempt to requeue_pull which can cause a deadlock if this is the last reference
818
834
// Dispatch request in an external thread in case it needs to be destroyed
819
835
node->background ([connection_l, pull]() {
@@ -900,15 +916,16 @@ void rai::bootstrap_attempt::run ()
900
916
if (!stopped)
901
917
{
902
918
BOOST_LOG (node->log ) << " Completed pulls" ;
919
+ request_push (lock);
903
920
// Start lazy bootstrap if some lazy keys were inserted
904
921
if (!lazy_keys.empty ())
905
922
{
906
923
lock.unlock ();
924
+ lazy_mode = true ;
907
925
lazy_run ();
908
926
lock.lock ();
909
927
}
910
928
}
911
- request_push (lock);
912
929
stopped = true ;
913
930
condition.notify_all ();
914
931
idle.clear ();
@@ -1156,19 +1173,19 @@ void rai::bootstrap_attempt::add_bulk_push_target (rai::block_hash const & head,
1156
1173
void rai::bootstrap_attempt::lazy_start (rai::block_hash const & hash_a)
1157
1174
{
1158
1175
std::unique_lock<std::mutex> lock (lazy_mutex);
1159
- // Add start blocks
1160
- if (lazy_keys.find (hash_a) == lazy_keys.end ())
1176
+ // Add start blocks, limit 1024 (32k with disabled legacy bootstrap)
1177
+ size_t max_keys (node->flags .disable_legacy_bootstrap ? 32 * 1024 : 1024 );
1178
+ if (lazy_keys.size () < max_keys && lazy_keys.find (hash_a) == lazy_keys.end () && lazy_blocks.find (hash_a) == lazy_blocks.end ())
1161
1179
{
1162
1180
lazy_keys.insert (hash_a);
1181
+ lazy_pulls.push_back (hash_a);
1163
1182
}
1164
- lazy_add (hash_a);
1165
1183
}
1166
1184
1167
1185
void rai::bootstrap_attempt::lazy_add (rai::block_hash const & hash_a)
1168
1186
{
1169
1187
// Add only unknown blocks
1170
1188
assert (!lazy_mutex.try_lock ());
1171
-
1172
1189
if (lazy_blocks.find (hash_a) == lazy_blocks.end ())
1173
1190
{
1174
1191
lazy_pulls.push_back (hash_a);
@@ -1183,7 +1200,7 @@ void rai::bootstrap_attempt::lazy_pull_flush ()
1183
1200
// Recheck if block was already processed
1184
1201
if (lazy_blocks.find (pull_start) == lazy_blocks.end ())
1185
1202
{
1186
- add_pull (rai::pull_info (pull_start, pull_start, rai::block_hash (0 )));
1203
+ add_pull (rai::pull_info (pull_start, pull_start, rai::block_hash (0 ), lazy_max_pull_blocks ));
1187
1204
}
1188
1205
}
1189
1206
lazy_pulls.clear ();
@@ -1194,7 +1211,7 @@ bool rai::bootstrap_attempt::lazy_finished ()
1194
1211
bool result (true );
1195
1212
auto transaction (node->store .tx_begin_read ());
1196
1213
std::unique_lock<std::mutex> lock (lazy_mutex);
1197
- for (auto it (lazy_keys.begin ()), end (lazy_keys.end ()); it != end;)
1214
+ for (auto it (lazy_keys.begin ()), end (lazy_keys.end ()); it != end && !stopped ;)
1198
1215
{
1199
1216
if (node->store .block_exists (transaction, *it))
1200
1217
{
@@ -1208,18 +1225,24 @@ bool rai::bootstrap_attempt::lazy_finished ()
1208
1225
// No need to increment `it` as we break above.
1209
1226
}
1210
1227
}
1228
+ // Finish lazy bootstrap without lazy pulls (in combination with still_pulling ())
1229
+ if (!result && lazy_pulls.empty ())
1230
+ {
1231
+ result = true ;
1232
+ }
1211
1233
return result;
1212
1234
}
1213
1235
1214
1236
void rai::bootstrap_attempt::lazy_run ()
1215
1237
{
1216
1238
populate_connections ();
1217
1239
auto start_time (std::chrono::steady_clock::now ());
1218
- auto max_time (std::chrono::milliseconds ( 30 * 60 * 1000 ));
1240
+ auto max_time (std::chrono::minutes (node-> flags . disable_legacy_bootstrap ? 48 * 60 : 30 ));
1219
1241
std::unique_lock<std::mutex> lock (mutex);
1220
1242
while ((still_pulling () || !lazy_finished ()) && std::chrono::steady_clock::now () - start_time < max_time)
1221
1243
{
1222
- while (still_pulling ())
1244
+ unsigned iterations (0 );
1245
+ while (still_pulling () && std::chrono::steady_clock::now () - start_time < max_time)
1223
1246
{
1224
1247
if (!pulls.empty ())
1225
1248
{
@@ -1236,6 +1259,14 @@ void rai::bootstrap_attempt::lazy_run ()
1236
1259
{
1237
1260
condition.wait (lock);
1238
1261
}
1262
+ ++iterations;
1263
+ // Flushing lazy pulls
1264
+ if (iterations % 100 == 0 )
1265
+ {
1266
+ lock.unlock ();
1267
+ lazy_pull_flush ();
1268
+ lock.lock ();
1269
+ }
1239
1270
}
1240
1271
// Flushing may resolve forks which can add more pulls
1241
1272
// Flushing lazy pulls
@@ -1253,10 +1284,10 @@ void rai::bootstrap_attempt::lazy_run ()
1253
1284
idle.clear ();
1254
1285
}
1255
1286
1256
- bool rai::bootstrap_attempt::process_block (std::shared_ptr<rai::block> block_a)
1287
+ bool rai::bootstrap_attempt::process_block (std::shared_ptr<rai::block> block_a, uint64_t total_blocks, bool block_expected )
1257
1288
{
1258
1289
bool stop_pull (false );
1259
- if (lazy_mode)
1290
+ if (lazy_mode && block_expected )
1260
1291
{
1261
1292
auto hash (block_a->hash ());
1262
1293
std::unique_lock<std::mutex> lock (lazy_mutex);
@@ -1267,49 +1298,90 @@ bool rai::bootstrap_attempt::process_block (std::shared_ptr<rai::block> block_a)
1267
1298
auto transaction (node->store .tx_begin_read ());
1268
1299
if (!node->store .block_exists (transaction, hash))
1269
1300
{
1270
- lazy_blocks. insert (hash );
1301
+ rai:: uint128_t balance (std::numeric_limits<rai:: uint128_t >:: max () );
1271
1302
node->block_processor .add (block_a, std::chrono::steady_clock::time_point ());
1272
1303
// Search for new dependencies
1273
1304
if (!block_a->source ().is_zero () && !node->store .block_exists (transaction, block_a->source ()))
1274
1305
{
1275
1306
lazy_add (block_a->source ());
1276
1307
}
1308
+ else if (block_a->type () == rai::block_type::send)
1309
+ {
1310
+ // Calculate balance for legacy send blocks
1311
+ std::shared_ptr<rai::send_block> block_l (std::static_pointer_cast<rai::send_block> (block_a));
1312
+ if (block_l != nullptr )
1313
+ {
1314
+ balance = block_l->hashables .balance .number ();
1315
+ }
1316
+ }
1277
1317
else if (block_a->type () == rai::block_type::state)
1278
1318
{
1279
1319
std::shared_ptr<rai::state_block> block_l (std::static_pointer_cast<rai::state_block> (block_a));
1280
1320
if (block_l != nullptr )
1281
1321
{
1322
+ balance = block_l->hashables .balance .number ();
1282
1323
rai::block_hash link (block_l->hashables .link );
1283
1324
// If link is not epoch link or 0. And if block from link unknown
1284
1325
if (!link.is_zero () && link != node->ledger .epoch_link && lazy_blocks.find (link) == lazy_blocks.end () && !node->store .block_exists (transaction, link))
1285
1326
{
1327
+ rai::block_hash previous (block_l->hashables .previous );
1286
1328
// If state block previous is 0 then source block required
1287
- if (block_l-> hashables . previous .is_zero ())
1329
+ if (previous.is_zero ())
1288
1330
{
1289
1331
lazy_add (link);
1290
1332
}
1291
1333
// In other cases previous block balance required to find out subtype of state block
1292
- else if (node->store .block_exists (transaction, block_l-> hashables . previous ))
1334
+ else if (node->store .block_exists (transaction, previous))
1293
1335
{
1294
- rai::amount prev_balance (node->ledger .balance (transaction, block_l-> hashables . previous ));
1295
- if (prev_balance.number () <= block_l-> hashables . balance . number () )
1336
+ rai::amount prev_balance (node->ledger .balance (transaction, previous));
1337
+ if (prev_balance.number () <= balance)
1296
1338
{
1297
1339
lazy_add (link);
1298
1340
}
1299
1341
}
1342
+ // Search balance of already processed previous blocks
1343
+ else if (lazy_blocks.find (previous) != lazy_blocks.end ())
1344
+ {
1345
+ auto previous_balance (lazy_balances.find (previous));
1346
+ if (previous_balance != lazy_balances.end ())
1347
+ {
1348
+ if (previous_balance->second <= balance)
1349
+ {
1350
+ lazy_add (link);
1351
+ }
1352
+ lazy_balances.erase (previous_balance);
1353
+ }
1354
+ }
1355
+ // Insert in unknown state blocks if previous wasn't already processed
1300
1356
else
1301
1357
{
1302
- lazy_state_unknown.insert (std::make_pair (block_l-> hashables . previous , block_l ));
1358
+ lazy_state_unknown.insert (std::make_pair (previous, std::make_pair (link, balance) ));
1303
1359
}
1304
1360
}
1305
1361
}
1306
1362
}
1363
+ lazy_blocks.insert (hash);
1364
+ // Adding lazy balances
1365
+ if (total_blocks == 0 )
1366
+ {
1367
+ lazy_balances.insert (std::make_pair (hash, balance));
1368
+ }
1369
+ // Removing lazy balances
1370
+ if (!block_a->previous ().is_zero () && lazy_balances.find (block_a->previous ()) != lazy_balances.end ())
1371
+ {
1372
+ lazy_balances.erase (block_a->previous ());
1373
+ }
1307
1374
}
1308
1375
// Drop bulk_pull if block is already known (ledger)
1309
1376
else
1310
1377
{
1311
1378
// Disabled until server rewrite
1312
1379
// stop_pull = true;
1380
+ // Force drop lazy bootstrap connection for long bulk_pull
1381
+ if (total_blocks > lazy_max_pull_blocks)
1382
+ {
1383
+ stop_pull = true ;
1384
+ }
1313
1385
}
1314
1386
// Search unknown state blocks balances
1315
1387
auto find_state (lazy_state_unknown.find (hash));
@@ -1321,18 +1393,18 @@ bool rai::bootstrap_attempt::process_block (std::shared_ptr<rai::block> block_a)
1321
1393
if (block_a->type () == rai::block_type::state)
1322
1394
{
1323
1395
std::shared_ptr<rai::state_block> block_l (std::static_pointer_cast<rai::state_block> (block_a));
1324
- if (block_l->hashables .balance .number () <= next_block-> hashables . balance . number () )
1396
+ if (block_l->hashables .balance .number () <= next_block. second )
1325
1397
{
1326
- lazy_add (next_block-> hashables . link );
1398
+ lazy_add (next_block. first );
1327
1399
}
1328
1400
}
1329
1401
// Retrieve balance for previous legacy send blocks
1330
1402
else if (block_a->type () == rai::block_type::send)
1331
1403
{
1332
1404
std::shared_ptr<rai::send_block> block_l (std::static_pointer_cast<rai::send_block> (block_a));
1333
- if (block_l->hashables .balance .number () <= next_block-> hashables . balance . number () )
1405
+ if (block_l->hashables .balance .number () <= next_block. second )
1334
1406
{
1335
- lazy_add (next_block-> hashables . link );
1407
+ lazy_add (next_block. first );
1336
1408
}
1337
1409
}
1338
1410
// Weak assumption for other legacy block types
@@ -1347,8 +1419,18 @@ bool rai::bootstrap_attempt::process_block (std::shared_ptr<rai::block> block_a)
1347
1419
{
1348
1420
// Disabled until server rewrite
1349
1421
// stop_pull = true;
1422
+ // Force drop lazy bootstrap connection for long bulk_pull
1423
+ if (total_blocks > lazy_max_pull_blocks)
1424
+ {
1425
+ stop_pull = true ;
1426
+ }
1350
1427
}
1351
1428
}
1429
+ else if (lazy_mode)
1430
+ {
1431
+ // Drop connection with unexpected block for lazy bootstrap
1432
+ stop_pull = true ;
1433
+ }
1352
1434
else
1353
1435
{
1354
1436
node->block_processor .add (block_a, std::chrono::steady_clock::time_point ());
0 commit comments