apache/cassandra · error · IllegalStateException

Maximum pool size has been changed while resizing

Error message

Maximum pool size has been changed while resizing

What it means

setMaximumPoolSize resizes the worker-permit pool with a compareAndSet on the current maximum; if another thread changed maximumPoolSize between the read and the CAS, the resize is abandoned and an IllegalStateException is thrown rather than silently overwriting the concurrent update. It is a lost-update detection mechanism for concurrent resizes.

Source

Thrown at src/java/org/apache/cassandra/concurrent/SEPExecutor.java:381

    public int getMaximumPoolSize()
    {
        return maximumPoolSize.get();
    }

    @Override
    public synchronized void setMaximumPoolSize(int newMaximumPoolSize)
    {
        final int oldMaximumPoolSize = maximumPoolSize.get();

        if (newMaximumPoolSize < 0)
        {
            throw new IllegalArgumentException("Maximum number of workers must not be negative");
        }

        int deltaWorkPermits = newMaximumPoolSize - oldMaximumPoolSize;
        if (!maximumPoolSize.compareAndSet(oldMaximumPoolSize, newMaximumPoolSize))
        {
            throw new IllegalStateException("Maximum pool size has been changed while resizing");
        }

        if (deltaWorkPermits == 0)
            return;

        permits.updateAndGet(cur -> updateWorkPermits(cur, workPermits(cur) + deltaWorkPermits));
        logger.info("Resized {} maximum pool size from {} to {}", name, oldMaximumPoolSize, newMaximumPoolSize);

        // If we we have more work permits than before we should spin up a worker now rather than waiting
        // until either a new task is enqueued (if all workers are descheduled) or a spinning worker calls
        // maybeSchedule().
        pool.maybeStartSpinningWorker();

        maximumPoolSizeListener.onUpdateMaximumPoolSize(newMaximumPoolSize);
    }

    private static int taskPermits(long both)
    {

View on GitHub (pinned to 88fd0f6a0e)

Solutions

  1. Retry the resize: re-read the new maximumPoolSize (getMaximumPoolSize) and call setMaximumPoolSize again until it succeeds
  2. Serialize resizes: perform all setMaximumPoolSize calls from a single thread or under a shared lock
  3. Check whether multiple components (JMX + config applier) are resizing the same executor and let only one own it
  4. Catch IllegalStateException around the call and retry with backoff if concurrent resizes are expected

Example fix

// before
executor.setMaximumPoolSize(newSize); // IllegalStateException on race
// after
boolean resized = false;
while (!resized)
{
    try { executor.setMaximumPoolSize(newSize); resized = true; }
    catch (IllegalStateException e) { Thread.yield(); }
}
Defensive patterns

Strategy: retry

Validate before calling

// detect concurrent resizers before attempting
if (resizeInProgress.compareAndSet(false, true))
    try { executor.setMaximumPoolSize(newSize); } finally { resizeInProgress.set(false); }

Try / catch

try
{
    executor.setMaximumPoolSize(newSize);
}
catch (IllegalStateException e)
{
    // someone else resized concurrently; re-read and retry
    newSize = executor.getMaximumPoolSize();
}

Prevention

When it happens

Trigger: Two threads (e.g. two JMX calls, an admin tool and a background resizer) calling setMaximumPoolSize on the same SEPExecutor concurrently; a retry loop that re-reads stale size and collides with another resize in flight.

Common situations: Operational tooling resizing pools concurrently during load; automated tuners racing with manual JMX changes; test code resizing pools from multiple threads.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of apache/cassandra@88fd0f6a0e (2026-09-10). Data as JSON: /api/errors/5ab333c73fb15370. Report an issue: GitHub.