/* * Copyright 2016-2017 the original author or authors. * * 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.springframework.data.redis.connection.jedis; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; import java.util.Arrays; import java.util.List; import java.util.Map.Entry; import java.util.concurrent.LinkedBlockingDeque; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import org.junit.AfterClass; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameters; import org.springframework.data.redis.ConnectionFactoryTracker; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; import org.springframework.data.redis.connection.lettuce.LettuceTestClientResources; import org.springframework.data.redis.core.BoundHashOperations; import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ScanOptions; import org.springframework.data.redis.core.StringRedisTemplate; /** * @autor Mark Paluch * @author Christoph Strobl */ @RunWith(Parameterized.class) public class ScanTests { RedisConnectionFactory factory; RedisTemplate<String, String> redisOperations; ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 10, 1, TimeUnit.MINUTES, new LinkedBlockingDeque<Runnable>()); public ScanTests(RedisConnectionFactory factory) { this.factory = factory; ConnectionFactoryTracker.add(factory); } @Parameters public static List<RedisConnectionFactory> params() { JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory(); jedisConnectionFactory.setHostName("127.0.0.1"); jedisConnectionFactory.setPort(6379); jedisConnectionFactory.afterPropertiesSet(); LettuceConnectionFactory lettuceConnectionFactory = new LettuceConnectionFactory(); lettuceConnectionFactory.setClientResources(LettuceTestClientResources.getSharedClientResources()); lettuceConnectionFactory.setHostName("127.0.0.1"); lettuceConnectionFactory.setPort(6379); lettuceConnectionFactory.afterPropertiesSet(); return Arrays.<RedisConnectionFactory> asList(jedisConnectionFactory, lettuceConnectionFactory); } @AfterClass public static void cleanUp() { ConnectionFactoryTracker.cleanUp(); } @Before public void setUp() { redisOperations = new StringRedisTemplate(factory); redisOperations.afterPropertiesSet(); } @Test public void contextLoads() throws InterruptedException { BoundHashOperations<String, String, String> hash = redisOperations.boundHashOps("hash"); final AtomicReference<Exception> exception = new AtomicReference<Exception>(); // Create some keys so that SCAN requires a while to return all data. for (int i = 0; i < 10000; i++) { hash.put("key-" + i, "value"); } // Concurrent access for (int i = 0; i < 10; i++) { executor.submit(new Runnable() { @Override public void run() { try { Cursor<Entry<Object, Object>> cursorMap = redisOperations.boundHashOps("hash") .scan(ScanOptions.scanOptions().match("*").count(100).build()); // This line invokes the lazy SCAN invocation while (cursorMap.hasNext()) { cursorMap.next(); } cursorMap.close(); } catch (Exception e) { exception.set(e); } } }); } // Wait until work is finished while (executor.getActiveCount() > 0) { Thread.sleep(100); } executor.shutdown(); assertThat(exception.get(), is(nullValue())); } }