Skip to content

Delay shutdown when payout transaction broadcasts are pending#7593

Open
alvasw wants to merge 1 commit intobisq-network:masterfrom
alvasw:delay_shutdown_confirm_payment_received
Open

Delay shutdown when payout transaction broadcasts are pending#7593
alvasw wants to merge 1 commit intobisq-network:masterfrom
alvasw:delay_shutdown_confirm_payment_received

Conversation

@alvasw
Copy link
Contributor

@alvasw alvasw commented Feb 24, 2026

A support team member reported an issue where sellers confirm receipt of payment, but the payout transaction is not broadcasted because they close Bisq before the transaction is broadcasted.

To address this, we now check for pending payout transaction broadcasts during shutdown and delay the shutdown if necessary.

Summary by CodeRabbit

  • New Features

    • Shutdown now waits briefly (up to 2s) to ensure pending payment-received messages are published before the app stops, reducing risk of lost notifications during shutdown.
    • Release flow now tracks payment-received messages to ensure they are sent even on failures.
  • Tests

    • Added tests covering shutdown wait behavior and payment-message handling.

@coderabbitai
Copy link

coderabbitai bot commented Feb 24, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7b5628f and c819f4d.

📒 Files selected for processing (5)
  • core/src/main/java/bisq/core/app/BisqExecutable.java
  • core/src/main/java/bisq/core/app/ShutdownDelayer.java
  • core/src/test/java/bisq/core/app/ShutdownDelayerTests.java
  • desktop/src/main/java/bisq/desktop/app/BisqApp.java
  • desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java
🚧 Files skipped from review as they are similar to previous changes (4)
  • desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java
  • desktop/src/main/java/bisq/desktop/app/BisqApp.java
  • core/src/main/java/bisq/core/app/ShutdownDelayer.java
  • core/src/test/java/bisq/core/app/ShutdownDelayerTests.java

📝 Walkthrough

Walkthrough

Adds a ShutdownDelayer utility with a Clock abstraction to track and wait up to 2 seconds for pending payment-received messages during shutdown; integrates a BlockingClock at startup, reports pending messages from seller payment flow, and invokes the wait during application shutdown.

Changes

Cohort / File(s) Summary
Core ShutdownDelayer Implementation
core/src/main/java/bisq/core/app/ShutdownDelayer.java
New public class providing Clock interface, BlockingClock, atomic pending-message counter, API to report messages, and deadline-based wait (2s) for pending messages.
Core App Launch Initialization
core/src/main/java/bisq/core/app/BisqExecutable.java
Sets ShutdownDelayer clock to BlockingClock during application startup.
Desktop App Shutdown Hook
desktop/src/main/java/bisq/desktop/app/BisqApp.java
Calls ShutdownDelayer.maybeWaitForPendingMessagesToBePublished() in the user-initiated shutdown flow before stopping the app.
Seller Payment Flow Integration
desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java
Reports pending payment messages before release and wires success/failure paths to report message sent so pending count is decremented.
Tests
core/src/test/java/bisq/core/app/ShutdownDelayerTests.java
New tests with FakeClock validating no-wait when no pending messages and delay behavior when messages are pending.

Sequence Diagram

