apache/pulsar · warning · PulsarClientException

Commit interrupted

Error message

Commit interrupted

What it means

TransactionV5.commit() blocks on the underlying v4 transaction's commit future. If the waiting thread is interrupted while blocked, the interrupt flag is restored and a PulsarClientException with the fixed message "Commit interrupted" is thrown. The transaction's actual commit outcome is unknown at that point — only the wait was cancelled.

Source

Thrown at pulsar-client-v5/src/main/java/org/apache/pulsar/client/impl/v5/TransactionV5.java:51

    private final org.apache.pulsar.client.api.transaction.Transaction v4Transaction;
    private final AsyncTransaction asyncView;

    TransactionV5(org.apache.pulsar.client.api.transaction.Transaction v4Transaction) {
        this.v4Transaction = v4Transaction;
        this.asyncView = new AsyncView();
    }

    org.apache.pulsar.client.api.transaction.Transaction v4Transaction() {
        return v4Transaction;
    }

    @Override
    public void commit() throws PulsarClientException {
        try {
            v4Transaction.commit().get();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new PulsarClientException("Commit interrupted", e);
        } catch (ExecutionException e) {
            Throwable cause = e.getCause() != null ? e.getCause() : e;
            throw new PulsarClientException(cause.getMessage(), cause);
        }
    }

    @Override
    public void abort() throws PulsarClientException {
        try {
            v4Transaction.abort().get();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new PulsarClientException("Abort interrupted", e);
        } catch (ExecutionException e) {
            Throwable cause = e.getCause() != null ? e.getCause() : e;
            throw new PulsarClientException(cause.getMessage(), cause);
        }
    }

View on GitHub (pinned to 820761864e)

Solutions

  1. Do not interrupt threads waiting on commit(); await completion before shutdown, or use asyncView (async()) with a CompletableFuture handler instead of blocking commit().
  2. Check the transaction's final state after recovery — the commit may have succeeded despite the interrupt; abort/retry idempotently as needed.
  3. Ensure the thread's interrupt status handling is intentional: the library already restores the flag, so subsequent blocking calls will fail fast — re-create the transaction if needed.

Example fix

// before
try (TransactionV5 txn = ... ) {
    txn.commit(); // blocking; interrupted on shutdown
}
// after
AsyncTransaction txn = ...;
txn.commitAsync()
   .orTimeout(30, TimeUnit.SECONDS)
   .whenComplete((r, ex) -> {
       if (ex != null) log.warn("commit failed", ex);
   });
Defensive patterns

Strategy: try-catch

Validate before calling

if (Thread.currentThread().isInterrupted()) {
    throw new PulsarClientException("thread already interrupted; skipping commit");
}

Try / catch

try {
    txn.commit();
} catch (PulsarClientException e) {
    if ("Commit interrupted".equals(e.getMessage())) {
        // verify transaction state on coordinator before retrying
    }
}

Prevention

When it happens

Trigger: Calling commit() on a thread that gets interrupted while blocked in v4Transaction.commit().get() — e.g. executor shutdown, task cancellation, or another thread calling Thread.interrupt().

Common situations: Shutting down an application or executor while a transaction commit is pending; timeout-based task cancellation interrupting worker threads; request-handling threads interrupted by a web server during graceful shutdown.

Related errors


AI-assisted analysis of apache/pulsar@820761864e (2026-09-06). Data as JSON: /api/errors/00c50faec83f7363. Report an issue: GitHub.