From 2a534c45db127a472be336efacea40413d47b9d5 Mon Sep 17 00:00:00 2001 From: amackillop Date: Tue, 20 Jan 2026 13:25:35 -0800 Subject: [PATCH] Deduplicate PaymentReceived events on restart PaymentClaimed events can be replayed on every Node startup. Since nodes are pinged at minimum every 30 minutes, this causes duplicate PaymentReceived events to stack up in the queue when the event queue is not being actively processed. These stacked events can cause payouts to fail as they timeout before the queue is drained to the actual events related to a payout. Add a check before queuing PaymentReceived to skip if an event for that payment_id already exists in the queue, preventing duplicates. --- src/event.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/event.rs b/src/event.rs index 70e02207e..8d475d20e 100644 --- a/src/event.rs +++ b/src/event.rs @@ -358,6 +358,14 @@ where Ok(()) } + /// Check if a PaymentReceived event with the given payment_id already exists in the queue. + pub(crate) fn contains_payment_received(&self, payment_id: &PaymentId) -> bool { + let locked_queue = self.queue.lock().unwrap(); + locked_queue.iter().any(|event| { + matches!(event, Event::PaymentReceived { payment_id: Some(id), .. } if id == payment_id) + }) + } + fn persist_queue(&self, locked_queue: &VecDeque) -> Result<(), Error> { let data = EventQueueSerWrapper(locked_queue).encode(); self.kv_store @@ -938,6 +946,18 @@ where }, } + // Check if a PaymentReceived event for this payment already exists in the queue. + // This handles the case where PaymentClaimed is replayed on restart - we only + // want to queue one PaymentReceived event per payment. + if self.event_queue.contains_payment_received(&payment_id) { + log_debug!( + self.logger, + "Skipping duplicate PaymentReceived for payment {}: event already queued", + payment_id, + ); + return Ok(()); + } + let event = Event::PaymentReceived { payment_id: Some(payment_id), payment_hash,