We now delete all keys that are emitted by the source publisher when invoking delete(Publisher) and unlink(Publisher). Both methods split incoming keys into chunks of 128 keys to keep a balance between latency and the issued command count.
Previously, delete(Publisher) and unlink(Publisher) only deleted the first key.
Original Pull Request: #379
We now consider the property type in value conversion for constructor arguments to provide the appropriate value type. Previously, we considered the component type which converted a Collection<T> to a single T that caused downstream ClassCastException.
Original Pull Request: #378
We now expose registerNullValueSerializer(…) on GenericJackson2JsonRedisSerializer to allow customization of an externally provided ObjectMapper. NullValueSerializer was previously registered only within a constructor that takes classPropertyTypeName and provided no further possibilities to customize ObjectMapper.
Code wishing to provide a custom ObjectMapper can now call GenericJackson2JsonRedisSerializer.registerNullValueSerializer(objectMapper, …) and perform additional customizations before using ObjectMapper with GenericJackson2JsonRedisSerializer.
Original Pull Request: #376
Stream subscriptions now report reliably their state reflecting regarding activation, cancellation and while being active.
Previously, registering a subscription with immediate cancel could report an inactive subscription although StreamMessageListenerContainer could perform a stream read.
When activating a subscription in StreamMessageListenerContainer, awaiting activation (awaitStart(…)), immediately cancelling the subscription, and reading active state via Subscription.isActive(), Subscription.isActive() could report in this case false. This was because we checked that the status was either running or the stream read has reached event looping. The state was CANCELLED and the task thread had not yet reached the event loop and so there was a gap.
Original Pull Request: #377
We now connect asynchronously to expose non-blocking connection behavior for reactive API usage. We also use a non-blocking, asynchronous pool implementation for connections that are used through a reactive API. Shared connection usage, which is blocking on the very first access can be pre-initialized during connection factory initialization instead of initialization on first access:
LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory();
connectionFactory.setEagerInitialization(true);
connectionFactory.afterPropertiesSet();
We no longer require offloading of blocking connects to a dedicated scheduler which makes reactive API usage fully non-blocking.
Original Pull Request: #315
We now shuffle the set of Redis Cluster nodes before retrieving the topology. This change reduces load on the first node in the known nodes set.
Original pull request: #373.
Exract nested Stream types into connection.stream package. Extract common code from Template-API based toObjectRecord handling into StreamObjectMapper.
StreamReceiver and StreamMessageListenerContainer now can emit MapRecord or ObjectRecord for easier consumption of stream records.
Original Pull Request: #356
Move API so the stream is more like the hash having dedicated serializers for fields and their values. Also allow appending simple types such as string directly to a stream without having to go through creating a Map. Those simple types can also be read back.
Provide means to hash complex objects when added to the stream.
Update Documentation.
Original Pull Request: #356
We now support Redis Streams to add, read and consume stream records. We introduced StreamOperations, BoundStreamOperations, and ReactiveStreamOperations to interact with Redis Streams using imperative and reactive programming models. Record represents items within a stream. There are various flavors of Stream Records:
* MapRecord (maps to the hash body used in stream messages).
* Binary MapRecord: byte[] and ByteBuffer variants of MapRecord.
* ObjectRecord: Simple and Complex Objects mapped onto the stream body hash using ObjectHashMapper.
Redis Streams are supported for the Lettuce client only as Jedis has not received yet Redis Stream support.
Messages can be created as Map or as object:
redisTemplate.opsForStream().add("my-stream", Collections.singletonMap("key", "value"));
redisTemplate.opsForStream().add(ObjectRecord.create("my-logins", new LoginEvent(…)));
Streams can be consumed by using a StreamMessageListenerContainer that allows for stream subscriptions or a StreamReceiver.
Synchronous Message Listener:
StreamMessageListenerContainer<String, MapRecord<String, String, String>> container = StreamMessageListenerContainer
.create(connectionFactory);
container.start();
Subscription subscription = container.receive(StreamOffset.fromStart("my-stream"), record -> … );
Reactive Message Receiver:
StreamReceiverOptions<String, ObjectRecord<String, LoginEvent>> receiverOptions = StreamReceiverOptions.builder()
.targetType(LoginEvent.class).build();
StreamReceiver<String, ObjectRecord<String, LoginEvent>> receiver = StreamReceiver.create(connectionFactory, receiverOptions);
Flux<ObjectRecord<String, LoginEvent>> messages = receiver.receive(StreamOffset.fromStart("my-logins"));
Original Pull Request: #356
Create unit tests for RedisAtomic counter initialization. Refactor setIfAbsent(…) method to initializeIfAbsent() to not expose additional API methods.
Original pull request: #367.
LettuceClientConfigurationBuilder now allows configuration of the quiet shutdown period to improve shutdown time of the Netty EventLoop when using non-shared ClientResources.
Original pull request: #363.
This commit adds the following public methods to RedisAtomicInteger,
RedisAtomicLong, and RedisAtomicDouble:
T accumulateAndGet(T updateValue, BinaryOperator<T> accumulatorFunction);
T getAndAccumulate(T updateValue, BinaryOperator<T> accumulatorFunction);
T updateAndGet(UnaryOperator<T> updateFunction);
T getAndUpdate(UnaryOperator<T> updateFunction);
These methods are primarily useful for doing CAS operations with the
help of Binary/UnaryOperators to generate the new value. They mirror
their counterparts on the AtomicInteger and AtomicLong classes
available in the java.util.concurrent.atomic package.
Original pull request: #362.
We now renamed RedisStaticMasterSlaveConfiguration and StaticMasterSlaveConnectionProvider to RedisStaticMasterReplicaConfiguration respective StaticMasterReplicaConnectionProvider to reflect changes in Redis nomenclature regarding replication.
Original Pull Request: #355
Refactor lower/upper bound retrieval into LettuceConverters for a consistent lower/upper boundary return value. Use minus one (-1) as the upper bound index to always retrieve the last element if the upper boundary is not bounded.
Original pull request: #353.
We now return in List, String, and ZSet commands a lower/upper bound range index to prevent NullPointerException. Previously, unbounded ranges could render null values that were attempted to cast to primitives.
Original pull request: #353.
We now consider a transactional rollback that returns an empty EXEC result as rollback for the CAS (compare-and-set) operation. Previously, we checked only that the response of EXEC is not null. A rollback returns an empty list which was previously considered a successful CAS operation.
The code for CAS is now extracted to CompareAndSet and is reused from RedisAtomic implementations.
Original Pull Request: #349
We now support immutable objects for saving (e.g. object without a provided Id) and loading (i.e. persistence constructor declares a subset of properties). New instances are created using wither methods/Kotlin copy(…) methods if an immutable object requires association with an Id.
Association of the Id to its object moved from RedisKeyValueAdapter to RedisKeyValueTemplate.
We now catch exceptions that may occur during unsubscription from the subscribed patterns/channels.
Previously, RuntimeExceptions were not caught and prevented a fast shutdown of the ApplicationContext.
Original Pull Request: #346
We now support IsTrue and IsFalse keywords usage in repository query methods to create queries with predicates for boolean fields values without requiring to pass a boolean argument.
class Person {
@Id String id;
@Indexed Boolean alive;
}
interface PersonRepository extends Repository<Person, String> {
Person findByAliveIsTrue();
Person findByAliveIsFalse();
}
Original Pull Request: #342
We now release the native connection back to the connection provider when LettuceSubscription is closed. Previously, we just closed the connection which interfered with pooling as pooling connection providers still had a reference on the connection.
Original Pull Request: #341
We now provide reactive SCAN support for keys, hashes, sets, and sorted sets. Scanning uses a cursor-based Flux and takes subscriber demand into account to issue the according scan commands once a batch of keys/values/entries was emitted. Specifying a SCAN limit controls the batch (page) size and can be used to optimize the number of roundtrips between the application and Redis.
Original Pull Request: #343
Move BitfieldCommand to top-level type BitFieldSubCommands.
Use primitives in command objects. Add toString methods. Add utility methods to extract responses from Jedis Client. Use Connection.execute(…) to invoke BITFIELD using Jedis. Fix Overflow values to string rendering.
Convert test ticket references to new format.
Original pull request: #227.
We now offer support for BITFIELD via RedisConnection and RedisClusterConnection using Lettuce and Jedis.
connection.bitField(key,
create()
.set(INT_8).valueAt(offset(0).multipliedByTypeLength()).to(100)
.incr(signed(8)).valueAt(offset(102)).overflow(FAIL).by(1));
Original pull request: #227.