@@ -51,6 +51,9 @@ pub struct ExecutionTimeObserver {
51
51
// via consensus.
52
52
object_utilization_tracker : LruCache < ObjectID , ObjectUtilization > ,
53
53
54
+ // Sorted list of recently indebted objects, updated by consensus handler.
55
+ indebted_objects : Vec < ObjectID > ,
56
+
54
57
sharing_rate_limiter : RateLimiter <
55
58
governor:: state:: NotKeyed ,
56
59
governor:: state:: InMemoryState ,
@@ -87,14 +90,17 @@ impl ExecutionTimeObserver {
87
90
88
91
let ( tx_local_execution_time, mut rx_local_execution_time) =
89
92
mpsc:: channel ( config. observation_channel_capacity ( ) . into ( ) ) ;
90
- epoch_store. set_local_execution_time_channel ( tx_local_execution_time) ;
93
+ let ( tx_object_debts, mut rx_object_debts) =
94
+ mpsc:: channel ( config. object_debt_channel_capacity ( ) . into ( ) ) ;
95
+ epoch_store. set_local_execution_time_channels ( tx_local_execution_time, tx_object_debts) ;
91
96
92
97
// TODO: pre-populate local observations with stored data from prior epoch.
93
98
let mut observer = Self {
94
99
epoch_store : Arc :: downgrade ( & epoch_store) ,
95
100
consensus_adapter,
96
101
local_observations : LruCache :: new ( config. observation_cache_size ( ) ) ,
97
102
object_utilization_tracker : LruCache :: new ( config. object_utilization_cache_size ( ) ) ,
103
+ indebted_objects : Vec :: new ( ) ,
98
104
sharing_rate_limiter : RateLimiter :: direct (
99
105
Quota :: per_second ( config. observation_sharing_rate_limit ( ) )
100
106
. allow_burst ( config. observation_sharing_burst_limit ( ) ) ,
@@ -103,10 +109,19 @@ impl ExecutionTimeObserver {
103
109
config,
104
110
} ;
105
111
spawn_monitored_task ! ( epoch_store. within_alive_epoch( async move {
106
- while let Some ( ( tx, timings, total_duration) ) = rx_local_execution_time. recv( ) . await {
107
- observer
108
- . record_local_observations( & tx, & timings, total_duration)
109
- . await ;
112
+ loop {
113
+ tokio:: select! {
114
+ // TODO: add metrics for messages received.
115
+ Some ( object_debts) = rx_object_debts. recv( ) => {
116
+ observer. update_indebted_objects( object_debts) ;
117
+ }
118
+ Some ( ( tx, timings, total_duration) ) = rx_local_execution_time. recv( ) => {
119
+ observer
120
+ . record_local_observations( & tx, & timings, total_duration)
121
+ . await ;
122
+ }
123
+ else => { break }
124
+ }
110
125
}
111
126
info!( "shutting down ExecutionTimeObserver" ) ;
112
127
} ) ) ;
@@ -136,6 +151,7 @@ impl ExecutionTimeObserver {
136
151
} ,
137
152
local_observations : LruCache :: new ( NonZeroUsize :: new ( 10000 ) . unwrap ( ) ) ,
138
153
object_utilization_tracker : LruCache :: new ( NonZeroUsize :: new ( 50000 ) . unwrap ( ) ) ,
154
+ indebted_objects : Vec :: new ( ) ,
139
155
sharing_rate_limiter : RateLimiter :: direct ( Quota :: per_hour ( std:: num:: NonZeroU32 :: MAX ) ) ,
140
156
}
141
157
}
@@ -154,12 +170,19 @@ impl ExecutionTimeObserver {
154
170
155
171
assert ! ( tx. commands. len( ) >= timings. len( ) ) ;
156
172
173
+ let mut uses_indebted_object = false ;
174
+
157
175
// Update the accumulated excess execution time for each mutable shared object
158
176
// used in this transaction, and determine the max overage.
159
177
let max_excess_per_object_execution_time = tx
160
178
. shared_input_objects ( )
161
179
. filter_map ( |obj| obj. mutable . then_some ( obj. id ) )
162
180
. map ( |id| {
181
+ // Mark if any object used in the tx is indebted.
182
+ if !uses_indebted_object && self . indebted_objects . binary_search ( & id) . is_ok ( ) {
183
+ uses_indebted_object = true ;
184
+ }
185
+
163
186
// For each object:
164
187
// - add the execution time of the current transaction to the tracker
165
188
// - subtract the maximum amount of time available for execution according
@@ -181,11 +204,9 @@ impl ExecutionTimeObserver {
181
204
utilization. excess_execution_time . saturating_sub (
182
205
utilization
183
206
. last_measured
184
- . map ( |last_measured| {
185
- now. duration_since ( last_measured)
186
- . mul_f64 ( self . protocol_params . target_utilization as f64 / 100.0 )
187
- } )
188
- . unwrap_or ( Duration :: MAX ) ,
207
+ . map ( |last_measured| now. duration_since ( last_measured) )
208
+ . unwrap_or ( Duration :: MAX )
209
+ . mul_f64 ( self . protocol_params . target_utilization as f64 / 100.0 ) ,
189
210
) ;
190
211
utilization. last_measured = Some ( now) ;
191
212
utilization. excess_execution_time
@@ -252,7 +273,7 @@ impl ExecutionTimeObserver {
252
273
>= self
253
274
. config
254
275
. observation_sharing_object_utilization_threshold ( ) ;
255
- if diff_exceeds_threshold && utilization_exceeds_threshold {
276
+ if diff_exceeds_threshold && ( utilization_exceeds_threshold || uses_indebted_object ) {
256
277
debug ! ( "sharing new execution time observation for {key:?}: {new_average:?}" ) ;
257
278
to_share. push ( ( key, new_average) ) ;
258
279
local_observation. last_shared = Some ( ( new_average, Instant :: now ( ) ) ) ;
@@ -297,6 +318,12 @@ impl ExecutionTimeObserver {
297
318
}
298
319
}
299
320
}
321
+
322
+ fn update_indebted_objects ( & mut self , mut object_debts : Vec < ObjectID > ) {
323
+ object_debts. sort_unstable ( ) ;
324
+ object_debts. dedup ( ) ;
325
+ self . indebted_objects = object_debts;
326
+ }
300
327
}
301
328
302
329
// Key used to save StoredExecutionTimeObservations in the Sui system state object's
0 commit comments