sequenceDiagram
    participant Seller as SellerStep3View
    participant ShutDown as ShutdownDelayer
    participant App as BisqApp
    participant Clock as BlockingClock

    rect rgba(100,150,200,0.5)
    Note over Seller,ShutDown: Payment release flow
    Seller->>ShutDown: reportPendingPaymentReceivedMessage()
    ShutDown->>ShutDown: pending++ 
    Seller->>App: onFiatPaymentReceived(callback -> reportPaymentReceivedMessageSent)
    App-->>Seller: async completion
    Seller->>ShutDown: reportPaymentReceivedMessageSent()
    ShutDown->>ShutDown: pending--
    end

    rect rgba(200,100,100,0.5)
    Note over App,Clock: Shutdown sequence
    App->>ShutDown: maybeWaitForPendingMessagesToBePublished()
    ShutDown->>Clock: getTimeInMillis()
    Clock-->>ShutDown: currentTime
    loop while pending > 0 and before deadline
        ShutDown->>Clock: waitForMillis(200)
        Clock->>Clock: sleep(200)
    end
    ShutDown-->>App: wait complete (or timeout)
    App->>App: continue shutdown
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 A tick, a count, a gentle pause,
I guard the hops for worthy cause,
Two seconds more to see them through,
Then close the burrow — safe and true,
Hoppity-handshake: messages flew! 🥕

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 6.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Description check ❓ Inconclusive The pull request description provides the context and motivation for the change, explaining the support issue and the solution. However, it does not follow the required template structure with sections like issue references and detailed explanation. Consider following the template more closely by adding issue references (Fixes #...) and organizing the description into clearer sections for better consistency with repository standards.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title clearly and specifically describes the main change: delaying shutdown when payout transaction broadcasts are pending, which aligns with the primary objective of the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@alvasw alvasw marked this pull request as draft February 24, 2026 19:17
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
core/src/main/java/bisq/core/app/ShutdownDelayer.java (2)

42-44: Guard against pending counter underflow.

If reportPaymentReceivedMessageSent() is called more times than the pending report, the counter can go negative and skip waits. Clamping to zero avoids this drift.

Suggested change
     public static void reportPaymentReceivedMessageSent() {
-        pendingPaymentReceivedMessages.decrementAndGet();
+        pendingPaymentReceivedMessages.updateAndGet(value -> Math.max(0, value - 1));
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/src/main/java/bisq/core/app/ShutdownDelayer.java` around lines 42 - 44,
reportPaymentReceivedMessageSent currently decrements
pendingPaymentReceivedMessages unconditionally which can drive the counter
negative; change the implementation in
ShutdownDelayer.reportPaymentReceivedMessageSent to clamp at zero instead of
allowing underflow by atomically updating pendingPaymentReceivedMessages (e.g.,
using getAndUpdate/updateAndGet or a CAS loop) so the counter only decrements
when > 0 and remains 0 otherwise, preserving thread safety and preventing
negative counts.

35-36: Provide a default Clock to avoid null usage.

If maybeWaitForPendingMessagesToBePublished() is called before a clock is injected, clock.getTimeInMillis() will NPE. A defensive default keeps behavior unchanged while making the utility safer.

Suggested change
-    `@Setter`
-    private static Clock clock;
+    `@Setter`
+    private static Clock clock = new BlockingClock();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/src/main/java/bisq/core/app/ShutdownDelayer.java` around lines 35 - 36,
The static Clock field in ShutdownDelayer can be null and cause an NPE when
maybeWaitForPendingMessagesToBePublished() calls clock.getTimeInMillis();
initialize the static field with a sensible default clock instance (the
project's default/system clock implementation) so clock is never null, and keep
the `@Setter` to allow injection; update ShutdownDelayer's private static Clock
clock declaration to assign that default (ensuring behavior and tests remain
unchanged).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@core/src/test/java/bisq/core/app/ShutdownDelayerTests.java`:
- Around line 40-56: The test increments ShutdownDelayer's static pending
counter and doesn't reset it, causing order-dependent flakiness; modify
delayTest to restore isolation by resetting the counter before and/or after the
test: add a call to a reset method on ShutdownDelayer (e.g.
ShutdownDelayer.resetPendingCount()) in delayTest (or create that method if it
doesn't exist) which sets the internal static pending counter to 0, ensuring
reportPendingPaymentReceivedMessage() calls in delayTest don't affect
noDelayTest.

---

Nitpick comments:
In `@core/src/main/java/bisq/core/app/ShutdownDelayer.java`:
- Around line 42-44: reportPaymentReceivedMessageSent currently decrements
pendingPaymentReceivedMessages unconditionally which can drive the counter
negative; change the implementation in
ShutdownDelayer.reportPaymentReceivedMessageSent to clamp at zero instead of
allowing underflow by atomically updating pendingPaymentReceivedMessages (e.g.,
using getAndUpdate/updateAndGet or a CAS loop) so the counter only decrements
when > 0 and remains 0 otherwise, preserving thread safety and preventing
negative counts.
- Around line 35-36: The static Clock field in ShutdownDelayer can be null and
cause an NPE when maybeWaitForPendingMessagesToBePublished() calls
clock.getTimeInMillis(); initialize the static field with a sensible default
clock instance (the project's default/system clock implementation) so clock is
never null, and keep the `@Setter` to allow injection; update ShutdownDelayer's
private static Clock clock declaration to assign that default (ensuring behavior
and tests remain unchanged).

ℹ️ Review info

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 80f5ed8 and 7b5628f.

📒 Files selected for processing (5)
  • core/src/main/java/bisq/core/app/BisqExecutable.java
  • core/src/main/java/bisq/core/app/ShutdownDelayer.java
  • core/src/test/java/bisq/core/app/ShutdownDelayerTests.java
  • desktop/src/main/java/bisq/desktop/app/BisqApp.java
  • desktop/src/main/java/bisq/desktop/main/portfolio/pendingtrades/steps/seller/SellerStep3View.java

A support team member reported an issue where sellers confirm receipt of
payment, but the payout transaction is not broadcasted because they
close Bisq before the transaction is broadcasted.

To address this, we now check for pending payout transaction broadcasts
during shutdown and delay the shutdown if necessary.
@alvasw alvasw force-pushed the delay_shutdown_confirm_payment_received branch from 7b5628f to c819f4d Compare February 27, 2026 13:21
@alvasw alvasw marked this pull request as ready for review February 28, 2026 16:52
Copy link
Collaborator

@HenrikJannsen HenrikJannsen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

utACK

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants