@@ -333,11 +333,12 @@ setLedgerTxnHeader(LedgerHeader const& lh, Application& app)
333
333
}
334
334
335
335
void
336
- LedgerManagerImpl::loadLastKnownLedger (function<void ()> handler)
336
+ LedgerManagerImpl::loadLastKnownLedger (bool restoreBucketlist,
337
+ bool isLedgerStateReady)
337
338
{
338
339
ZoneScoped;
339
- auto ledgerTime = mLedgerClose .TimeScope ();
340
340
341
+ // Step 1. Load LCL state from the DB and extract latest ledger hash
341
342
string lastLedger =
342
343
mApp .getPersistentState ().getState (PersistentState::kLastClosedLedger );
343
344
@@ -346,103 +347,119 @@ LedgerManagerImpl::loadLastKnownLedger(function<void()> handler)
346
347
throw std::runtime_error (
347
348
" No reference in DB to any last closed ledger" );
348
349
}
349
- else
350
- {
351
- CLOG_INFO (Ledger, " Last closed ledger (LCL) hash is {}" , lastLedger);
352
- Hash lastLedgerHash = hexToBin256 (lastLedger);
353
350
354
- if (mApp .getConfig ().MODE_STORES_HISTORY_LEDGERHEADERS )
351
+ CLOG_INFO (Ledger, " Last closed ledger (LCL) hash is {}" , lastLedger);
352
+ Hash lastLedgerHash = hexToBin256 (lastLedger);
353
+
354
+ // Step 2. Restore LedgerHeader from DB based on the ledger hash derived
355
+ // earlier, or verify we're at genesis if in no-history mode
356
+ std::optional<LedgerHeader> latestLedgerHeader;
357
+ if (mApp .getConfig ().MODE_STORES_HISTORY_LEDGERHEADERS )
358
+ {
359
+ if (mRebuildInMemoryState )
355
360
{
356
- if (mRebuildInMemoryState )
361
+ LedgerHeader lh;
362
+ CLOG_INFO (Ledger,
363
+ " Setting empty ledger while core rebuilds state: {}" ,
364
+ ledgerAbbrev (lh));
365
+ setLedgerTxnHeader (lh, mApp );
366
+ latestLedgerHeader = lh;
367
+ }
368
+ else
369
+ {
370
+ auto currentLedger =
371
+ LedgerHeaderUtils::loadByHash (getDatabase (), lastLedgerHash);
372
+ if (!currentLedger)
357
373
{
358
- LedgerHeader lh;
359
- CLOG_INFO (Ledger,
360
- " Setting empty ledger while core rebuilds state: {}" ,
361
- ledgerAbbrev (lh));
362
- setLedgerTxnHeader (lh, mApp );
374
+ throw std::runtime_error (" Could not load ledger from database" );
363
375
}
364
- else
376
+ HistoryArchiveState has = getLastClosedLedgerHAS ();
377
+ if (currentLedger->ledgerSeq != has.currentLedger )
365
378
{
366
- auto currentLedger = LedgerHeaderUtils::loadByHash (
367
- getDatabase (), lastLedgerHash);
368
- if (!currentLedger)
369
- {
370
- throw std::runtime_error (
371
- " Could not load ledger from database" );
372
- }
373
- HistoryArchiveState has = getLastClosedLedgerHAS ();
374
- if (currentLedger->ledgerSeq != has.currentLedger )
375
- {
376
- throw std::runtime_error (
377
- " Invalid database state: last known "
378
- " ledger does not agree with HAS" );
379
- }
380
-
381
- CLOG_INFO (Ledger, " Loaded LCL header from database: {}" ,
382
- ledgerAbbrev (*currentLedger));
383
- setLedgerTxnHeader (*currentLedger, mApp );
379
+ throw std::runtime_error (" Invalid database state: last known "
380
+ " ledger does not agree with HAS" );
384
381
}
382
+
383
+ CLOG_INFO (Ledger, " Loaded LCL header from database: {}" ,
384
+ ledgerAbbrev (*currentLedger));
385
+ setLedgerTxnHeader (*currentLedger, mApp );
386
+ latestLedgerHeader = *currentLedger;
385
387
}
386
- else
388
+ }
389
+ else
390
+ {
391
+ // In no-history mode, this method should only be called when
392
+ // the LCL is genesis.
393
+ releaseAssertOrThrow (mLastClosedLedger .hash == lastLedgerHash);
394
+ releaseAssertOrThrow (mLastClosedLedger .header .ledgerSeq ==
395
+ GENESIS_LEDGER_SEQ);
396
+ CLOG_INFO (Ledger, " LCL is genesis: {}" ,
397
+ ledgerAbbrev (mLastClosedLedger ));
398
+ latestLedgerHeader = mLastClosedLedger .header ;
399
+ }
400
+
401
+ releaseAssert (latestLedgerHeader.has_value ());
402
+
403
+ // Step 3. Restore BucketList if we're doing a full core startup
404
+ // (startServices=true), OR when using BucketListDB
405
+ if (restoreBucketlist || mApp .getConfig ().isUsingBucketListDB ())
406
+ {
407
+ HistoryArchiveState has = getLastClosedLedgerHAS ();
408
+ auto missing = mApp .getBucketManager ().checkForMissingBucketsFiles (has);
409
+ auto pubmissing = mApp .getHistoryManager ()
410
+ .getMissingBucketsReferencedByPublishQueue ();
411
+ missing.insert (missing.end (), pubmissing.begin (), pubmissing.end ());
412
+ if (!missing.empty ())
387
413
{
388
- // In no-history mode, this method should only be called when
389
- // the LCL is genesis.
390
- releaseAssertOrThrow (mLastClosedLedger .hash == lastLedgerHash);
391
- releaseAssertOrThrow (mLastClosedLedger .header .ledgerSeq ==
392
- GENESIS_LEDGER_SEQ);
393
- CLOG_INFO (Ledger, " LCL is genesis: {}" ,
394
- ledgerAbbrev (mLastClosedLedger ));
414
+ CLOG_ERROR (Ledger,
415
+ " {} buckets are missing from bucket directory '{}'" ,
416
+ missing.size (), mApp .getBucketManager ().getBucketDir ());
417
+ throw std::runtime_error (" Bucket directory is corrupt" );
395
418
}
396
419
397
- if (handler )
420
+ if (mApp . getConfig (). MODE_ENABLES_BUCKETLIST )
398
421
{
399
- HistoryArchiveState has = getLastClosedLedgerHAS ();
400
- auto missing =
401
- mApp . getBucketManager (). checkForMissingBucketsFiles (has);
402
- auto pubmissing = mApp . getHistoryManager ()
403
- . getMissingBucketsReferencedByPublishQueue ();
404
- missing. insert (missing. end (), pubmissing. begin (), pubmissing. end () );
405
- if (!missing. empty () )
422
+ // Only restart merges in full startup mode. Many modes in core
423
+ // (standalone offline commands, in-memory setup) do not need to
424
+ // spin up expensive merge processes.
425
+ auto assumeStateWork =
426
+ mApp . getWorkScheduler (). executeWork <AssumeStateWork>(
427
+ has, latestLedgerHeader-> ledgerVersion , restoreBucketlist );
428
+ if (assumeStateWork-> getState () == BasicWork::State::WORK_SUCCESS )
406
429
{
407
- CLOG_ERROR (
408
- Ledger, " {} buckets are missing from bucket directory '{}'" ,
409
- missing.size (), mApp .getBucketManager ().getBucketDir ());
410
- throw std::runtime_error (" Bucket directory is corrupt" );
430
+ CLOG_INFO (Ledger, " Assumed bucket-state for LCL: {}" ,
431
+ ledgerAbbrev (*latestLedgerHeader));
411
432
}
412
433
else
413
434
{
414
- {
415
- LedgerTxn ltx (mApp .getLedgerTxnRoot ());
416
- auto header = ltx.loadHeader ();
417
- uint32_t ledgerVersion = header.current ().ledgerVersion ;
418
- if (mApp .getConfig ().MODE_ENABLES_BUCKETLIST )
419
- {
420
- auto assumeStateWork =
421
- mApp .getWorkScheduler ()
422
- .executeWork <AssumeStateWork>(has,
423
- ledgerVersion);
424
- if (assumeStateWork->getState () ==
425
- BasicWork::State::WORK_SUCCESS)
426
- {
427
- CLOG_INFO (Ledger,
428
- " Assumed bucket-state for LCL: {}" ,
429
- ledgerAbbrev (header.current ()));
430
- }
431
- else
432
- {
433
- // Work should only fail during graceful shutdown
434
- releaseAssert (mApp .isStopping ());
435
- }
436
- }
437
- advanceLedgerPointers (header.current ());
438
- }
439
- handler ();
435
+ // Work should only fail during graceful shutdown
436
+ releaseAssertOrThrow (mApp .isStopping ());
440
437
}
441
438
}
442
- else
439
+ }
440
+
441
+ // Step 4. Restore LedgerManager's internal state
442
+ advanceLedgerPointers (*latestLedgerHeader);
443
+
444
+ if (protocolVersionStartsFrom (latestLedgerHeader->ledgerVersion ,
445
+ SOROBAN_PROTOCOL_VERSION))
446
+ {
447
+ if (isLedgerStateReady)
443
448
{
449
+ // Step 5. If ledger state is ready and core is in v20, load network
450
+ // configs right away
444
451
LedgerTxn ltx (mApp .getLedgerTxnRoot ());
445
- advanceLedgerPointers (ltx.loadHeader ().current ());
452
+ updateNetworkConfig (ltx);
453
+ }
454
+ else
455
+ {
456
+ // In some modes, e.g. in-memory, core's state is rebuilt
457
+ // asynchronously via catchup. In this case, we're not able to load
458
+ // the network config at this time, and instead must let catchup do
459
+ // it when ready.
460
+ CLOG_INFO (Ledger,
461
+ " Ledger state is being rebuilt, network config will "
462
+ " be loaded once the rebuild is done" );
446
463
}
447
464
}
448
465
}
0 commit comments