Skip to content

Commit 0d552ec

Browse files
mp911dechristophstrobl
authored andcommitted
Encapsulate Redis Scan CursorId.
We now retain the raw cursor value without attempting to convert it into a long as Redis uses 64 bit unsigned integers exceeding Long.MAX_VALUE.
1 parent 861e288 commit 0d552ec

20 files changed

+331
-89
lines changed

src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterHashCommands.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,14 +275,14 @@ public Cursor<Entry<byte[], byte[]>> hScan(byte[] key, ScanOptions options) {
275275
return new ScanCursor<Entry<byte[], byte[]>>(options) {
276276

277277
@Override
278-
protected ScanIteration<Entry<byte[], byte[]>> doScan(long cursorId, ScanOptions options) {
278+
protected ScanIteration<Entry<byte[], byte[]>> doScan(CursorId cursorId, ScanOptions options) {
279279

280280
ScanParams params = JedisConverters.toScanParams(options);
281281

282282
ScanResult<Entry<byte[], byte[]>> result = connection.getCluster().hscan(key,
283283
JedisConverters.toBytes(Long.toUnsignedString(cursorId)),
284284
params);
285-
return new ScanIteration<>(Long.parseUnsignedLong(result.getCursor()), result.getResult());
285+
return new ScanIteration<>(CursorId.of(result.getCursor()), result.getResult());
286286
}
287287
}.open();
288288
}

src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterKeyCommands.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -177,11 +177,11 @@ Cursor<byte[]> scan(RedisClusterNode node, ScanOptions options) {
177177
return new ScanCursor<byte[]>(0, options) {
178178

179179
@Override
180-
protected ScanIteration<byte[]> doScan(long cursorId, ScanOptions options) {
180+
protected ScanIteration<byte[]> doScan(CursorId cursorId, ScanOptions options) {
181181

182182
ScanParams params = JedisConverters.toScanParams(options);
183-
ScanResult<String> result = client.scan(Long.toUnsignedString(cursorId), params);
184-
return new ScanIteration<>(Long.parseUnsignedLong(result.getCursor()),
183+
ScanResult<String> result = client.scan(cursorId.getCursorId(), params);
184+
return new ScanIteration<>(CursorId.of(result.getCursor()),
185185
JedisConverters.stringListToByteList().convert(result.getResult()));
186186
}
187187
}.open();

src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterSetCommands.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -394,12 +394,11 @@ public Cursor<byte[]> sScan(byte[] key, ScanOptions options) {
394394
return new ScanCursor<byte[]>(options) {
395395

396396
@Override
397-
protected ScanIteration<byte[]> doScan(long cursorId, ScanOptions options) {
397+
protected ScanIteration<byte[]> doScan(CursorId cursorId, ScanOptions options) {
398398

399399
ScanParams params = JedisConverters.toScanParams(options);
400-
ScanResult<byte[]> result = connection.getCluster().sscan(key,
401-
JedisConverters.toBytes(Long.toUnsignedString(cursorId)), params);
402-
return new ScanIteration<>(Long.parseUnsignedLong(result.getCursor()), result.getResult());
400+
ScanResult<byte[]> result = connection.getCluster().sscan(key, JedisConverters.toBytes(cursorId), params);
401+
return new ScanIteration<>(CursorId.of(result.getCursor()), result.getResult());
403402
}
404403
}.open();
405404
}

src/main/java/org/springframework/data/redis/connection/jedis/JedisClusterZSetCommands.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1079,13 +1079,13 @@ public Cursor<Tuple> zScan(byte[] key, ScanOptions options) {
10791079
return new ScanCursor<Tuple>(options) {
10801080

10811081
@Override
1082-
protected ScanIteration<Tuple> doScan(long cursorId, ScanOptions options) {
1082+
protected ScanIteration<Tuple> doScan(CursorId cursorId, ScanOptions options) {
10831083

10841084
ScanParams params = JedisConverters.toScanParams(options);
10851085

10861086
ScanResult<redis.clients.jedis.resps.Tuple> result = connection.getCluster().zscan(key,
1087-
JedisConverters.toBytes(Long.toUnsignedString(cursorId)), params);
1088-
return new ScanIteration<>(Long.parseUnsignedLong(result.getCursor()),
1087+
JedisConverters.toBytes(cursorId), params);
1088+
return new ScanIteration<>(CursorId.of(result.getCursor()),
10891089
JedisConverters.tuplesToTuples().convert(result.getResult()));
10901090
}
10911091
}.open();

src/main/java/org/springframework/data/redis/connection/jedis/JedisConverters.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
import org.springframework.data.redis.connection.convert.StringToRedisClientInfoConverter;
7676
import org.springframework.data.redis.connection.zset.DefaultTuple;
7777
import org.springframework.data.redis.connection.zset.Tuple;
78+
import org.springframework.data.redis.core.Cursor;
7879
import org.springframework.data.redis.core.ScanOptions;
7980
import org.springframework.data.redis.core.types.Expiration;
8081
import org.springframework.data.redis.core.types.RedisClientInfo;
@@ -175,6 +176,10 @@ public static byte[] toBytes(Number source) {
175176
return toBytes(String.valueOf(source));
176177
}
177178

179+
public static byte[] toBytes(Cursor.CursorId source) {
180+
return toBytes(source.getCursorId());
181+
}
182+
178183
@Nullable
179184
public static byte[] toBytes(@Nullable String source) {
180185
return source == null ? null : SafeEncoder.encode(source);

src/main/java/org/springframework/data/redis/connection/jedis/JedisHashCommands.java

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.springframework.data.redis.connection.RedisHashCommands;
3131
import org.springframework.data.redis.connection.convert.Converters;
3232
import org.springframework.data.redis.core.Cursor;
33+
import org.springframework.data.redis.core.Cursor.CursorId;
3334
import org.springframework.data.redis.core.KeyBoundCursor;
3435
import org.springframework.data.redis.core.ScanIteration;
3536
import org.springframework.data.redis.core.ScanOptions;
@@ -149,8 +150,7 @@ public List<Entry<byte[], byte[]>> hRandFieldWithValues(byte[] key, long count)
149150

150151
List<Entry<byte[], byte[]>> convertedMapEntryList = new ArrayList<>(mapEntryList.size());
151152

152-
mapEntryList.forEach(entry ->
153-
convertedMapEntryList.add(Converters.entryOf(entry.getKey(), entry.getValue())));
153+
mapEntryList.forEach(entry -> convertedMapEntryList.add(Converters.entryOf(entry.getKey(), entry.getValue())));
154154

155155
return convertedMapEntryList;
156156

@@ -219,24 +219,17 @@ public List<byte[]> hVals(byte[] key) {
219219

220220
@Override
221221
public Cursor<Entry<byte[], byte[]>> hScan(byte[] key, ScanOptions options) {
222-
return hScan(key, 0, options);
222+
return hScan(key, CursorId.initial(), options);
223223
}
224224

225-
/**
226-
* @since 1.4
227-
* @param key
228-
* @param cursorId
229-
* @param options
230-
* @return
231-
*/
232-
public Cursor<Entry<byte[], byte[]>> hScan(byte[] key, long cursorId, ScanOptions options) {
225+
public Cursor<Entry<byte[], byte[]>> hScan(byte[] key, CursorId cursorId, ScanOptions options) {
233226

234227
Assert.notNull(key, "Key must not be null");
235228

236229
return new KeyBoundCursor<Entry<byte[], byte[]>>(key, cursorId, options) {
237230

238231
@Override
239-
protected ScanIteration<Entry<byte[], byte[]>> doScan(byte[] key, long cursorId, ScanOptions options) {
232+
protected ScanIteration<Entry<byte[], byte[]>> doScan(byte[] key, CursorId cursorId, ScanOptions options) {
240233

241234
if (isQueueing() || isPipelined()) {
242235
throw new InvalidDataAccessApiUsageException("'HSCAN' cannot be called in pipeline / transaction mode");
@@ -245,9 +238,8 @@ protected ScanIteration<Entry<byte[], byte[]>> doScan(byte[] key, long cursorId,
245238
ScanParams params = JedisConverters.toScanParams(options);
246239

247240
ScanResult<Entry<byte[], byte[]>> result = connection.getJedis().hscan(key,
248-
JedisConverters.toBytes(Long.toUnsignedString(cursorId)),
249-
params);
250-
return new ScanIteration<>(Long.parseUnsignedLong(result.getCursor()), result.getResult());
241+
JedisConverters.toBytes(cursorId), params);
242+
return new ScanIteration<>(CursorId.of(result.getCursor()), result.getResult());
251243
}
252244

253245
@Override

src/main/java/org/springframework/data/redis/connection/jedis/JedisKeyCommands.java

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.springframework.data.redis.connection.ValueEncoding.RedisValueEncoding;
3737
import org.springframework.data.redis.connection.convert.Converters;
3838
import org.springframework.data.redis.core.Cursor;
39+
import org.springframework.data.redis.core.Cursor.CursorId;
3940
import org.springframework.data.redis.core.KeyScanOptions;
4041
import org.springframework.data.redis.core.ScanCursor;
4142
import org.springframework.data.redis.core.ScanIteration;
@@ -131,7 +132,7 @@ public Set<byte[]> keys(byte[] pattern) {
131132

132133
@Override
133134
public Cursor<byte[]> scan(ScanOptions options) {
134-
return scan(0, options != null ? options : ScanOptions.NONE);
135+
return scan(CursorId.initial(), options != null ? options : ScanOptions.NONE);
135136
}
136137

137138
/**
@@ -140,12 +141,12 @@ public Cursor<byte[]> scan(ScanOptions options) {
140141
* @param options
141142
* @return
142143
*/
143-
public Cursor<byte[]> scan(long cursorId, ScanOptions options) {
144+
public Cursor<byte[]> scan(CursorId cursorId, ScanOptions options) {
144145

145146
return new ScanCursor<byte[]>(cursorId, options) {
146147

147148
@Override
148-
protected ScanIteration<byte[]> doScan(long cursorId, ScanOptions options) {
149+
protected ScanIteration<byte[]> doScan(CursorId cursorId, ScanOptions options) {
149150

150151
if (isQueueing() || isPipelined()) {
151152
throw new InvalidDataAccessApiUsageException("'SCAN' cannot be called in pipeline / transaction mode");
@@ -165,12 +166,12 @@ protected ScanIteration<byte[]> doScan(long cursorId, ScanOptions options) {
165166
}
166167

167168
if (type != null) {
168-
result = connection.getJedis().scan(JedisConverters.toBytes(Long.toUnsignedString(cursorId)), params, type);
169+
result = connection.getJedis().scan(JedisConverters.toBytes(cursorId), params, type);
169170
} else {
170-
result = connection.getJedis().scan(JedisConverters.toBytes(Long.toUnsignedString(cursorId)), params);
171+
result = connection.getJedis().scan(JedisConverters.toBytes(cursorId), params);
171172
}
172173

173-
return new ScanIteration<>(Long.parseUnsignedLong(result.getCursor()), result.getResult());
174+
return new ScanIteration<>(CursorId.of(result.getCursor()), result.getResult());
174175
}
175176

176177
protected void doClose() {

src/main/java/org/springframework/data/redis/connection/jedis/JedisSetCommands.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.springframework.dao.InvalidDataAccessApiUsageException;
2828
import org.springframework.data.redis.connection.RedisSetCommands;
2929
import org.springframework.data.redis.core.Cursor;
30+
import org.springframework.data.redis.core.Cursor.CursorId;
3031
import org.springframework.data.redis.core.KeyBoundCursor;
3132
import org.springframework.data.redis.core.ScanIteration;
3233
import org.springframework.data.redis.core.ScanOptions;
@@ -206,34 +207,33 @@ public Long sUnionStore(byte[] destKey, byte[]... keys) {
206207

207208
@Override
208209
public Cursor<byte[]> sScan(byte[] key, ScanOptions options) {
209-
return sScan(key, 0, options);
210+
return sScan(key, CursorId.initial(), options);
210211
}
211212

212213
/**
213-
* @since 1.4
214214
* @param key
215215
* @param cursorId
216216
* @param options
217217
* @return
218+
* @since 3.2.1
218219
*/
219-
public Cursor<byte[]> sScan(byte[] key, long cursorId, ScanOptions options) {
220+
public Cursor<byte[]> sScan(byte[] key, CursorId cursorId, ScanOptions options) {
220221

221222
Assert.notNull(key, "Key must not be null");
222223

223224
return new KeyBoundCursor<byte[]>(key, cursorId, options) {
224225

225226
@Override
226-
protected ScanIteration<byte[]> doScan(byte[] key, long cursorId, ScanOptions options) {
227+
protected ScanIteration<byte[]> doScan(byte[] key, CursorId cursorId, ScanOptions options) {
227228

228229
if (isQueueing() || isPipelined()) {
229230
throw new InvalidDataAccessApiUsageException("'SSCAN' cannot be called in pipeline / transaction mode");
230231
}
231232

232233
ScanParams params = JedisConverters.toScanParams(options);
233234

234-
ScanResult<byte[]> result = connection.getJedis().sscan(key,
235-
JedisConverters.toBytes(Long.toUnsignedString(cursorId)), params);
236-
return new ScanIteration<>(Long.parseUnsignedLong(result.getCursor()), result.getResult());
235+
ScanResult<byte[]> result = connection.getJedis().sscan(key, JedisConverters.toBytes(cursorId), params);
236+
return new ScanIteration<>(CursorId.of(result.getCursor()), result.getResult());
237237
}
238238

239239
protected void doClose() {

src/main/java/org/springframework/data/redis/connection/jedis/JedisZSetCommands.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.springframework.data.redis.connection.zset.Tuple;
3636
import org.springframework.data.redis.connection.zset.Weights;
3737
import org.springframework.data.redis.core.Cursor;
38+
import org.springframework.data.redis.core.Cursor.CursorId;
3839
import org.springframework.data.redis.core.KeyBoundCursor;
3940
import org.springframework.data.redis.core.ScanIteration;
4041
import org.springframework.data.redis.core.ScanOptions;
@@ -561,24 +562,25 @@ public Long zUnionStore(byte[] destKey, byte[]... sets) {
561562

562563
@Override
563564
public Cursor<Tuple> zScan(byte[] key, ScanOptions options) {
564-
return zScan(key, 0L, options);
565+
return zScan(key, CursorId.initial(), options);
565566
}
566567

568+
567569
/**
568-
* @since 1.4
569570
* @param key
570571
* @param cursorId
571572
* @param options
572573
* @return
574+
* @since 3.2.1
573575
*/
574-
public Cursor<Tuple> zScan(byte[] key, Long cursorId, ScanOptions options) {
576+
public Cursor<Tuple> zScan(byte[] key, CursorId cursorId, ScanOptions options) {
575577

576578
Assert.notNull(key, "Key must not be null");
577579

578580
return new KeyBoundCursor<Tuple>(key, cursorId, options) {
579581

580582
@Override
581-
protected ScanIteration<Tuple> doScan(byte[] key, long cursorId, ScanOptions options) {
583+
protected ScanIteration<Tuple> doScan(byte[] key, CursorId cursorId, ScanOptions options) {
582584

583585
if (isQueueing() || isPipelined()) {
584586
throw new InvalidDataAccessApiUsageException("'ZSCAN' cannot be called in pipeline / transaction mode");
@@ -587,8 +589,8 @@ protected ScanIteration<Tuple> doScan(byte[] key, long cursorId, ScanOptions opt
587589
ScanParams params = JedisConverters.toScanParams(options);
588590

589591
ScanResult<redis.clients.jedis.resps.Tuple> result = connection.getJedis().zscan(key,
590-
JedisConverters.toBytes(Long.toUnsignedString(cursorId)), params);
591-
return new ScanIteration<>(Long.parseUnsignedLong(result.getCursor()),
592+
JedisConverters.toBytes(cursorId), params);
593+
return new ScanIteration<>(CursorId.of(result.getCursor()),
592594
JedisConverters.tuplesToTuples().convert(result.getResult()));
593595
}
594596

src/main/java/org/springframework/data/redis/connection/lettuce/LettuceConnection.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757

5858
import org.apache.commons.logging.Log;
5959
import org.apache.commons.logging.LogFactory;
60+
6061
import org.springframework.beans.BeanUtils;
6162
import org.springframework.core.convert.converter.Converter;
6263
import org.springframework.dao.DataAccessException;
@@ -70,6 +71,7 @@
7071
import org.springframework.data.redis.connection.lettuce.LettuceConnectionProvider.TargetAware;
7172
import org.springframework.data.redis.connection.lettuce.LettuceResult.LettuceResultBuilder;
7273
import org.springframework.data.redis.connection.lettuce.LettuceResult.LettuceStatusResult;
74+
import org.springframework.data.redis.core.Cursor.CursorId;
7375
import org.springframework.data.redis.core.RedisCommand;
7476
import org.springframework.lang.Nullable;
7577
import org.springframework.util.Assert;
@@ -1060,8 +1062,8 @@ private void potentiallySelectDatabase(int dbIndex) {
10601062
}
10611063
}
10621064

1063-
io.lettuce.core.ScanCursor getScanCursor(long cursorId) {
1064-
return io.lettuce.core.ScanCursor.of(Long.toUnsignedString(cursorId));
1065+
io.lettuce.core.ScanCursor getScanCursor(CursorId cursorId) {
1066+
return io.lettuce.core.ScanCursor.of(cursorId.getCursorId());
10651067
}
10661068

10671069
private void validateCommandIfRunningInTransactionMode(ProtocolKeyword cmd, byte[]... args) {

src/main/java/org/springframework/data/redis/connection/lettuce/LettuceHashCommands.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.springframework.data.redis.connection.RedisHashCommands;
3030
import org.springframework.data.redis.connection.convert.Converters;
3131
import org.springframework.data.redis.core.Cursor;
32+
import org.springframework.data.redis.core.Cursor.CursorId;
3233
import org.springframework.data.redis.core.KeyBoundCursor;
3334
import org.springframework.data.redis.core.ScanIteration;
3435
import org.springframework.data.redis.core.ScanOptions;
@@ -204,24 +205,25 @@ public List<byte[]> hVals(byte[] key) {
204205

205206
@Override
206207
public Cursor<Entry<byte[], byte[]>> hScan(byte[] key, ScanOptions options) {
207-
return hScan(key, 0, options);
208+
return hScan(key, CursorId.initial(), options);
208209
}
209210

211+
210212
/**
211-
* @since 1.4
212213
* @param key
213214
* @param cursorId
214215
* @param options
215216
* @return
217+
* @since 1.4
216218
*/
217-
public Cursor<Entry<byte[], byte[]>> hScan(byte[] key, long cursorId, ScanOptions options) {
219+
public Cursor<Entry<byte[], byte[]>> hScan(byte[] key, CursorId cursorId, ScanOptions options) {
218220

219221
Assert.notNull(key, "Key must not be null");
220222

221223
return new KeyBoundCursor<Entry<byte[], byte[]>>(key, cursorId, options) {
222224

223225
@Override
224-
protected ScanIteration<Entry<byte[], byte[]>> doScan(byte[] key, long cursorId, ScanOptions options) {
226+
protected ScanIteration<Entry<byte[], byte[]>> doScan(byte[] key, CursorId cursorId, ScanOptions options) {
225227

226228
if (connection.isQueueing() || connection.isPipelined()) {
227229
throw new InvalidDataAccessApiUsageException("'HSCAN' cannot be called in pipeline / transaction mode");
@@ -235,7 +237,7 @@ protected ScanIteration<Entry<byte[], byte[]>> doScan(byte[] key, long cursorId,
235237
String nextCursorId = mapScanCursor.getCursor();
236238

237239
Map<byte[], byte[]> values = mapScanCursor.getMap();
238-
return new ScanIteration<>(Long.parseUnsignedLong(nextCursorId), values.entrySet());
240+
return new ScanIteration<>(CursorId.of(nextCursorId), values.entrySet());
239241
}
240242

241243
@Override

0 commit comments

Comments
 (0)