/** * Copyright 2016 Nikita Koksharov * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.redisson.reactive; import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.List; import org.reactivestreams.Publisher; import org.reactivestreams.Subscriber; import org.reactivestreams.Subscription; import org.redisson.RedissonKeys; import org.redisson.api.RKeysReactive; import org.redisson.client.codec.StringCodec; import org.redisson.client.protocol.RedisCommands; import org.redisson.client.protocol.decoder.ListScanResult; import org.redisson.cluster.ClusterSlotRange; import org.redisson.command.CommandReactiveService; import org.redisson.connection.MasterSlaveEntry; import reactor.rx.Stream; import reactor.rx.Streams; import reactor.rx.subscription.ReactiveSubscription; public class RedissonKeysReactive implements RKeysReactive { private final CommandReactiveService commandExecutor; private final RedissonKeys instance; public RedissonKeysReactive(CommandReactiveService commandExecutor) { super(); instance = new RedissonKeys(commandExecutor); this.commandExecutor = commandExecutor; } @Override public Publisher<Integer> getSlot(String key) { return commandExecutor.reactive(instance.getSlotAsync(key)); } @Override public Publisher<String> getKeysByPattern(String pattern) { List<Publisher<String>> publishers = new ArrayList<Publisher<String>>(); for (MasterSlaveEntry entry : commandExecutor.getConnectionManager().getEntrySet()) { publishers.add(createKeysIterator(entry, pattern)); } return Streams.merge(publishers); } @Override public Publisher<String> getKeys() { return getKeysByPattern(null); } private Publisher<ListScanResult<String>> scanIterator(MasterSlaveEntry entry, long startPos, String pattern) { if (pattern == null) { return commandExecutor.writeReactive(entry, StringCodec.INSTANCE, RedisCommands.SCAN, startPos); } return commandExecutor.writeReactive(entry, StringCodec.INSTANCE, RedisCommands.SCAN, startPos, "MATCH", pattern); } private Publisher<String> createKeysIterator(final MasterSlaveEntry entry, final String pattern) { return new Stream<String>() { @Override public void subscribe(final Subscriber<? super String> t) { t.onSubscribe(new ReactiveSubscription<String>(this, t) { private List<String> firstValues; private long nextIterPos; private InetSocketAddress client; private long currentIndex; @Override protected void onRequest(final long n) { currentIndex = n; nextValues(); } protected void nextValues() { final ReactiveSubscription<String> m = this; scanIterator(entry, nextIterPos, pattern).subscribe(new Subscriber<ListScanResult<String>>() { @Override public void onSubscribe(Subscription s) { s.request(Long.MAX_VALUE); } @Override public void onNext(ListScanResult<String> res) { client = res.getRedisClient(); long prevIterPos = nextIterPos; if (nextIterPos == 0 && firstValues == null) { firstValues = res.getValues(); } else if (res.getValues().equals(firstValues)) { m.onComplete(); currentIndex = 0; return; } nextIterPos = res.getPos(); if (prevIterPos == nextIterPos) { nextIterPos = -1; } for (String val : res.getValues()) { m.onNext(val); currentIndex--; if (currentIndex == 0) { m.onComplete(); return; } } if (nextIterPos == -1) { m.onComplete(); currentIndex = 0; } } @Override public void onError(Throwable error) { m.onError(error); } @Override public void onComplete() { if (currentIndex == 0) { return; } nextValues(); } }); } }); } }; } @Override public Publisher<Collection<String>> findKeysByPattern(String pattern) { return commandExecutor.reactive(instance.findKeysByPatternAsync(pattern)); } @Override public Publisher<String> randomKey() { return commandExecutor.reactive(instance.randomKeyAsync()); } @Override public Publisher<Long> deleteByPattern(String pattern) { return commandExecutor.reactive(instance.deleteByPatternAsync(pattern)); } @Override public Publisher<Long> delete(String ... keys) { return commandExecutor.reactive(instance.deleteAsync(keys)); } @Override public Publisher<Void> flushdb() { return commandExecutor.reactive(instance.flushdbAsync()); } @Override public Publisher<Void> flushall() { return commandExecutor.reactive(instance.flushallAsync()); } }