@@ -17,6 +17,8 @@ public final class ExecutionAttribution implements AutoCloseable {
1717 private final Map <String , long []> stages = new LinkedHashMap <>();
1818 private final Map <String , long []> reads = new LinkedHashMap <>();
1919 private final Map <Phase , Map <String , long []>> phaseReads = new EnumMap <>(Phase .class );
20+ private final Map <String , long []> siteReads = new LinkedHashMap <>();
21+ private ReadSite site = ReadSite .OTHER ;
2022 private Phase phase = Phase .OTHER ;
2123 private boolean published ;
2224
@@ -26,6 +28,38 @@ public enum Phase {
2628 private final String label = name ().toLowerCase (Locale .ROOT );
2729 }
2830
31+ public enum ReadSite {
32+ OTHER , BANDWIDTH_OWNER , BANDWIDTH_RECEIVER , BALANCE_HISTORY ;
33+
34+ private final String label = name ().toLowerCase (Locale .ROOT );
35+ }
36+
37+ /** Narrow caller scope; restores the enclosing site even when a read throws. */
38+ public static ReadScope readSite (ReadSite site ) {
39+ ExecutionAttribution current = CURRENT .get ();
40+ return current == null ? null : new ReadScope (current , site );
41+ }
42+
43+ public static final class ReadScope implements AutoCloseable {
44+ private final ExecutionAttribution owner ;
45+ private final ReadSite previous ;
46+
47+ private ReadScope (ExecutionAttribution owner , ReadSite site ) {
48+ this .owner = owner ;
49+ this .previous = owner .site ;
50+ owner .site = site ;
51+ }
52+
53+ @ Override
54+ public void close () {
55+ owner .site = previous ;
56+ }
57+ }
58+
59+ private String siteKey (String key ) {
60+ return phase .label + ":" + site .label + ":" + key ;
61+ }
62+
2963 public static long start (Phase phase ) {
3064 ExecutionAttribution current = CURRENT .get ();
3165 if (current == null ) {
@@ -82,6 +116,7 @@ public static long sample(String store, String operation) {
82116 long [] row = current .reads .computeIfAbsent (key , ignored -> new long [5 ]);
83117 current .phaseReads .computeIfAbsent (current .phase , ignored -> new LinkedHashMap <>())
84118 .computeIfAbsent (key , ignored -> new long [5 ])[0 ]++;
119+ current .siteReads .computeIfAbsent (current .siteKey (key ), ignored -> new long [5 ])[0 ]++;
85120 return (row [0 ]++ & 63 ) == 0 ? System .nanoTime () : 0 ;
86121 }
87122
@@ -95,6 +130,7 @@ public static void sampled(String store, String operation, long start, int layer
95130 String key = store + ":" + operation ;
96131 long elapsed = System .nanoTime () - start ;
97132 finish (current .reads .get (key ), elapsed , layers , hit );
133+ finish (current .siteReads .get (current .siteKey (key )), elapsed , layers , hit );
98134 finish (current .phaseReads .get (current .phase ).get (key ), elapsed , layers , hit );
99135 }
100136 }
@@ -137,6 +173,19 @@ public void publish() {
137173 }
138174 }
139175 });
176+ siteReads .forEach ((name , row ) -> {
177+ String [] parts = name .split (":" );
178+ logger .info ("Chainbase execution site read: head={}, blockHash={}, status=executed, "
179+ + "phase={}, site={}, store={}, operation={}, calls={}, samples={}, nanos={}, "
180+ + "layers={}, hits={}" , head , hash , parts [0 ], parts [1 ], parts [2 ], parts [3 ],
181+ row [0 ], row [1 ], row [2 ], row [3 ], row [4 ]);
182+ if (Metrics .enabled ()) {
183+ String [] kinds = {"calls" , "samples" , "nanos" , "layers" , "hits" };
184+ for (int i = 0 ; i < kinds .length ; i ++) {
185+ Export .SITE_READS .labels (parts [0 ], parts [1 ], parts [2 ], parts [3 ], kinds [i ]).inc (row [i ]);
186+ }
187+ }
188+ });
140189 phaseReads .forEach ((phase , operations ) -> operations .forEach ((name , row ) -> {
141190 String [] parts = name .split (":" );
142191 logger .info ("Chainbase execution phase read: head={}, blockHash={}, status=executed, "
@@ -182,6 +231,10 @@ private static final class Export {
182231 .name ("tron_chainbase_execution_phase_read_total" )
183232 .help ("Partition of loop read counters by caller phase; sampled time is not total IO." )
184233 .labelNames ("phase" , "store" , "operation" , "kind" ).register ();
234+ private static final Counter SITE_READS = Counter .build ()
235+ .name ("tron_chainbase_execution_site_read_total" )
236+ .help ("Caller site partition of loop reads; sampled time is not total IO." )
237+ .labelNames ("phase" , "site" , "store" , "operation" , "kind" ).register ();
185238 private static final Counter READS = Counter .build ()
186239 .name ("tron_chainbase_execution_read_total" )
187240 .help ("Loop-thread reads; sampled nanoseconds/layers/hits, not physical disk IO." )
0 commit comments