/** * @param keys must not be {@literal null}. * @return * @since 2.0 */ public static boolean isSameSlotForAllKeys(ByteBuffer... keys) { Assert.notNull(keys, "Keys must not be null!"); return isSameSlotForAllKeys(Arrays.asList(keys)); }
@Override public byte[] bRPopLPush(int timeout, byte[] srcKey, byte[] dstKey) { Assert.notNull(srcKey, "Source key must not be null!"); Assert.notNull(dstKey, "Destination key must not be null!"); if (ClusterSlotHashUtil.isSameSlotForAllKeys(srcKey, dstKey)) { return super.bRPopLPush(timeout, srcKey, dstKey); } List<byte[]> val = bRPop(timeout, srcKey); if (!CollectionUtils.isEmpty(val)) { lPush(dstKey, val.get(1)); return val.get(1); } return null; } }
@Override public byte[] rPopLPush(byte[] srcKey, byte[] dstKey) { Assert.notNull(srcKey, "Source key must not be null!"); Assert.notNull(dstKey, "Destination key must not be null!"); if (ClusterSlotHashUtil.isSameSlotForAllKeys(srcKey, dstKey)) { return super.rPopLPush(srcKey, dstKey); } byte[] val = rPop(srcKey); lPush(dstKey, val); return val; }
@Override public Long sInterStore(byte[] destKey, byte[]... keys) { Assert.notNull(destKey, "Destination key must not be null!"); Assert.notNull(keys, "Source keys must not be null!"); Assert.noNullElements(keys, "Source keys must not contain null elements!"); byte[][] allKeys = ByteUtils.mergeArrays(destKey, keys); if (ClusterSlotHashUtil.isSameSlotForAllKeys(allKeys)) { return super.sInterStore(destKey, keys); } Set<byte[]> result = sInter(keys); if (result.isEmpty()) { return 0L; } return sAdd(destKey, result.toArray(new byte[result.size()][])); }
@Override public void rename(byte[] sourceKey, byte[] targetKey) { Assert.notNull(sourceKey, "Source key must not be null!"); Assert.notNull(targetKey, "Target key must not be null!"); if (ClusterSlotHashUtil.isSameSlotForAllKeys(sourceKey, targetKey)) { super.rename(sourceKey, targetKey); return; } byte[] value = dump(sourceKey); if (value != null && value.length > 0) { restore(targetKey, 0, value); del(sourceKey); } }
@Override public Flux<CommandResponse<SUnionCommand, Flux<ByteBuffer>>> sUnion(Publisher<SUnionCommand> commands) { return getConnection().execute(cmd -> Flux.from(commands).concatMap(command -> { Assert.notNull(command.getKeys(), "Keys must not be null!"); if (ClusterSlotHashUtil.isSameSlotForAllKeys(command.getKeys())) { return super.sUnion(Mono.just(command)); } Flux<ByteBuffer> result = Flux.merge(command.getKeys().stream().map(cmd::smembers).collect(Collectors.toList())) .distinct(); return Mono.just(new CommandResponse<>(command, result)); })); }
@Override public Long pfCount(byte[]... keys) { if (ClusterSlotHashUtil.isSameSlotForAllKeys(keys)) { try { return super.pfCount(keys); } catch (Exception ex) { throw convertLettuceAccessException(ex); } } throw new InvalidDataAccessApiUsageException("All keys must map to same slot for pfcount in cluster mode."); }
@Override public Flux<BooleanResponse<PfMergeCommand>> pfMerge(Publisher<PfMergeCommand> commands) { return getConnection().execute(cmd -> Flux.from(commands).concatMap(command -> { Assert.notNull(command.getKey(), "Key must not be null for PFMERGE"); Assert.notEmpty(command.getSourceKeys(), "Source keys must not be null or empty for PFMERGE!"); List<ByteBuffer> keys = new ArrayList<>(command.getSourceKeys()); keys.add(command.getKey()); if (ClusterSlotHashUtil.isSameSlotForAllKeys(keys.toArray(new ByteBuffer[keys.size()]))) { return super.pfMerge(Mono.just(command)); } return Mono .error(new InvalidDataAccessApiUsageException("All keys must map to same slot for PFMERGE in cluster mode.")); })); }
@Override public List<byte[]> mGet(byte[]... keys) { Assert.notNull(keys, "Keys must not be null!"); Assert.noNullElements(keys, "Keys must not contain null elements!"); if (ClusterSlotHashUtil.isSameSlotForAllKeys(keys)) { return connection.getCluster().mget(keys); } return connection.getClusterCommandExecutor() .executeMultiKeyCommand((JedisMultiKeyClusterCommandCallback<byte[]>) BinaryJedis::get, Arrays.asList(keys)) .resultsAsListSortBy(keys); }
@Override public Long zInterStore(byte[] destKey, byte[]... sets) { Assert.notNull(destKey, "Destination key must not be null!"); Assert.notNull(sets, "Source sets must not be null!"); Assert.noNullElements(sets, "Source sets must not contain null elements!"); byte[][] allKeys = ByteUtils.mergeArrays(destKey, sets); if (ClusterSlotHashUtil.isSameSlotForAllKeys(allKeys)) { try { return connection.getCluster().zinterstore(destKey, sets); } catch (Exception ex) { throw convertJedisAccessException(ex); } } throw new InvalidDataAccessApiUsageException("ZINTERSTORE can only be executed when all keys map to the same slot"); }
@Override public Long bitOp(BitOperation op, byte[] destination, byte[]... keys) { Assert.notNull(op, "BitOperation must not be null!"); Assert.notNull(destination, "Destination key must not be null!"); byte[][] allKeys = ByteUtils.mergeArrays(destination, keys); if (ClusterSlotHashUtil.isSameSlotForAllKeys(allKeys)) { try { return connection.getCluster().bitop(JedisConverters.toBitOp(op), destination, keys); } catch (Exception ex) { throw convertJedisAccessException(ex); } } throw new InvalidDataAccessApiUsageException("BITOP is only supported for same slot keys in cluster mode."); }
@Override public Long pfCount(byte[]... keys) { Assert.notEmpty(keys, "PFCOUNT requires at least one non 'null' key."); Assert.noNullElements(keys, "Keys for PFCOUNT must not contain 'null'."); if (ClusterSlotHashUtil.isSameSlotForAllKeys(keys)) { try { return connection.getCluster().pfcount(keys); } catch (Exception ex) { throw convertJedisAccessException(ex); } } throw new InvalidDataAccessApiUsageException("All keys must map to same slot for pfcount in cluster mode."); }
@Override public Flux<ReactiveRedisConnection.NumericResponse<BitOpCommand, Long>> bitOp(Publisher<BitOpCommand> commands) { return getConnection().execute(cmd -> Flux.from(commands).concatMap(command -> { List<ByteBuffer> keys = new ArrayList<>(command.getKeys()); keys.add(command.getDestinationKey()); if (ClusterSlotHashUtil.isSameSlotForAllKeys(keys)) { return super.bitOp(Mono.just(command)); } return Mono .error(new InvalidDataAccessApiUsageException("All keys must map to the same slot for BITOP command.")); })); }
@Override public Flux<NumericResponse<PfCountCommand, Long>> pfCount(Publisher<PfCountCommand> commands) { return getConnection().execute(cmd -> Flux.from(commands).concatMap(command -> { Assert.notEmpty(command.getKeys(), "Keys must be null or empty for PFCOUNT!"); if (ClusterSlotHashUtil .isSameSlotForAllKeys(command.getKeys().toArray(new ByteBuffer[command.getKeys().size()]))) { return super.pfCount(Mono.just(command)); } return Mono .error(new InvalidDataAccessApiUsageException("All keys must map to same slot for PFCOUNT in cluster mode.")); })); } }
@Override public Flux<ReactiveRedisConnection.BooleanResponse<MSetCommand>> mSetNX(Publisher<MSetCommand> commands) { return getConnection().execute(cmd -> Flux.from(commands).concatMap(command -> { if (ClusterSlotHashUtil.isSameSlotForAllKeys(command.getKeyValuePairs().keySet())) { return super.mSetNX(Mono.just(command)); } return Mono .error(new InvalidDataAccessApiUsageException("All keys must map to the same slot for MSETNX command.")); })); } }
@Override public void pfMerge(byte[] destinationKey, byte[]... sourceKeys) { byte[][] allKeys = ByteUtils.mergeArrays(destinationKey, sourceKeys); if (ClusterSlotHashUtil.isSameSlotForAllKeys(allKeys)) { try { super.pfMerge(destinationKey, sourceKeys); return; } catch (Exception ex) { throw convertLettuceAccessException(ex); } } throw new InvalidDataAccessApiUsageException("All keys must map to same slot for pfmerge in cluster mode."); } }
@Override public Flux<PopResponse> bPop(Publisher<BPopCommand> commands) { return getConnection().execute(cmd -> Flux.from(commands).concatMap(command -> { Assert.notNull(command.getKeys(), "Keys must not be null!"); Assert.notNull(command.getDirection(), "Direction must not be null!"); if (ClusterSlotHashUtil.isSameSlotForAllKeys(command.getKeys())) { return super.bPop(Mono.just(command)); } return Mono.error(new InvalidDataAccessApiUsageException("All keys must map to the same slot for BPOP command.")); })); }
@Override public Flux<ByteBufferResponse<RPopLPushCommand>> rPopLPush(Publisher<RPopLPushCommand> commands) { return getConnection().execute(cmd -> Flux.from(commands).concatMap(command -> { Assert.notNull(command.getKey(), "Key must not be null!"); Assert.notNull(command.getDestination(), "Destination key must not be null!"); if (ClusterSlotHashUtil.isSameSlotForAllKeys(command.getKey(), command.getDestination())) { return super.rPopLPush(Mono.just(command)); } Mono<ByteBuffer> result = cmd.rpop(command.getKey()) .flatMap(value -> cmd.lpush(command.getDestination(), value).map(x -> value)); return result.map(value -> new ByteBufferResponse<>(command, value)); })); } }
@Override public Flux<NumericResponse<ZUnionStoreCommand, Long>> zUnionStore(Publisher<ZUnionStoreCommand> commands) { return getConnection().execute(cmd -> Flux.from(commands).concatMap(command -> { Assert.notEmpty(command.getSourceKeys(), "Source keys must not be null or empty."); if (ClusterSlotHashUtil.isSameSlotForAllKeys(command.getSourceKeys())) { return super.zUnionStore(Mono.just(command)); } return Mono .error(new InvalidDataAccessApiUsageException("All keys must map to the same slot for ZUNIONSTORE command.")); })); }
@Override public Flux<NumericResponse<ZInterStoreCommand, Long>> zInterStore(Publisher<ZInterStoreCommand> commands) { return getConnection().execute(cmd -> Flux.from(commands).concatMap(command -> { Assert.notEmpty(command.getSourceKeys(), "Source keys must not be null or empty."); if (ClusterSlotHashUtil.isSameSlotForAllKeys(command.getSourceKeys())) { return super.zInterStore(Mono.just(command)); } return Mono .error(new InvalidDataAccessApiUsageException("All keys must map to the same slot for ZINTERSTORE command.")); })); } }