/* * Copyright 2011-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.lettuce; import static org.hamcrest.core.Is.*; import static org.hamcrest.core.IsEqual.*; import static org.hamcrest.core.IsNull.*; import static org.junit.Assert.*; import io.lettuce.core.RedisException; import io.lettuce.core.api.async.RedisAsyncCommands; import io.lettuce.core.api.reactive.BaseRedisReactiveCommands; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.springframework.data.redis.ConnectionFactoryTracker; import org.springframework.data.redis.RedisConnectionFailureException; import org.springframework.data.redis.RedisSystemException; import org.springframework.data.redis.SettingsUtils; import org.springframework.data.redis.connection.DefaultStringRedisConnection; import org.springframework.data.redis.connection.RedisConnection; import org.springframework.data.redis.connection.StringRedisConnection; /** * Integration test of {@link LettuceConnectionFactory} * * @author Jennifer Hickey * @author Thomas Darimont * @author Christoph Strobl * @author Mark Paluch */ public class LettuceConnectionFactoryTests { private LettuceConnectionFactory factory; private StringRedisConnection connection; @Before public void setUp() { factory = new LettuceConnectionFactory(SettingsUtils.getHost(), SettingsUtils.getPort()); factory.setClientResources(LettuceTestClientResources.getSharedClientResources()); factory.afterPropertiesSet(); factory.setShutdownTimeout(0); connection = new DefaultStringRedisConnection(factory.getConnection()); } @After public void tearDown() { factory.destroy(); if (connection != null) { connection.close(); } } @AfterClass public static void cleanUp() { ConnectionFactoryTracker.cleanUp(); } @SuppressWarnings("rawtypes") @Test public void testGetNewConnectionOnError() throws Exception { factory.setValidateConnection(true); connection.lPush("alist", "baz"); RedisAsyncCommands nativeConn = (RedisAsyncCommands) connection.getNativeConnection(); nativeConn.getStatefulConnection().close(); // Give some time for async channel close Thread.sleep(500); connection.bLPop(1, "alist".getBytes()); try { connection.get("test3"); fail("Expected exception using natively closed conn"); } catch (RedisSystemException e) { // expected, shared conn is closed } DefaultStringRedisConnection conn2 = new DefaultStringRedisConnection(factory.getConnection()); assertNotSame(nativeConn, conn2.getNativeConnection()); conn2.set("anotherkey", "anothervalue"); assertEquals("anothervalue", conn2.get("anotherkey")); conn2.close(); } @SuppressWarnings("rawtypes") @Test public void testConnectionErrorNoValidate() throws Exception { connection.lPush("ablist", "baz"); ((RedisAsyncCommands) connection.getNativeConnection()).getStatefulConnection().close(); // Give some time for async channel close Thread.sleep(500); DefaultStringRedisConnection conn2 = new DefaultStringRedisConnection(factory.getConnection()); try { conn2.set("anotherkey", "anothervalue"); fail("Expected exception using natively closed conn"); } catch (RedisSystemException e) { // expected, as we are re-using the natively closed conn } finally { conn2.close(); } } @Test public void testValidateNoError() { factory.setValidateConnection(true); RedisConnection conn2 = factory.getConnection(); assertSame(connection.getNativeConnection(), conn2.getNativeConnection()); } @Test public void testSelectDb() { LettuceConnectionFactory factory2 = new LettuceConnectionFactory(SettingsUtils.getHost(), SettingsUtils.getPort()); factory2.setClientResources(LettuceTestClientResources.getSharedClientResources()); factory2.setShutdownTimeout(0); factory2.setDatabase(1); factory2.afterPropertiesSet(); ConnectionFactoryTracker.add(factory2); StringRedisConnection connection2 = new DefaultStringRedisConnection(factory2.getConnection()); connection2.flushDb(); // put an item in database 0 connection.set("sometestkey", "sometestvalue"); try { // there should still be nothing in database 1 assertEquals(Long.valueOf(0), connection2.dbSize()); } finally { connection2.close(); factory2.destroy(); } } @SuppressWarnings("unchecked") @Test public void testDisableSharedConnection() throws Exception { factory.setShareNativeConnection(false); RedisConnection conn2 = factory.getConnection(); assertNotSame(connection.getNativeConnection(), conn2.getNativeConnection()); // Give some time for native connection to asynchronously initialize, else close doesn't work Thread.sleep(100); conn2.close(); assertTrue(conn2.isClosed()); // Give some time for native connection to asynchronously close Thread.sleep(100); try { ((RedisAsyncCommands<byte[], byte[]>) conn2.getNativeConnection()).ping(); fail("The native connection should be closed"); } catch (RedisException e) { // expected } } @SuppressWarnings("unchecked") @Test public void testResetConnection() { RedisAsyncCommands<byte[], byte[]> nativeConn = (RedisAsyncCommands<byte[], byte[]>) connection .getNativeConnection(); factory.resetConnection(); assertNotSame(nativeConn, factory.getConnection().getNativeConnection()); nativeConn.getStatefulConnection().close(); } @SuppressWarnings("unchecked") @Test public void testInitConnection() { RedisAsyncCommands<byte[], byte[]> nativeConn = (RedisAsyncCommands<byte[], byte[]>) connection .getNativeConnection(); factory.initConnection(); RedisConnection newConnection = factory.getConnection(); assertNotSame(nativeConn, newConnection.getNativeConnection()); newConnection.close(); } @SuppressWarnings("unchecked") @Test public void testResetAndInitConnection() { RedisAsyncCommands<byte[], byte[]> nativeConn = (RedisAsyncCommands<byte[], byte[]>) connection .getNativeConnection(); factory.resetConnection(); factory.initConnection(); RedisConnection newConnection = factory.getConnection(); assertNotSame(nativeConn, newConnection.getNativeConnection()); newConnection.close(); } @Test public void testGetConnectionException() { factory.resetConnection(); factory.setHostName("fakeHost"); factory.afterPropertiesSet(); try { factory.getConnection(); fail("Expected connection failure exception"); } catch (RedisConnectionFailureException e) {} } @Test public void testGetConnectionNotSharedBadHostname() { factory.setShareNativeConnection(false); factory.setHostName("fakeHost"); factory.afterPropertiesSet(); factory.getConnection(); } @Test public void testGetSharedConnectionNotShared() { factory.setShareNativeConnection(false); factory.setHostName("fakeHost"); factory.afterPropertiesSet(); assertNull(factory.getSharedConnection()); } @Test public void testCreateFactoryWithPool() { DefaultLettucePool pool = new DefaultLettucePool(SettingsUtils.getHost(), SettingsUtils.getPort()); pool.setClientResources(LettuceTestClientResources.getSharedClientResources()); pool.afterPropertiesSet(); LettuceConnectionFactory factory2 = new LettuceConnectionFactory(pool); factory2.setShutdownTimeout(0); factory2.afterPropertiesSet(); ConnectionFactoryTracker.add(factory2); RedisConnection conn2 = factory2.getConnection(); conn2.close(); factory2.destroy(); pool.destroy(); } @Ignore("Uncomment this test to manually check connection reuse in a pool scenario") @Test public void testLotsOfConnections() throws InterruptedException { // Running a netstat here should show only the 8 conns from the pool (plus 2 from setUp and 1 from factory2 // afterPropertiesSet for shared conn) DefaultLettucePool pool = new DefaultLettucePool(SettingsUtils.getHost(), SettingsUtils.getPort()); pool.afterPropertiesSet(); final LettuceConnectionFactory factory2 = new LettuceConnectionFactory(pool); factory2.afterPropertiesSet(); ConnectionFactoryTracker.add(factory2); for (int i = 1; i < 1000; i++) { Thread th = new Thread(new Runnable() { public void run() { factory2.getConnection().bRPop(50000, "foo".getBytes()); } }); th.start(); } Thread.sleep(234234234); } @Ignore("Redis must have requirepass set to run this test") @Test public void testConnectWithPassword() { factory.setPassword("foo"); factory.afterPropertiesSet(); RedisConnection conn = factory.getConnection(); // Test shared and dedicated conns conn.ping(); conn.bLPop(1, "key".getBytes()); conn.close(); } @Test // DATAREDIS-431 public void dbIndexShouldBePropagatedCorrectly() { LettuceConnectionFactory factory = new LettuceConnectionFactory(); factory.setClientResources(LettuceTestClientResources.getSharedClientResources()); factory.setDatabase(2); factory.afterPropertiesSet(); ConnectionFactoryTracker.add(factory); StringRedisConnection connectionToDbIndex2 = new DefaultStringRedisConnection(factory.getConnection()); try { String key = "key-in-db-2"; connectionToDbIndex2.set(key, "the wheel of time"); assertThat(connection.get(key), nullValue()); assertThat(connectionToDbIndex2.get(key), notNullValue()); } finally { connectionToDbIndex2.close(); } } @Test // DATAREDIS-462 public void factoryWorksWithoutClientResources() { LettuceConnectionFactory factory = new LettuceConnectionFactory(); factory.setShutdownTimeout(0); factory.afterPropertiesSet(); ConnectionFactoryTracker.add(factory); StringRedisConnection connection = new DefaultStringRedisConnection(factory.getConnection()); try { assertThat(connection.ping(), is(equalTo("PONG"))); } finally { connection.close(); } } @Test // DATAREDIS-525 public void factoryShouldReturnReactiveConnectionWhenCorrectly() { LettuceConnectionFactory factory = new LettuceConnectionFactory(); factory.setClientResources(LettuceTestClientResources.getSharedClientResources()); factory.afterPropertiesSet(); ConnectionFactoryTracker.add(factory); assertThat(factory.getReactiveConnection().execute(BaseRedisReactiveCommands::ping).blockFirst(), is("PONG")); } }