Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
* @author Oscar Cai
* @author Sébastien Volle
* @author John Blum
* @author LeeHyungGeol
* @since 1.3
* @link <a href=
* "https://github.com/antirez/redis/blob/843de8b786562d8d77c78d83a971060adc61f77a/src/server.c#L180">Redis
Expand Down Expand Up @@ -292,12 +293,16 @@ public enum RedisCommand {
ZINTERSTORE("rw", 3), //
ZRANGE("r", 3), //
ZRANGEBYSCORE("r", 3), //
ZRANGEWITHSCORES("r", 3), //
ZRANGEBYSCOREWITHSCORES("r", 2), //
ZRANK("r", 2, 2), //
ZREM("rw", 2), //
ZREMRANGEBYRANK("rw", 3, 3), //
ZREMRANGEBYSCORE("rw", 3, 3), //
ZREVRANGE("r", 3), //
ZREVRANGEBYSCORE("r", 3), //
ZREVRANGEWITHSCORES("r", 3), //
ZREVRANGEBYSCOREWITHSCORES("r", 2), //
ZREVRANK("r", 2, 2), //
ZSCORE("r", 2, 2), //
ZUNIONSTORE("rw", 3), //
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import java.sql.SQLException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;

import javax.sql.DataSource;
Expand All @@ -42,13 +43,15 @@
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory;
import org.springframework.data.redis.core.ZSetOperations.TypedTuple;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.support.TransactionTemplate;

/**
* Transactional integration tests for {@link StringRedisTemplate}.
*
* @author Christoph Strobl
* @author LeeHyungGeol
*/
@ParameterizedClass
@MethodSource("argumentsStream")
Expand Down Expand Up @@ -116,6 +119,96 @@ void visibilityDuringManagedTransaction() throws SQLException {
.containsEntry("isMember(inside)", false);
}

@SuppressWarnings("unchecked")
@Test // GH-3187
void allRangeWithScoresMethodsShouldExecuteImmediatelyInTransaction() throws SQLException {

DataSource ds = mock(DataSource.class);
when(ds.getConnection()).thenReturn(mock(Connection.class));

DataSourceTransactionManager txMgr = new DataSourceTransactionManager(ds);
TransactionTemplate txTemplate = new TransactionTemplate(txMgr);
txTemplate.afterPropertiesSet();

// Add data outside transaction
stringTemplate.opsForZSet().add("testzset", "outside1", 1.0);
stringTemplate.opsForZSet().add("testzset", "outside2", 2.0);

Map<String, Object> result = txTemplate.execute(x -> {
Map<String, Object> ops = new LinkedHashMap<>();

// Query data added outside transaction (should execute immediately)
ops.put("rangeWithScores_before",
stringTemplate.opsForZSet().rangeWithScores("testzset", 0, -1));
ops.put("reverseRangeWithScores_before",
stringTemplate.opsForZSet().reverseRangeWithScores("testzset", 0, -1));
ops.put("rangeByScoreWithScores_before",
stringTemplate.opsForZSet().rangeByScoreWithScores("testzset", 1.0, 2.0));
ops.put("reverseRangeByScoreWithScores_before",
stringTemplate.opsForZSet().reverseRangeByScoreWithScores("testzset", 1.0, 2.0));

// Add inside transaction (goes into multi/exec queue)
ops.put("add_result", stringTemplate.opsForZSet().add("testzset", "inside", 3.0));

// Changes made inside transaction should not be visible yet (read executes immediately)
ops.put("rangeWithScores_after",
stringTemplate.opsForZSet().rangeWithScores("testzset", 0, -1));
ops.put("reverseRangeWithScores_after",
stringTemplate.opsForZSet().reverseRangeWithScores("testzset", 0, -1));
ops.put("rangeByScoreWithScores_after",
stringTemplate.opsForZSet().rangeByScoreWithScores("testzset", 1.0, 3.0));
ops.put("reverseRangeByScoreWithScores_after",
stringTemplate.opsForZSet().reverseRangeByScoreWithScores("testzset", 1.0, 3.0));

return ops;
});

// add result is null (no result until exec)
assertThat(result).containsEntry("add_result", null);

// before: only data added outside transaction is visible
assertThat((Set<TypedTuple<String>>) result.get("rangeWithScores_before"))
.hasSize(2)
.extracting(TypedTuple::getValue)
.containsExactly("outside1", "outside2");

assertThat((Set<TypedTuple<String>>) result.get("reverseRangeWithScores_before"))
.hasSize(2)
.extracting(TypedTuple::getValue)
.containsExactly("outside2", "outside1");

assertThat((Set<TypedTuple<String>>) result.get("rangeByScoreWithScores_before"))
.hasSize(2)
.extracting(TypedTuple::getValue)
.containsExactly("outside1", "outside2");

assertThat((Set<TypedTuple<String>>) result.get("reverseRangeByScoreWithScores_before"))
.hasSize(2)
.extracting(TypedTuple::getValue)
.containsExactly("outside2", "outside1");

// after: changes made inside transaction are still not visible
assertThat((Set<TypedTuple<String>>) result.get("rangeWithScores_after"))
.hasSize(2)
.extracting(TypedTuple::getValue)
.containsExactly("outside1", "outside2");

assertThat((Set<TypedTuple<String>>) result.get("reverseRangeWithScores_after"))
.hasSize(2)
.extracting(TypedTuple::getValue)
.containsExactly("outside2", "outside1");

assertThat((Set<TypedTuple<String>>) result.get("rangeByScoreWithScores_after"))
.hasSize(2)
.extracting(TypedTuple::getValue)
.containsExactly("outside1", "outside2");

assertThat((Set<TypedTuple<String>>) result.get("reverseRangeByScoreWithScores_after"))
.hasSize(2)
.extracting(TypedTuple::getValue)
.containsExactly("outside2", "outside1");
}

static Stream<Arguments> argumentsStream() {

LettuceConnectionFactory lcf = new LettuceConnectionFactory(SettingsUtils.standaloneConfiguration());
Expand Down