From c2858055a8050ab4689a47dbdf5d2c3e266d382e Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 28 Nov 2017 11:34:12 +0100 Subject: [PATCH] #323 - Add examples for Reactive Redis Template. --- redis/reactive/pom.xml | 6 ++ .../redis/RedisTestConfiguration.java | 12 ++- .../redis/operations/ListOperationsTests.java | 85 ++++++++++++++++ .../operations/ValueOperationsTests.java | 98 +++++++++++++++++++ .../src/test/resources/application.properties | 1 + 5 files changed, 200 insertions(+), 2 deletions(-) create mode 100644 redis/reactive/src/test/java/example/springdata/redis/operations/ListOperationsTests.java create mode 100644 redis/reactive/src/test/java/example/springdata/redis/operations/ValueOperationsTests.java diff --git a/redis/reactive/pom.xml b/redis/reactive/pom.xml index 98637fc4..2b89bca7 100644 --- a/redis/reactive/pom.xml +++ b/redis/reactive/pom.xml @@ -29,6 +29,12 @@ lettuce-core + + io.projectreactor + reactor-test + test + + ${project.groupId} spring-data-redis-example-utils diff --git a/redis/reactive/src/test/java/example/springdata/redis/RedisTestConfiguration.java b/redis/reactive/src/test/java/example/springdata/redis/RedisTestConfiguration.java index 26c7d1dc..41edf738 100644 --- a/redis/reactive/src/test/java/example/springdata/redis/RedisTestConfiguration.java +++ b/redis/reactive/src/test/java/example/springdata/redis/RedisTestConfiguration.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 the original author or authors. + * 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. @@ -20,8 +20,11 @@ import javax.annotation.PreDestroy; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.Bean; +import org.springframework.data.redis.connection.ReactiveRedisConnectionFactory; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory; +import org.springframework.data.redis.core.ReactiveRedisTemplate; +import org.springframework.data.redis.serializer.RedisSerializationContext; /** * @author Mark Paluch @@ -32,10 +35,15 @@ public class RedisTestConfiguration { @Autowired RedisConnectionFactory factory; @Bean - public RedisConnectionFactory redisConnectionFactory() { + public LettuceConnectionFactory redisConnectionFactory() { return new LettuceConnectionFactory(); } + @Bean + public ReactiveRedisTemplate reactiveRedisTemplate(ReactiveRedisConnectionFactory connectionFactory) { + return new ReactiveRedisTemplate<>(connectionFactory, RedisSerializationContext.string()); + } + /** * Clear database before shut down. */ diff --git a/redis/reactive/src/test/java/example/springdata/redis/operations/ListOperationsTests.java b/redis/reactive/src/test/java/example/springdata/redis/operations/ListOperationsTests.java new file mode 100644 index 00000000..5390c9a4 --- /dev/null +++ b/redis/reactive/src/test/java/example/springdata/redis/operations/ListOperationsTests.java @@ -0,0 +1,85 @@ +/* + * Copyright 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 example.springdata.redis.operations; + +import example.springdata.redis.RedisTestConfiguration; +import example.springdata.redis.test.util.RequiresRedisServer; +import lombok.extern.slf4j.Slf4j; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import java.time.Duration; +import java.util.logging.Level; + +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.redis.core.ReactiveListOperations; +import org.springframework.data.redis.core.ReactiveRedisOperations; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * Show usage of reactive Template API on Redis lists using {@link ReactiveRedisOperations}. + * + * @author Mark Paluch + */ +@Slf4j +@RunWith(SpringRunner.class) +@SpringBootTest(classes = RedisTestConfiguration.class) +public class ListOperationsTests { + + // we only want to run this tests when redis is up an running + public static @ClassRule RequiresRedisServer requiresServer = RequiresRedisServer.onLocalhost(); + + @Autowired ReactiveRedisOperations operations; + + @Before + public void before() { + StepVerifier.create(operations.execute(it -> it.serverCommands().flushDb())).expectNext("OK").verifyComplete(); + } + + /** + * A simple queue using Redis blocking list commands {@code BLPOP} and {@code LPUSH} to produce the queue message. + */ + @Test + public void shouldPollAndPopulateQueue() { + + String queue = "foo"; + + ReactiveListOperations listOperations = operations.opsForList(); + + Mono blpop = listOperations // + .leftPop(queue, Duration.ofSeconds(30)) // + .log("example.springdata.redis", Level.INFO); + + log.info("Blocking pop...waiting for message"); + StepVerifier.create(blpop) // + .then(() -> { + + Mono.delay(Duration.ofSeconds(10)).doOnSuccess(it -> { + + log.info("Subscriber produces message"); + + }).then(listOperations.leftPush(queue, "Hello, World!")).subscribe(); + + }).expectNext("Hello, World!").verifyComplete(); + + log.info("Blocking pop...done!"); + } +} diff --git a/redis/reactive/src/test/java/example/springdata/redis/operations/ValueOperationsTests.java b/redis/reactive/src/test/java/example/springdata/redis/operations/ValueOperationsTests.java new file mode 100644 index 00000000..2492417b --- /dev/null +++ b/redis/reactive/src/test/java/example/springdata/redis/operations/ValueOperationsTests.java @@ -0,0 +1,98 @@ +/* + * Copyright 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 example.springdata.redis.operations; + +import static org.assertj.core.api.Assertions.*; + +import example.springdata.redis.RedisTestConfiguration; +import example.springdata.redis.test.util.RequiresRedisServer; +import lombok.extern.slf4j.Slf4j; +import reactor.core.publisher.Mono; +import reactor.test.StepVerifier; + +import java.time.Duration; + +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.data.redis.core.ReactiveRedisOperations; +import org.springframework.data.redis.core.ReactiveValueOperations; +import org.springframework.test.context.junit4.SpringRunner; + +/** + * Show usage of reactive Template API on Redis strings using {@link ReactiveRedisOperations}. + * + * @author Mark Paluch + */ +@Slf4j +@RunWith(SpringRunner.class) +@SpringBootTest(classes = RedisTestConfiguration.class) +public class ValueOperationsTests { + + // we only want to run this tests when redis is up an running + public static @ClassRule RequiresRedisServer requiresServer = RequiresRedisServer.onLocalhost(); + + @Autowired ReactiveRedisOperations operations; + + @Before + public void before() { + StepVerifier.create(operations.execute(it -> it.serverCommands().flushDb())).expectNext("OK").verifyComplete(); + } + + /** + * Implement a simple caching sequence using {@code GET} and {@code SETEX} commands. + */ + @Test + public void shouldCacheValue() { + + String cacheKey = "foo"; + + ReactiveValueOperations valueOperations = operations.opsForValue(); + + Mono cachedMono = valueOperations.get(cacheKey) // + .switchIfEmpty(cacheValue().flatMap(it -> { + + return valueOperations.set(cacheKey, it, Duration.ofSeconds(60)).then(Mono.just(it)); + })); + + log.info("Initial access (takes a while...)"); + + StepVerifier.create(cachedMono).expectSubscription() // + .expectNoEvent(Duration.ofSeconds(9)) // + .expectNext("Hello, World!") // + .verifyComplete(); + + log.info("Subsequent access (use cached value)"); + + Duration duration = StepVerifier.create(cachedMono) // + .expectNext("Hello, World!") // + .verifyComplete(); + + log.info("Done"); + + assertThat(duration).isLessThan(Duration.ofSeconds(2)); + } + + /** + * @return the cache value that is expensive to calculate. + */ + private Mono cacheValue() { + return Mono.delay(Duration.ofSeconds(10)).then(Mono.just("Hello, World!")); + } +} diff --git a/redis/reactive/src/test/resources/application.properties b/redis/reactive/src/test/resources/application.properties index bac6605c..c96b3847 100644 --- a/redis/reactive/src/test/resources/application.properties +++ b/redis/reactive/src/test/resources/application.properties @@ -1 +1,2 @@ logging.level.root=WARN +logging.level.example.springdata.redis=INFO