apache/pulsar · warning · BookieException.MetadataStoreException

Interrupted writing cookie for bookie ${bookieId}

Error message

Interrupted writing cookie for bookie ${bookieId}

What it means

writeCookie performs a blocking store.put(path, data, expectedVersion).get(timeout) on the asynchronous MetadataStore. If the waiting thread is interrupted, the manager restores the interrupt flag and rethrows the operation as BookieException.MetadataStoreException with the message 'Interrupted writing cookie for bookie <bookieId>'. It signals that the thread was asked to stop (shutdown, cancellation) rather than that the store rejected the write, so the cookie write's outcome is indeterminate.

Source

Thrown at pulsar-metadata/src/main/java/org/apache/pulsar/metadata/bookkeeper/PulsarRegistrationManager.java:241

    public void writeCookie(BookieId bookieId, Versioned<byte[]> cookieData) throws BookieException {
        String path = this.cookiePath + "/" + bookieId;
        try {
            long version;
            if (Version.NEW == cookieData.getVersion()) {
                version = -1L;
            } else {
                if (!(cookieData.getVersion() instanceof LongVersion)) {
                    throw new BookieException.BookieIllegalOpException(
                            "Invalid version type, expected it to be LongVersion");
                }
                version = ((LongVersion) cookieData.getVersion()).getLongVersion();
            }

            store.put(path, cookieData.getValue(), Optional.of(version))
                    .get(BLOCKING_CALL_TIMEOUT, MILLISECONDS);
        } catch (InterruptedException ie) {
            Thread.currentThread().interrupt();
            throw new BookieException.MetadataStoreException("Interrupted writing cookie for bookie " + bookieId, ie);
        } catch (ExecutionException e) {
            if (e.getCause() instanceof MetadataStoreException.BadVersionException) {
                throw new BookieException.CookieExistException(bookieId.toString());
            } else {
                throw new BookieException.MetadataStoreException("Failed to write cookie for bookie " + bookieId);
            }
        } catch (TimeoutException ex) {
            throw new BookieException.MetadataStoreException("Failed to write cookie for bookie " + bookieId, ex);
        }
    }

    @Override
    public Versioned<byte[]> readCookie(BookieId bookieId) throws BookieException {
        String path = this.cookiePath + "/" + bookieId;
        try {
            Optional<GetResult> res = store.get(path).get(BLOCKING_CALL_TIMEOUT, MILLISECONDS);
            if (!res.isPresent()) {
                throw new BookieException.CookieNotFoundException(bookieId.toString());

View on GitHub (pinned to 820761864e)

Solutions

  1. Treat it as a shutdown signal: let the BookieException propagate and complete the interrupted shutdown cleanly; do not swallow or loop-retry.
  2. Check the interrupt status and stop the calling task rather than restarting cookie writes immediately.
  3. If interruptions recur during normal operation, audit which component interrupts the bookie threads (executor shutdownNow, cancellation) and fix the lifecycle ordering so cookie registration finishes before shutdown.
  4. After the restart completes, verify the cookie exists (readCookie) since the interrupted write may or may not have landed.

Example fix

// before
try {
    regManager.writeCookie(bookieId, cookie);
} catch (BookieException e) {
    log.warn("retrying", e); // swallows interrupt, retries
    regManager.writeCookie(bookieId, cookie);
}

// after
try {
    regManager.writeCookie(bookieId, cookie);
} catch (BookieException.MetadataStoreException e) {
    if (Thread.currentThread().isInterrupted()) {
        throw e; // shutdown in progress, do not retry
    }
    // handle genuine store failure
}
Defensive patterns

Strategy: try-catch

Try / catch

try {
    regManager.writeCookie(bookieId, cookie);
} catch (BookieException.MetadataStoreException e) {
    if (Thread.currentThread().isInterrupted()) {
        // shutdown path: propagate and stop, do not retry
        throw e;
    }
    throw e;
}

Prevention

When it happens

Trigger: The thread blocked in writeCookie's .get(BLOCKING_CALL_TIMEOUT, MILLISECONDS) receives Thread.interrupt() — typically during bookie shutdown while writing/rewriting its cookie, or when an enclosing scheduler/executor cancels the registration task mid-write.

Common situations: Bookie process shutdown or failover while cookie registration is still in flight; a watchdog or test harness interrupting a hung metadata store (e.g. etcd/ZooKeeper connection stall) and thus unblocking the future wait; executor shutDownNow() cancelling startup threads.

Related errors


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