@@ -3,8 +3,10 @@ package snapshotdl
33import (
44 "context"
55 "fmt"
6+ stdlog "log"
67 "strconv"
78 "strings"
9+ "sync"
810 "time"
911
1012 "github.com/Overclock-Validator/mithril/pkg/mlog"
@@ -13,6 +15,26 @@ import (
1315 "github.com/Overclock-Validator/solana-snapshot-finder-go/pkg/snapshot"
1416)
1517
18+ // stdlogMu protects global log config changes from racing if multiple
19+ // snapshot flows run concurrently.
20+ var stdlogMu sync.Mutex
21+
22+ // suppressStdlogTimestamps temporarily strips stdlib log flags/prefix so
23+ // snapshot-finder output matches Mithril style. Caller must defer the
24+ // returned restore function.
25+ func suppressStdlogTimestamps () func () {
26+ stdlogMu .Lock ()
27+ oldFlags := stdlog .Flags ()
28+ oldPrefix := stdlog .Prefix ()
29+ stdlog .SetFlags (0 )
30+ stdlog .SetPrefix ("" )
31+ return func () {
32+ stdlog .SetFlags (oldFlags )
33+ stdlog .SetPrefix (oldPrefix )
34+ stdlogMu .Unlock ()
35+ }
36+ }
37+
1638// SnapshotInfo contains details about a selected snapshot source.
1739// This is returned by GetSnapshotURLWithInfo for display purposes.
1840type SnapshotInfo struct {
@@ -176,7 +198,6 @@ func (sc SnapshotConfig) toInternalConfig(path string) config.Config {
176198 AllowedNodeVersions : sc .AllowedNodeVersions ,
177199 MaxFullSnapshots : sc .MaxFullSnapshots ,
178200 SafetyMarginSlots : sc .SafetyMarginSlots ,
179- Quiet : true , // Mithril prints its own summary
180201 }
181202}
182203
@@ -290,6 +311,7 @@ func DownloadSnapshot(ctx context.Context, rpcEndpoints []string, path string) (
290311// The returned URL can be passed directly to snapshot processing functions
291312// which will stream the data from HTTP (no disk download required).
292313func GetSnapshotURL (ctx context.Context , snapCfg SnapshotConfig ) (string , int , int , error ) {
314+ defer suppressStdlogTimestamps ()()
293315 cfg := snapCfg .toInternalConfig ("" )
294316
295317 // Step 1: Get reference slot from multiple RPCs for reliability
@@ -317,7 +339,7 @@ func GetSnapshotURL(ctx context.Context, snapCfg SnapshotConfig) (string, int, i
317339
318340 // Step 4: Sort and select best nodes by download speed
319341 // Note: This also populates filter pipeline stats in ProbeStats
320- bestNodes , _ , speedStats := rpc .SortBestNodesWithStats (results , cfg , stats , referenceSlot )
342+ bestNodes , _ := rpc .SortBestNodesWithStats (results , cfg , stats , referenceSlot )
321343 if len (bestNodes ) == 0 {
322344 return "" , 0 , 0 , fmt .Errorf ("no suitable nodes found with snapshots" )
323345 }
@@ -331,8 +353,7 @@ func GetSnapshotURL(ctx context.Context, snapCfg SnapshotConfig) (string, int, i
331353 MinVersion : cfg .MinNodeVersion ,
332354 AllowedVersions : cfg .AllowedNodeVersions ,
333355 }
334- stats .PrintNodeDiscoveryReport ()
335- stats .PrintFilterPipeline (filterCfg , speedStats )
356+ stats .PrintReport (filterCfg )
336357 }
337358
338359 // Step 5: Get snapshot URL from best nodes (with configurable fallback)
@@ -399,6 +420,7 @@ func GetSnapshotURL(ctx context.Context, snapCfg SnapshotConfig) (string, int, i
399420// This is like GetSnapshotURL but returns a SnapshotInfo struct with additional
400421// details useful for display (node IP, version, speed, age).
401422func GetSnapshotURLWithInfo (ctx context.Context , snapCfg SnapshotConfig ) (* SnapshotInfo , error ) {
423+ defer suppressStdlogTimestamps ()()
402424 cfg := snapCfg .toInternalConfig ("" )
403425
404426 // Step 1: Get reference slot from multiple RPCs for reliability
@@ -424,11 +446,6 @@ func GetSnapshotURLWithInfo(ctx context.Context, snapCfg SnapshotConfig) (*Snaps
424446 // This prevents selecting a fast full snapshot that has no compatible incrementals
425447 results , incBaseStats := filterByIncrementalBaseMatch (results )
426448
427- // Print Node Discovery Report (before speed testing)
428- if stats != nil {
429- stats .PrintNodeDiscoveryReport ()
430- }
431-
432449 // Print incremental base match stats
433450 if incBaseStats .totalWithFull > 0 {
434451 mlog .Log .Infof ("Incremental base matching: %d/%d full snapshots have compatible incrementals (%d unique base slots)" ,
@@ -442,49 +459,23 @@ func GetSnapshotURLWithInfo(ctx context.Context, snapCfg SnapshotConfig) (*Snaps
442459 // Step 4: Sort and select best nodes by download speed
443460 // Note: This also populates filter pipeline stats in ProbeStats
444461 mlog .Log .Infof ("Testing download speeds (Stage 1 + Stage 2)..." )
445- bestNodes , rankedNodes , speedStats := rpc .SortBestNodesWithStats (results , cfg , stats , referenceSlot )
462+ bestNodes , rankedNodes := rpc .SortBestNodesWithStats (results , cfg , stats , referenceSlot )
446463 if len (bestNodes ) == 0 {
447464 return nil , fmt .Errorf ("no suitable nodes found with snapshots (check incremental base matching)" )
448465 }
449466
450- // Print Stage 2 candidates as a table (no timestamps)
451- maxCandidates := 8
452- if len (rankedNodes ) < maxCandidates {
453- maxCandidates = len (rankedNodes )
454- }
455- candidates := make ([]rpc.RankedNodeInfo , maxCandidates )
456- for i := 0 ; i < maxCandidates ; i ++ {
457- rn := rankedNodes [i ]
458- candidates [i ] = rpc.RankedNodeInfo {
459- Rank : i + 1 ,
460- RPC : rn .Result .RPC ,
461- Version : rn .Result .Version ,
462- RTTMs : int (rn .Result .Latency ), // Latency is already in milliseconds
463- SpeedS1 : rn .S1 .MedianMBs ,
464- SpeedS2 : rn .S2 .MinMBs ,
465- }
466- }
467- rpc .PrintStage2CandidatesTable (candidates )
468-
469- // Print Filter Pipeline with speed test stats (after speed testing, before source selection)
470- filterCfg := rpc.FilterConfig {
471- MaxRTTMs : cfg .MaxRTTMs ,
472- FullThreshold : cfg .FullThreshold ,
473- IncThreshold : cfg .IncrementalThreshold ,
474- MinVersion : cfg .MinNodeVersion ,
475- AllowedVersions : cfg .AllowedNodeVersions ,
476- }
467+ // Print statistics report (after filtering is complete)
477468 if stats != nil {
478- stats .PrintFilterPipeline (filterCfg , speedStats )
469+ filterCfg := rpc.FilterConfig {
470+ MaxRTTMs : cfg .MaxRTTMs ,
471+ FullThreshold : cfg .FullThreshold ,
472+ IncThreshold : cfg .IncrementalThreshold ,
473+ MinVersion : cfg .MinNodeVersion ,
474+ AllowedVersions : cfg .AllowedNodeVersions ,
475+ }
476+ stats .PrintReport (filterCfg )
479477 }
480478
481- // Speed test log writing disabled - uncomment to enable
482- // if speedStats != nil && snapCfg.LogDir != "" {
483- // if err := speedStats.WriteSpeedTestLog(snapCfg.LogDir, filterCfg); err != nil {
484- // mlog.Log.Infof("Warning: failed to write speed test log: %v", err)
485- // }
486- // }
487-
488479 // Step 5: Get snapshot URL from best nodes (with configurable fallback)
489480 var snapshotURL string
490481 var snapshotSlot int
@@ -574,6 +565,7 @@ func GetSnapshotURLWithInfo(ctx context.Context, snapCfg SnapshotConfig) (*Snaps
574565
575566// DownloadSnapshotWithConfig is like DownloadSnapshot but accepts custom config
576567func DownloadSnapshotWithConfig (ctx context.Context , path string , snapCfg SnapshotConfig ) (string , int , int , error ) {
568+ defer suppressStdlogTimestamps ()()
577569 cfg := snapCfg .toInternalConfig (path )
578570
579571 // Step 1: Get reference slot from multiple RPCs for reliability
@@ -601,7 +593,7 @@ func DownloadSnapshotWithConfig(ctx context.Context, path string, snapCfg Snapsh
601593
602594 // Step 4: Sort and select best nodes by download speed
603595 // Note: This also populates filter pipeline stats in ProbeStats
604- bestNodes , _ , speedStats := rpc .SortBestNodesWithStats (results , cfg , stats , referenceSlot )
596+ bestNodes , _ := rpc .SortBestNodesWithStats (results , cfg , stats , referenceSlot )
605597 if len (bestNodes ) == 0 {
606598 return "" , 0 , 0 , fmt .Errorf ("no suitable nodes found with snapshots" )
607599 }
@@ -615,8 +607,7 @@ func DownloadSnapshotWithConfig(ctx context.Context, path string, snapCfg Snapsh
615607 MinVersion : cfg .MinNodeVersion ,
616608 AllowedVersions : cfg .AllowedNodeVersions ,
617609 }
618- stats .PrintNodeDiscoveryReport ()
619- stats .PrintFilterPipeline (filterCfg , speedStats )
610+ stats .PrintReport (filterCfg )
620611 }
621612
622613 // Step 5: Download snapshot from best nodes (with configurable fallback)
@@ -694,6 +685,7 @@ func DownloadIncrementalSnapshot(rpcEndpoints []string, path string, referenceSl
694685
695686// DownloadIncrementalSnapshotWithConfig is like DownloadIncrementalSnapshot but accepts custom config
696687func DownloadIncrementalSnapshotWithConfig (path string , referenceSlot int , fullSnapshotSlot int , snapCfg SnapshotConfig ) (string , int , int , error ) {
688+ defer suppressStdlogTimestamps ()()
697689 cfg := snapCfg .toInternalConfig (path )
698690 ctx := context .Background ()
699691
@@ -720,8 +712,7 @@ func DownloadIncrementalSnapshotWithConfig(path string, referenceSlot int, fullS
720712 MinVersion : cfg .MinNodeVersion ,
721713 AllowedVersions : cfg .AllowedNodeVersions ,
722714 }
723- stats .PrintNodeDiscoveryReport ()
724- stats .PrintFilterPipeline (filterCfg , nil )
715+ stats .PrintReport (filterCfg )
725716 }
726717
727718 // Step 3: Filter to only nodes with matching incremental base slot FIRST
@@ -740,7 +731,7 @@ func DownloadIncrementalSnapshotWithConfig(path string, referenceSlot int, fullS
740731 }
741732
742733 // Step 3.5: Apply threshold filtering with two-pass approach
743- bestNodes , rankedNodes , _ := rpc .SortBestRPCsFilteredBySlot (
734+ bestNodes , rankedNodes := rpc .SortBestRPCsFilteredBySlot (
744735 baseMatchingResults , cfg , stats , int64 (fullSnapshotSlot ), referenceSlot )
745736
746737 // Pass 2: If no matches, relax incremental threshold
@@ -751,7 +742,7 @@ func DownloadIncrementalSnapshotWithConfig(path string, referenceSlot int, fullS
751742
752743 relaxedCfg := cfg
753744 relaxedCfg .IncrementalThreshold = relaxedThreshold
754- bestNodes , rankedNodes , _ = rpc .SortBestRPCsFilteredBySlot (
745+ bestNodes , rankedNodes = rpc .SortBestRPCsFilteredBySlot (
755746 baseMatchingResults , relaxedCfg , nil , int64 (fullSnapshotSlot ), referenceSlot )
756747 }
757748
@@ -867,6 +858,7 @@ func extractFullSnapshotSlot(path string) int {
867858// b. Speed (faster downloads preferred when end slots are equal)
868859// 4. Try multiple candidates for resilience (uses MaxSnapshotURLAttempts)
869860func GetIncrementalSnapshotURL (fullSnapshotURL string , referenceSlot int , fullSnapshotSlot int , snapCfg SnapshotConfig ) (string , int , int , error ) {
861+ defer suppressStdlogTimestamps ()()
870862 cfg := snapCfg .toInternalConfig ("" )
871863 ctx := context .Background ()
872864
@@ -927,8 +919,7 @@ func GetIncrementalSnapshotURL(fullSnapshotURL string, referenceSlot int, fullSn
927919 MinVersion : cfg .MinNodeVersion ,
928920 AllowedVersions : cfg .AllowedNodeVersions ,
929921 }
930- stats .PrintNodeDiscoveryReport ()
931- stats .PrintFilterPipeline (filterCfg , nil )
922+ stats .PrintReport (filterCfg )
932923 }
933924
934925 // Step 2.1: Filter to only nodes with matching base slot FIRST (before threshold filtering)
@@ -952,7 +943,7 @@ func GetIncrementalSnapshotURL(fullSnapshotURL string, referenceSlot int, fullSn
952943 var matchingNodes []rpc.RankedNode
953944
954945 // Pass 1: Apply normal incremental threshold
955- _ , rankedNodes , _ := rpc .SortBestRPCsFilteredBySlot (
946+ _ , rankedNodes := rpc .SortBestRPCsFilteredBySlot (
956947 baseMatchingResults , cfg , stats , int64 (fullSnapshotSlot ), referenceSlot )
957948 for _ , node := range rankedNodes {
958949 matchingNodes = append (matchingNodes , node )
@@ -968,7 +959,7 @@ func GetIncrementalSnapshotURL(fullSnapshotURL string, referenceSlot int, fullSn
968959 relaxedCfg := cfg
969960 relaxedCfg .IncrementalThreshold = relaxedThreshold
970961
971- _ , rankedNodesRelaxed , _ := rpc .SortBestRPCsFilteredBySlot (
962+ _ , rankedNodesRelaxed := rpc .SortBestRPCsFilteredBySlot (
972963 baseMatchingResults , relaxedCfg , nil , int64 (fullSnapshotSlot ), referenceSlot )
973964 for _ , node := range rankedNodesRelaxed {
974965 matchingNodes = append (matchingNodes , node )
0 commit comments