@@ -2824,23 +2824,28 @@ func (s *SQLQueueIntegrationSuite) TestCrashAfterRetryLimitDoesNotLoseMessages()
28242824 require .NoError (t , deliveries ["msg-A" ].Ack (s .ctx ))
28252825 t .Logf ("Acked msg-A" )
28262826
2827- // Nack B — eligible for redelivery after its retry backoff.
2827+ // Keep C in flight while B exhausts its retry budget.
2828+ heldVisibilityMs := subConfig .LeaseDurationMs + subConfig .VisibilityTimeoutMs
2829+ require .NoError (t , deliveries ["msg-C" ].ExtendVisibilityTimeout (s .ctx , heldVisibilityMs ))
2830+
28282831 require .NoError (t , deliveries ["msg-B" ].Nack (s .ctx , failure .New ("msg-B failed" )))
2829- t .Logf ("Nacked msg-B, waiting for retry-limit to trigger auto-DLQ" )
2832+ retryDelivery := receive (t , deliveryChan1 )
2833+ require .Equal (t , "msg-B" , retryDelivery .Message ().ID )
2834+ require .Equal (t , 2 , retryDelivery .Attempt ())
28302835
2831- // Do NOT ack msg-C — simulating in-flight at crash time.
2836+ dlqTopic := topic + subConfig .DLQ .TopicSuffix
2837+ dlqConfig := testSubConfig ("worker-1" , "crash-retry-dlq-cg" )
2838+ dlqDeliveryChan , err := q1 .Subscriber ().Subscribe (s .ctx , dlqTopic , dlqConfig )
2839+ require .NoError (t , err )
28322840
2833- // Wait for msg-B to be redelivered and auto-DLQ'd by the poll loop.
2834- // The poll loop picks up the nacked msg-B, sees retry_count >= MaxAttempts, moves it to DLQ.
2835- // We just need to wait long enough for that to happen before crashing.
2836- // A short sleep is acceptable here as we're waiting for the subscriber's
2837- // internal processing, not for a test condition. But let's use receive
2838- // to see if B comes back (it shouldn't, since auto-DLQ handles it internally).
2841+ const expireVisibilityMs = int64 (1 )
2842+ require .NoError (t , retryDelivery .ExtendVisibilityTimeout (s .ctx , expireVisibilityMs ))
2843+ dlqDelivery := receive (t , dlqDeliveryChan )
2844+ require .Equal (t , "msg-B" , dlqDelivery .Message ().ID )
2845+ require .NoError (t , dlqDelivery .Ack (s .ctx ))
28392846
2840- // Give the poll loop time to process the nack and auto-DLQ msg-B
2841- // We can't use event-driven wait here because auto-DLQ happens inside pollAndDeliver
2842- // without delivering to the channel. A brief pause lets the poll loop run.
2843- // Simulate crash
2847+ // Crash with C still unacked, but make it immediately recoverable once the lease expires.
2848+ require .NoError (t , deliveries ["msg-C" ].ExtendVisibilityTimeout (s .ctx , expireVisibilityMs ))
28442849 q1 .Close ()
28452850 t .Logf ("Worker-1 crashed (queue closed)" )
28462851
0 commit comments