@@ -24,6 +24,11 @@ struct ParallelPartitionConfig
24
24
, mInstructionsPerCluster (sorobanCfg.ledgerMaxInstructions() /
25
25
mStageCount )
26
26
{
27
+ CLOG_INFO (Herder,
28
+ " ParallelPartitionConfig: stages={}, clusters/stage={}, "
29
+ " instructions/cluster={}, total max instructions={}" ,
30
+ mStageCount , mClustersPerStage , mInstructionsPerCluster ,
31
+ sorobanCfg.ledgerMaxInstructions ());
27
32
}
28
33
29
34
uint64_t
@@ -108,10 +113,17 @@ class Stage
108
113
// exceed the theoretical limit of instructions per stage.
109
114
if (mInstructions + tx.mInstructions > mConfig .instructionsPerStage ())
110
115
{
116
+ CLOG_DEBUG (Herder,
117
+ " Tx {} rejected from stage: would exceed instruction "
118
+ " limit ({} + {} > {})" ,
119
+ tx.mId , mInstructions , tx.mInstructions ,
120
+ mConfig .instructionsPerStage ());
111
121
return false ;
112
122
}
113
123
// First, find all clusters that conflict with the new transaction.
114
124
auto conflictingClusters = getConflictingClusters (tx);
125
+ CLOG_DEBUG (Herder, " Tx {} has {} conflicting clusters" , tx.mId ,
126
+ conflictingClusters.size ());
115
127
116
128
// Then, try creating new clusters by merging the conflicting clusters
117
129
// together and adding the new transaction to the resulting cluster.
@@ -122,6 +134,9 @@ class Stage
122
134
// stage and thus no new clusters could be created.
123
135
if (!newClusters)
124
136
{
137
+ CLOG_DEBUG (Herder,
138
+ " Tx {} rejected: cluster would be too large after merge" ,
139
+ tx.mId );
125
140
return false ;
126
141
}
127
142
// If it's possible to pack the newly-created cluster into one of the
@@ -175,6 +190,9 @@ class Stage
175
190
// the required number of bins.
176
191
if (!newPacking)
177
192
{
193
+ CLOG_DEBUG (Herder,
194
+ " Tx {} rejected: bin packing failed after rebuild" ,
195
+ tx.mId );
178
196
return false ;
179
197
}
180
198
mClusters = std::move (newClusters.value ());
@@ -399,35 +417,53 @@ buildSurgePricedParallelSorobanPhaseWithStageCount(
399
417
400
418
// Visit the transactions in the surge pricing queue and try to add them to
401
419
// at least one of the stages.
402
- auto visitor = [&stages,
403
- &builderTxForTx](TransactionFrameBaseConstPtr const & tx) {
420
+ size_t processedCount = 0 ;
421
+ size_t rejectedCount = 0 ;
422
+ auto visitor = [&stages, &builderTxForTx, &processedCount,
423
+ &rejectedCount](TransactionFrameBaseConstPtr const & tx) {
404
424
bool added = false ;
405
425
auto builderTxIt = builderTxForTx.find (tx);
406
426
releaseAssert (builderTxIt != builderTxForTx.end ());
427
+
428
+ size_t stageNum = 0 ;
407
429
for (auto & stage : stages)
408
430
{
409
431
if (stage.tryAdd (*builderTxIt->second ))
410
432
{
411
433
added = true ;
434
+ CLOG_DEBUG (Herder, " Transaction {} added to stage {}" ,
435
+ builderTxIt->second ->mId , stageNum);
412
436
break ;
413
437
}
438
+ stageNum++;
414
439
}
415
440
if (added)
416
441
{
442
+ processedCount++;
417
443
return SurgePricingPriorityQueue::VisitTxResult::PROCESSED;
418
444
}
419
445
// If a transaction didn't fit into any of the stages, we consider it
420
446
// to have been excluded due to resource limits and thus notify the
421
447
// surge pricing queue that surge pricing should be triggered (
422
448
// REJECTED imitates the behavior for exceeding the resource limit
423
449
// within the queue itself).
450
+ rejectedCount++;
451
+ CLOG_DEBUG (Herder,
452
+ " Transaction {} rejected - couldn't fit in any stage" ,
453
+ builderTxIt->second ->mId );
424
454
return SurgePricingPriorityQueue::VisitTxResult::REJECTED;
425
455
};
426
456
427
457
ParallelPhaseBuildResult result;
428
458
std::vector<Resource> laneLeftUntilLimitUnused;
429
459
queue.popTopTxs (/* allowGaps */ true , visitor, laneLeftUntilLimitUnused,
430
460
result.mHadTxNotFittingLane , ledgerVersion);
461
+
462
+ CLOG_INFO (
463
+ Herder,
464
+ " Stage building complete: {} processed, {} rejected, stage count {}" ,
465
+ processedCount, rejectedCount, stageCount);
466
+
431
467
// There is only a single fee lane for Soroban, so there is only a single
432
468
// flag that indicates whether there was a transaction that didn't fit into
433
469
// lane (and thus all transactions are surge priced at once).
@@ -464,14 +500,24 @@ buildSurgePricedParallelSorobanPhaseWithStageCount(
464
500
{
465
501
releaseAssert (!cluster.empty ());
466
502
}
503
+ CLOG_DEBUG (Herder, " Stage {} has {} clusters" , stages.size () - 1 ,
504
+ resStage.size ());
467
505
}
468
506
// Ensure we don't return any empty stages, which is prohibited by the
469
507
// protocol. The algorithm builds the stages such that the stages are
470
508
// populated from first to last.
509
+ size_t emptyStagesRemoved = 0 ;
471
510
while (!result.mStages .empty () && result.mStages .back ().empty ())
472
511
{
473
512
result.mStages .pop_back ();
513
+ emptyStagesRemoved++;
474
514
}
515
+
516
+ CLOG_INFO (Herder,
517
+ " Final result: {} stages, {} empty stages removed, total fee {}" ,
518
+ result.mStages .size (), emptyStagesRemoved,
519
+ result.mTotalInclusionFee );
520
+
475
521
for (auto const & stage : result.mStages )
476
522
{
477
523
releaseAssert (!stage.empty ());
@@ -490,6 +536,9 @@ buildSurgePricedParallelSorobanPhase(
490
536
std::vector<bool >& hadTxNotFittingLane, uint32_t ledgerVersion)
491
537
{
492
538
ZoneScoped;
539
+ CLOG_DEBUG (Herder, " Building parallel Soroban phase with {} transactions" ,
540
+ txFrames.size ());
541
+
493
542
// We prefer the transaction sets that are well utilized, but we also want
494
543
// to lower the stage count when possible. Thus we will nominate a tx set
495
544
// that has the lowest amount of stages while still being within
@@ -507,6 +556,8 @@ buildSurgePricedParallelSorobanPhase(
507
556
auto const & txFrame = txFrames[i];
508
557
builderTxs.emplace_back (std::make_unique<BuilderTx>(i, *txFrame));
509
558
builderTxForTx.emplace (txFrame, builderTxs.back ().get ());
559
+ CLOG_DEBUG (Herder, " Transaction {} has {} instructions" , i,
560
+ txFrame->sorobanResources ().instructions );
510
561
}
511
562
512
563
// Before trying to include any transactions, find all the pairs of the
@@ -542,6 +593,11 @@ buildSurgePricedParallelSorobanPhase(
542
593
}
543
594
}
544
595
596
+ CLOG_DEBUG (Herder,
597
+ " Found {} RO keys and {} RW keys across all transactions" ,
598
+ txsWithRoKey.size (), txsWithRwKey.size ());
599
+
600
+ size_t totalConflicts = 0 ;
545
601
for (auto const & [key, rwTxIds] : txsWithRwKey)
546
602
{
547
603
// RW-RW conflicts
@@ -551,6 +607,7 @@ buildSurgePricedParallelSorobanPhase(
551
607
{
552
608
builderTxs[rwTxIds[i]]->mConflictTxs .set (rwTxIds[j]);
553
609
builderTxs[rwTxIds[j]]->mConflictTxs .set (rwTxIds[i]);
610
+ totalConflicts += 2 ;
554
611
}
555
612
}
556
613
// RO-RW conflicts
@@ -564,17 +621,25 @@ buildSurgePricedParallelSorobanPhase(
564
621
{
565
622
builderTxs[roTxIds[i]]->mConflictTxs .set (rwTxIds[j]);
566
623
builderTxs[rwTxIds[j]]->mConflictTxs .set (roTxIds[i]);
624
+ totalConflicts += 2 ;
567
625
}
568
626
}
569
627
}
570
628
}
571
629
630
+ CLOG_DEBUG (Herder, " Found {} total conflicts between transactions" ,
631
+ totalConflicts);
632
+
572
633
// Process the transactions in the surge pricing (decreasing fee) order.
573
634
// This also automatically ensures that the resource limits are respected
574
635
// for all the dimensions besides instructions.
575
636
SurgePricingPriorityQueue queue (
576
637
/* isHighestPriority */ true , laneConfig,
577
638
stellar::rand_uniform<size_t >(0 , std::numeric_limits<size_t >::max ()));
639
+
640
+ CLOG_DEBUG (Herder, " Adding {} transactions to surge pricing queue" ,
641
+ txFrames.size ());
642
+
578
643
for (auto const & tx : txFrames)
579
644
{
580
645
queue.add (tx, ledgerVersion);
@@ -586,6 +651,10 @@ buildSurgePricedParallelSorobanPhase(
586
651
cfg.SOROBAN_PHASE_MIN_STAGE_COUNT + 1 ;
587
652
std::vector<ParallelPhaseBuildResult> results (stageCountOptions);
588
653
654
+ CLOG_DEBUG (Herder, " Creating {} threads for stage counts {} to {}" ,
655
+ stageCountOptions, cfg.SOROBAN_PHASE_MIN_STAGE_COUNT ,
656
+ cfg.SOROBAN_PHASE_MAX_STAGE_COUNT );
657
+
589
658
for (uint32_t stageCount = cfg.SOROBAN_PHASE_MIN_STAGE_COUNT ;
590
659
stageCount <= cfg.SOROBAN_PHASE_MAX_STAGE_COUNT ; ++stageCount)
591
660
{
@@ -611,26 +680,46 @@ buildSurgePricedParallelSorobanPhase(
611
680
std::max (maxTotalInclusionFee, result.mTotalInclusionFee );
612
681
}
613
682
maxTotalInclusionFee *= MAX_INCLUSION_FEE_TOLERANCE_FOR_STAGE_COUNT;
683
+
684
+ CLOG_DEBUG (Herder, " Max total inclusion fee: {}, tolerance threshold: {}" ,
685
+ maxTotalInclusionFee /
686
+ MAX_INCLUSION_FEE_TOLERANCE_FOR_STAGE_COUNT,
687
+ maxTotalInclusionFee);
688
+
614
689
std::optional<size_t > bestResultIndex = std::nullopt;
615
690
for (size_t i = 0 ; i < results.size (); ++i)
616
691
{
617
692
CLOG_DEBUG (Herder,
618
693
" Parallel Soroban tx set nomination: {} stages => {} total "
619
694
" inclusion fee" ,
620
- results[i].mTotalInclusionFee , results[i].mStages . size () );
695
+ results[i].mStages . size () , results[i].mTotalInclusionFee );
621
696
if (results[i].mTotalInclusionFee < maxTotalInclusionFee)
622
697
{
698
+ CLOG_DEBUG (Herder, " Skipping result {} - fee {} below threshold {}" ,
699
+ i, results[i].mTotalInclusionFee , maxTotalInclusionFee);
623
700
continue ;
624
701
}
625
702
if (!bestResultIndex ||
626
703
results[i].mStages .size () <
627
704
results[bestResultIndex.value ()].mStages .size ())
628
705
{
706
+ CLOG_DEBUG (Herder, " New best result: index {} with {} stages" , i,
707
+ results[i].mStages .size ());
629
708
bestResultIndex = std::make_optional (i);
630
709
}
631
710
}
632
- releaseAssert (bestResultIndex.has_value ());
711
+
712
+ if (!bestResultIndex.has_value ())
713
+ {
714
+ CLOG_ERROR (Herder, " No valid parallel phase result found!" );
715
+ releaseAssert (false );
716
+ }
717
+
633
718
auto & bestResult = results[bestResultIndex.value ()];
719
+ CLOG_INFO (Herder, " Selected result {} with {} stages and {} total fee" ,
720
+ bestResultIndex.value (), bestResult.mStages .size (),
721
+ bestResult.mTotalInclusionFee );
722
+
634
723
hadTxNotFittingLane = std::move (bestResult.mHadTxNotFittingLane );
635
724
return std::move (bestResult.mStages );
636
725
}
0 commit comments