Skip to content

Commit df6e8a7

Browse files
committed
Mark deposits/burns as failed when they exhaust the retry limit
1 parent 16f8fd0 commit df6e8a7

3 files changed

Lines changed: 62 additions & 13 deletions

File tree

linera-bridge/src/monitor/evm.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,11 @@ pub(crate) async fn process_pending_deposits<E: linera_core::environment::Enviro
116116
}
117117
}
118118

119-
monitor.write().await.mark_deposit_retried(&pending.key);
119+
monitor
120+
.write()
121+
.await
122+
.mark_deposit_retried(&pending.key, max_retries)
123+
.await;
120124
}
121125
}
122126

linera-bridge/src/monitor/linera.rs

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -105,11 +105,19 @@ pub(crate) async fn process_pending_burns<E: linera_core::environment::Environme
105105

106106
match estimate_fits(evm_client.estimate_add_block_gas(&cert).await) {
107107
Ok(true) => {
108-
submit_addblock(monitor, evm_client, &cert, height, &event_indices).await;
108+
submit_addblock(
109+
monitor,
110+
evm_client,
111+
&cert,
112+
height,
113+
&event_indices,
114+
max_retries,
115+
)
116+
.await;
109117
relay::update_balance_metrics(evm_client, linera_client).await;
110118
}
111119
Ok(false) => {
112-
submit_chunked(monitor, evm_client, &cert, height, &by_tx).await;
120+
submit_chunked(monitor, evm_client, &cert, height, &by_tx, max_retries).await;
113121
relay::update_balance_metrics(evm_client, linera_client).await;
114122
}
115123
Err(error) => {
@@ -130,6 +138,7 @@ async fn submit_addblock<P: Provider>(
130138
cert: &linera_chain::types::ConfirmedBlockCertificate,
131139
height: BlockHeight,
132140
event_indices: &[u32],
141+
max_retries: u32,
133142
) {
134143
match evm_client.forward_cert(cert).await {
135144
Ok(()) => {
@@ -143,7 +152,7 @@ async fn submit_addblock<P: Provider>(
143152
tracing::warn!(?height, ?error, "addBlock submission failed");
144153
let mut state = monitor.write().await;
145154
for ei in event_indices {
146-
state.mark_burn_retried(height, *ei);
155+
state.mark_burn_retried(height, *ei, max_retries).await;
147156
}
148157
}
149158
}
@@ -158,11 +167,21 @@ async fn submit_chunked<P: Provider>(
158167
cert: &linera_chain::types::ConfirmedBlockCertificate,
159168
height: BlockHeight,
160169
by_tx: &[(u32, Vec<u32>)],
170+
max_retries: u32,
161171
) {
162172
for (tx_index, positions) in by_tx {
163173
let (chunks, oversized) = split_to_fit(evm_client, cert, *tx_index, positions).await;
164174
mark_oversized_failed(monitor, height, *tx_index, &oversized).await;
165-
submit_chunks_with_retry(monitor, evm_client, cert, height, *tx_index, chunks).await;
175+
submit_chunks_with_retry(
176+
monitor,
177+
evm_client,
178+
cert,
179+
height,
180+
*tx_index,
181+
chunks,
182+
max_retries,
183+
)
184+
.await;
166185
}
167186
}
168187

@@ -246,6 +265,7 @@ async fn submit_chunks_with_retry<P: Provider>(
246265
height: BlockHeight,
247266
tx_index: u32,
248267
events_chunks: Vec<Vec<u32>>,
268+
max_retries: u32,
249269
) {
250270
for events_chunk in events_chunks {
251271
if let Err(error) = evm_client
@@ -267,7 +287,7 @@ async fn submit_chunks_with_retry<P: Provider>(
267287
};
268288
let mut state = monitor.write().await;
269289
for ei in to_bump {
270-
state.mark_burn_retried(height, ei);
290+
state.mark_burn_retried(height, ei, max_retries).await;
271291
}
272292
}
273293
}

linera-bridge/src/monitor/mod.rs

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -403,10 +403,20 @@ impl MonitorState {
403403
Ok(())
404404
}
405405

406-
pub fn mark_deposit_retried(&mut self, key: &DepositKey) {
407-
if let Some(d) = self.deposits.get_mut(key) {
406+
/// Bumps the deposit's retry counter; if the bump exhausts `max_retries`,
407+
/// the deposit is marked `failed` (moved to `finished_deposits` in
408+
/// SQLite) so it does not get loaded back as a pending item on the next
409+
/// relayer start.
410+
pub async fn mark_deposit_retried(&mut self, key: &DepositKey, max_retries: u32) {
411+
let exhausted = if let Some(d) = self.deposits.get_mut(key) {
408412
d.retry_count += 1;
409413
d.last_retry_at = Some(Instant::now());
414+
d.retry_count >= max_retries
415+
} else {
416+
false
417+
};
418+
if exhausted {
419+
self.mark_deposit_failed(key).await;
410420
}
411421
}
412422

@@ -440,10 +450,25 @@ impl MonitorState {
440450
})
441451
}
442452

443-
pub fn mark_burn_retried(&mut self, height: BlockHeight, event_index: u32) {
444-
if let Some(b) = self.burns.get_mut(&(height, event_index)) {
453+
/// Bumps the burn's retry counter; if the bump exhausts `max_retries`,
454+
/// the burn is marked `failed` (moved to `finished_burns` in SQLite) so
455+
/// it does not get loaded back as a pending item on the next relayer
456+
/// start.
457+
pub async fn mark_burn_retried(
458+
&mut self,
459+
height: BlockHeight,
460+
event_index: u32,
461+
max_retries: u32,
462+
) {
463+
let exhausted = if let Some(b) = self.burns.get_mut(&(height, event_index)) {
445464
b.retry_count += 1;
446465
b.last_retry_at = Some(Instant::now());
466+
b.retry_count >= max_retries
467+
} else {
468+
false
469+
};
470+
if exhausted {
471+
self.mark_burn_failed(height, event_index).await;
447472
}
448473
}
449474

@@ -703,7 +728,7 @@ mod tests {
703728

704729
assert_eq!(state.deposits_ready_for_retry(10).len(), 1);
705730

706-
state.mark_deposit_retried(&key);
731+
state.mark_deposit_retried(&key, 10).await;
707732
assert_eq!(state.deposits_ready_for_retry(10).len(), 0);
708733

709734
state.mark_deposit_failed(&key).await;
@@ -736,7 +761,7 @@ mod tests {
736761
let next = state.next_deposit_for_retry(10);
737762
assert!(matches!(next, Some(p) if p.key == key));
738763

739-
state.mark_deposit_retried(&key);
764+
state.mark_deposit_retried(&key, 10).await;
740765
assert!(state.next_deposit_for_retry(10).is_none());
741766

742767
state.complete_deposit(&key).await;
@@ -837,7 +862,7 @@ mod tests {
837862
.await;
838863

839864
assert!(state.next_burn_for_retry(10).is_some());
840-
state.mark_burn_retried(height, 0);
865+
state.mark_burn_retried(height, 0, 10).await;
841866
assert!(state.next_burn_for_retry(10).is_none());
842867

843868
// Once forwarded, the item is no longer offered for retry.

0 commit comments

Comments
 (0)