redisson/redisson · error · InvalidDataAccessResourceUsageException

Clustered rename is not supported in a pipeline

Error message

Clustered rename is not supported in a pipeline

What it means

In cluster mode, RENAME works only when source and destination hash to the same slot. Redisson implements cross-slot rename manually (DUMP the value, read TTL, RESTORE at the new key, DEL the old) — a multi-step, non-atomic sequence that cannot be buffered in a pipeline. Therefore rename() inside an open pipeline throws InvalidDataAccessResourceUsageException.

Source

Thrown at redisson-spring/redisson-spring-data/redisson-spring-data-24/src/main/java/org/redisson/spring/data/connection/RedissonClusterConnection.java:433

                
                RFuture<ListScanResult<byte[]>> f = executorService.readAsync(client, ByteArrayCodec.INSTANCE, RedisCommands.SCAN, args.toArray());
                ListScanResult<byte[]> res = syncFuture(f);
                String pos = res.getPos();
                client = res.getRedisClient();
                if ("0".equals(pos)) {
                    client = null;
                }
                
                return new ScanIteration<byte[]>(Long.parseUnsignedLong(pos), res.getValues());
            }
        }.open();
    }

    @Override
    public void rename(byte[] oldName, byte[] newName) {

        if (isPipelined()) {
            throw new InvalidDataAccessResourceUsageException("Clustered rename is not supported in a pipeline");
        }

        if (executorService.getConnectionManager().calcSlot(oldName) == executorService.getConnectionManager().calcSlot(newName)) {
            super.rename(oldName, newName);
            return;
        }

        final byte[] value = dump(oldName);

        if (null != value) {

            final Long sourceTtlInSeconds = ttl(oldName);

            final long ttlInMilliseconds;
            if (null != sourceTtlInSeconds && sourceTtlInSeconds > 0) {
                ttlInMilliseconds = sourceTtlInSeconds * 1000;
            } else {
                ttlInMilliseconds = 0;

View on GitHub (pinned to 91188987c2)

Solutions

  1. Exclude rename/renameNX from pipelined blocks: close the pipeline, perform the rename, then continue batching
  2. If source and destination are known same-slot (e.g. hash tags like {user:1}:a -> {user:1}:b), call the underlying single-command rename outside the pipeline — it is then a single RENAME
  3. Design keys with matching hash tags so renames stay single-slot and atomic

Example fix

// before
connection.openPipeline();
connection.rename(oldKey, newKey); // throws
connection.closePipeline();

// after
connection.closePipeline();
connection.rename(oldKey, newKey);
connection.openPipeline(); // resume batching other commands
Defensive patterns

Strategy: validation

Validate before calling

// Guard before pipelining anything that may rename:
if (connection.isPipelined()) {
    connection.closePipeline(); // or use a separate connection for the rename
}
connection.rename(oldKey, newKey);

Try / catch

try {
    connection.rename(oldKey, newName);
} catch (InvalidDataAccessResourceUsageException e) {
    // rename inside pipeline: close pipeline, retry outside, reopen if needed
    connection.closePipeline();
    connection.rename(oldKey, newName);
}

Prevention

When it happens

Trigger: Calling connection.rename(oldName, newName) while isPipelined() is true (after openPipeline()). Same-slot renames would delegate to super.rename(), but the pipeline check runs first, so ANY rename in a pipeline throws, regardless of slot.

Common situations: Generic Spring Redis DAOs that wrap all mutations in executePipelined(); code ported from a non-cluster or Lettuce setup where pipelined rename worked; batch rename loops that were 'optimized' with pipelining when moving to cluster.

Related errors


AI-assisted analysis of redisson/redisson@91188987c2 (2026-08-14). Data as JSON: /api/errors/ee7cac55ab7affbe. Report an issue: GitHub.