11use std:: collections:: BTreeMap ;
2+ use std:: time:: Instant ;
23
34use malachitebft_core_types:: { Context , Height } ;
45use malachitebft_peer:: PeerId ;
3940 pub sync_height : Ctx :: Height ,
4041
4142 /// Decided value requests for these heights have been sent out to peers.
42- pub pending_value_requests : BTreeMap < Ctx :: Height , ( OutboundRequestId , RequestState ) > ,
43+ /// Tuple contains: (request_id, state, timestamp_when_sent)
44+ pub pending_value_requests : BTreeMap < Ctx :: Height , ( OutboundRequestId , RequestState , Instant ) > ,
4345
4446 /// Maps request ID to height for pending decided value requests.
4547 pub height_per_request_id : BTreeMap < OutboundRequestId , Ctx :: Height > ,
@@ -133,14 +135,14 @@ where
133135 . insert ( request_id. clone ( ) , height) ;
134136
135137 self . pending_value_requests
136- . insert ( height, ( request_id, RequestState :: WaitingResponse ) ) ;
138+ . insert ( height, ( request_id, RequestState :: WaitingResponse , Instant :: now ( ) ) ) ;
137139 }
138140
139141 /// Mark that a response has been received for a height.
140142 ///
141143 /// State transition: WaitingResponse -> WaitingValidation
142144 pub fn response_received ( & mut self , request_id : OutboundRequestId , height : Ctx :: Height ) {
143- if let Some ( ( req_id, state) ) = self . pending_value_requests . get_mut ( & height) {
145+ if let Some ( ( req_id, state, _ ) ) = self . pending_value_requests . get_mut ( & height) {
144146 if req_id != & request_id {
145147 return ; // A new request has been made in the meantime, ignore this response.
146148 }
@@ -155,7 +157,7 @@ where
155157 /// State transition: WaitingValidation -> Validated
156158 /// It is also possible to have the following transition: WaitingResponse -> Validated.
157159 pub fn validate_response ( & mut self , height : Ctx :: Height ) {
158- if let Some ( ( _, state) ) = self . pending_value_requests . get_mut ( & height) {
160+ if let Some ( ( _, state, _ ) ) = self . pending_value_requests . get_mut ( & height) {
159161 * state = RequestState :: Validated ;
160162 }
161163 }
@@ -167,7 +169,7 @@ where
167169
168170 /// Remove the pending decided value request for a given height.
169171 pub fn remove_pending_request_by_height ( & mut self , height : & Ctx :: Height ) {
170- if let Some ( ( request_id, _) ) = self . pending_value_requests . remove ( height) {
172+ if let Some ( ( request_id, _, _ ) ) = self . pending_value_requests . remove ( height) {
171173 self . height_per_request_id . remove ( & request_id) ;
172174 }
173175 }
@@ -191,7 +193,7 @@ where
191193
192194 /// Check if a pending decided value request for a given height is in the `Validated` state.
193195 pub fn is_pending_value_request_validated_by_height ( & self , height : & Ctx :: Height ) -> bool {
194- if let Some ( ( _, state) ) = self . pending_value_requests . get ( height) {
196+ if let Some ( ( _, state, _ ) ) = self . pending_value_requests . get ( height) {
195197 * state == RequestState :: Validated
196198 } else {
197199 false
@@ -207,6 +209,32 @@ where
207209 }
208210 }
209211
212+ /// Clear stale pending requests that have been waiting longer than the configured timeout.
213+ /// Returns the number of cleared requests.
214+ pub fn clear_stale_pending_requests ( & mut self ) -> usize {
215+ let timeout = self . config . request_timeout ;
216+ let now = Instant :: now ( ) ;
217+
218+ // Find heights with stale requests (not yet validated and older than timeout)
219+ let stale_heights: Vec < Ctx :: Height > = self
220+ . pending_value_requests
221+ . iter ( )
222+ . filter ( |( _, ( _, state, sent_at) ) | {
223+ * state != RequestState :: Validated && now. duration_since ( * sent_at) > timeout
224+ } )
225+ . map ( |( height, _) | * height)
226+ . collect ( ) ;
227+
228+ let count = stale_heights. len ( ) ;
229+
230+ // Remove stale requests
231+ for height in stale_heights {
232+ self . remove_pending_request_by_height ( & height) ;
233+ }
234+
235+ count
236+ }
237+
210238 /// Find the earliest height that any peer can serve, above the given height.
211239 /// Returns the height and a peer that can serve it.
212240 /// This is used when the requested height is not available from any peer.
0 commit comments