RedissonReactive.createTransaction method added. #1372
parent
220370e47d
commit
c3655e5ef4
@ -0,0 +1,157 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2018 Nikita Koksharov
|
||||||
|
*
|
||||||
|
* 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.redisson.api;
|
||||||
|
|
||||||
|
import org.reactivestreams.Publisher;
|
||||||
|
import org.redisson.client.codec.Codec;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Transaction object allows to execute transactions over Redisson objects.
|
||||||
|
* Uses locks for write operations and maintains data modification operations list till the commit/rollback operation.
|
||||||
|
* <p>
|
||||||
|
* Transaction isolation level: <b>READ_COMMITTED</b>
|
||||||
|
*
|
||||||
|
* @author Nikita Koksharov
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface RTransactionReactive {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns transactional object holder instance by name.
|
||||||
|
*
|
||||||
|
* @param <V> type of value
|
||||||
|
* @param name - name of object
|
||||||
|
* @return Bucket object
|
||||||
|
*/
|
||||||
|
<V> RBucketReactive<V> getBucket(String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns transactional object holder instance by name
|
||||||
|
* using provided codec for object.
|
||||||
|
*
|
||||||
|
* @param <V> type of value
|
||||||
|
* @param name - name of object
|
||||||
|
* @param codec - codec for values
|
||||||
|
* @return Bucket object
|
||||||
|
*/
|
||||||
|
<V> RBucketReactive<V> getBucket(String name, Codec codec);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns transactional map instance by name.
|
||||||
|
*
|
||||||
|
* @param <K> type of key
|
||||||
|
* @param <V> type of value
|
||||||
|
* @param name - name of object
|
||||||
|
* @return Map object
|
||||||
|
*/
|
||||||
|
<K, V> RMapReactive<K, V> getMap(String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns transactional map instance by name
|
||||||
|
* using provided codec for both map keys and values.
|
||||||
|
*
|
||||||
|
* @param <K> type of key
|
||||||
|
* @param <V> type of value
|
||||||
|
* @param name - name of object
|
||||||
|
* @param codec - codec for keys and values
|
||||||
|
* @return Map object
|
||||||
|
*/
|
||||||
|
<K, V> RMapReactive<K, V> getMap(String name, Codec codec);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns transactional set instance by name.
|
||||||
|
*
|
||||||
|
* @param <V> type of value
|
||||||
|
* @param name - name of object
|
||||||
|
* @return Set object
|
||||||
|
*/
|
||||||
|
<V> RSetReactive<V> getSet(String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns transactional set instance by name
|
||||||
|
* using provided codec for set objects.
|
||||||
|
*
|
||||||
|
* @param <V> type of value
|
||||||
|
* @param name - name of object
|
||||||
|
* @param codec - codec for values
|
||||||
|
* @return Set object
|
||||||
|
*/
|
||||||
|
<V> RSetReactive<V> getSet(String name, Codec codec);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns transactional set-based cache instance by <code>name</code>.
|
||||||
|
* Supports value eviction with a given TTL value.
|
||||||
|
*
|
||||||
|
* <p>If eviction is not required then it's better to use regular map {@link #getSet(String)}.</p>
|
||||||
|
*
|
||||||
|
* @param <V> type of value
|
||||||
|
* @param name - name of object
|
||||||
|
* @return SetCache object
|
||||||
|
*/
|
||||||
|
<V> RSetCacheReactive<V> getSetCache(String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns transactional set-based cache instance by <code>name</code>.
|
||||||
|
* Supports value eviction with a given TTL value.
|
||||||
|
*
|
||||||
|
* <p>If eviction is not required then it's better to use regular map {@link #getSet(String, Codec)}.</p>
|
||||||
|
*
|
||||||
|
* @param <V> type of value
|
||||||
|
* @param name - name of object
|
||||||
|
* @param codec - codec for values
|
||||||
|
* @return SetCache object
|
||||||
|
*/
|
||||||
|
<V> RSetCacheReactive<V> getSetCache(String name, Codec codec);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns transactional map-based cache instance by name.
|
||||||
|
* Supports entry eviction with a given MaxIdleTime and TTL settings.
|
||||||
|
* <p>
|
||||||
|
* If eviction is not required then it's better to use regular map {@link #getMap(String)}.</p>
|
||||||
|
*
|
||||||
|
* @param <K> type of key
|
||||||
|
* @param <V> type of value
|
||||||
|
* @param name - name of object
|
||||||
|
* @return MapCache object
|
||||||
|
*/
|
||||||
|
<K, V> RMapCacheReactive<K, V> getMapCache(String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns transactional map-based cache instance by <code>name</code>
|
||||||
|
* using provided <code>codec</code> for both cache keys and values.
|
||||||
|
* Supports entry eviction with a given MaxIdleTime and TTL settings.
|
||||||
|
* <p>
|
||||||
|
* If eviction is not required then it's better to use regular map {@link #getMap(String, Codec)}.
|
||||||
|
*
|
||||||
|
* @param <K> type of key
|
||||||
|
* @param <V> type of value
|
||||||
|
* @param name - object name
|
||||||
|
* @param codec - codec for keys and values
|
||||||
|
* @return MapCache object
|
||||||
|
*/
|
||||||
|
<K, V> RMapCacheReactive<K, V> getMapCache(String name, Codec codec);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Commits all changes made on this transaction.
|
||||||
|
*/
|
||||||
|
Publisher<Void> commit();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Rollback all changes made on this transaction.
|
||||||
|
*/
|
||||||
|
Publisher<Void> rollback();
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,119 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2018 Nikita Koksharov
|
||||||
|
*
|
||||||
|
* 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.redisson.reactive;
|
||||||
|
|
||||||
|
import org.reactivestreams.Publisher;
|
||||||
|
import org.redisson.api.RBucketReactive;
|
||||||
|
import org.redisson.api.RFuture;
|
||||||
|
import org.redisson.api.RMapCacheReactive;
|
||||||
|
import org.redisson.api.RMapReactive;
|
||||||
|
import org.redisson.api.RSetCacheReactive;
|
||||||
|
import org.redisson.api.RSetReactive;
|
||||||
|
import org.redisson.api.RTransaction;
|
||||||
|
import org.redisson.api.RTransactionReactive;
|
||||||
|
import org.redisson.api.TransactionOptions;
|
||||||
|
import org.redisson.client.codec.Codec;
|
||||||
|
import org.redisson.command.CommandReactiveExecutor;
|
||||||
|
import org.redisson.transaction.RedissonTransaction;
|
||||||
|
|
||||||
|
import reactor.fn.Supplier;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Nikita Koksharov
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public class RedissonTransactionReactive implements RTransactionReactive {
|
||||||
|
|
||||||
|
private final RTransaction transaction;
|
||||||
|
private final CommandReactiveExecutor executorService;
|
||||||
|
|
||||||
|
public RedissonTransactionReactive(CommandReactiveExecutor executorService, TransactionOptions options) {
|
||||||
|
this.transaction = new RedissonTransaction(executorService, options);
|
||||||
|
this.executorService = executorService;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <V> RBucketReactive<V> getBucket(String name) {
|
||||||
|
return new RedissonBucketReactive<V>(executorService, name, transaction.<V>getBucket(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <V> RBucketReactive<V> getBucket(String name, Codec codec) {
|
||||||
|
return new RedissonBucketReactive<V>(codec, executorService, name, transaction.<V>getBucket(name, codec));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <K, V> RMapReactive<K, V> getMap(String name) {
|
||||||
|
return new RedissonMapReactive<K, V>(executorService, name, null, transaction.<K, V>getMap(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <K, V> RMapReactive<K, V> getMap(String name, Codec codec) {
|
||||||
|
return new RedissonMapReactive<K, V>(codec, executorService, name, null, transaction.<K, V>getMap(name, codec));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <K, V> RMapCacheReactive<K, V> getMapCache(String name, Codec codec) {
|
||||||
|
return new RedissonMapCacheReactive<K, V>(codec, executorService, name, null, transaction.<K, V>getMapCache(name, codec));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <K, V> RMapCacheReactive<K, V> getMapCache(String name) {
|
||||||
|
return new RedissonMapCacheReactive<K, V>(executorService, name, null, transaction.<K, V>getMapCache(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <V> RSetReactive<V> getSet(String name) {
|
||||||
|
return new RedissonSetReactive<V>(executorService, name, transaction.<V>getSet(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <V> RSetReactive<V> getSet(String name, Codec codec) {
|
||||||
|
return new RedissonSetReactive<V>(codec, executorService, name, transaction.<V>getSet(name, codec));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <V> RSetCacheReactive<V> getSetCache(String name) {
|
||||||
|
return new RedissonSetCacheReactive<V>(executorService, name, transaction.<V>getSetCache(name));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <V> RSetCacheReactive<V> getSetCache(String name, Codec codec) {
|
||||||
|
return new RedissonSetCacheReactive<V>(codec, executorService, name, transaction.<V>getSetCache(name, codec));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Publisher<Void> commit() {
|
||||||
|
return new NettyFuturePublisher<Void>(new Supplier<RFuture<Void>>() {
|
||||||
|
@Override
|
||||||
|
public RFuture<Void> get() {
|
||||||
|
return transaction.commitAsync();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Publisher<Void> rollback() {
|
||||||
|
return new NettyFuturePublisher<Void>(new Supplier<RFuture<Void>>() {
|
||||||
|
@Override
|
||||||
|
public RFuture<Void> get() {
|
||||||
|
return transaction.rollbackAsync();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,148 @@
|
|||||||
|
package org.redisson.transaction;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.junit.Assert;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.redisson.BaseReactiveTest;
|
||||||
|
import org.redisson.api.RBucketReactive;
|
||||||
|
import org.redisson.api.RTransactionReactive;
|
||||||
|
import org.redisson.api.TransactionOptions;
|
||||||
|
|
||||||
|
public class RedissonTransactionalBucketReactiveTest extends BaseReactiveTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTimeout() throws InterruptedException {
|
||||||
|
RBucketReactive<String> b = redisson.getBucket("test");
|
||||||
|
sync(b.set("123"));
|
||||||
|
|
||||||
|
RTransactionReactive transaction = redisson.createTransaction(TransactionOptions.defaults().timeout(3, TimeUnit.SECONDS));
|
||||||
|
RBucketReactive<String> bucket = transaction.getBucket("test");
|
||||||
|
sync(bucket.set("234"));
|
||||||
|
|
||||||
|
Thread.sleep(3000);
|
||||||
|
|
||||||
|
try {
|
||||||
|
sync(transaction.commit());
|
||||||
|
Assert.fail();
|
||||||
|
} catch (TransactionException e) {
|
||||||
|
// skip
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread.sleep(1000);
|
||||||
|
|
||||||
|
assertThat(sync(b.get())).isEqualTo("123");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSet() {
|
||||||
|
RBucketReactive<String> b = redisson.getBucket("test");
|
||||||
|
sync(b.set("123"));
|
||||||
|
|
||||||
|
RTransactionReactive transaction = redisson.createTransaction(TransactionOptions.defaults());
|
||||||
|
RBucketReactive<String> bucket = transaction.getBucket("test");
|
||||||
|
sync(bucket.set("234"));
|
||||||
|
assertThat(sync(bucket.get())).isEqualTo("234");
|
||||||
|
|
||||||
|
sync(transaction.commit());
|
||||||
|
|
||||||
|
assertThat(sync(redisson.getKeys().count())).isEqualTo(1);
|
||||||
|
assertThat(sync(b.get())).isEqualTo("234");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAndSet() {
|
||||||
|
RBucketReactive<String> b = redisson.getBucket("test");
|
||||||
|
sync(b.set("123"));
|
||||||
|
|
||||||
|
RTransactionReactive transaction = redisson.createTransaction(TransactionOptions.defaults());
|
||||||
|
RBucketReactive<String> bucket = transaction.getBucket("test");
|
||||||
|
assertThat(sync(bucket.getAndSet("0"))).isEqualTo("123");
|
||||||
|
assertThat(sync(bucket.get())).isEqualTo("0");
|
||||||
|
assertThat(sync(bucket.getAndSet("324"))).isEqualTo("0");
|
||||||
|
|
||||||
|
sync(transaction.commit());
|
||||||
|
|
||||||
|
assertThat(sync(redisson.getKeys().count())).isEqualTo(1);
|
||||||
|
assertThat(sync(b.get())).isEqualTo("324");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testCompareAndSet() {
|
||||||
|
RBucketReactive<String> b = redisson.getBucket("test");
|
||||||
|
sync(b.set("123"));
|
||||||
|
|
||||||
|
RTransactionReactive transaction = redisson.createTransaction(TransactionOptions.defaults());
|
||||||
|
RBucketReactive<String> bucket = transaction.getBucket("test");
|
||||||
|
assertThat(sync(bucket.compareAndSet("0", "434"))).isFalse();
|
||||||
|
assertThat(sync(bucket.get())).isEqualTo("123");
|
||||||
|
assertThat(sync(bucket.compareAndSet("123", "232"))).isTrue();
|
||||||
|
assertThat(sync(bucket.get())).isEqualTo("232");
|
||||||
|
|
||||||
|
sync(transaction.commit());
|
||||||
|
|
||||||
|
assertThat(sync(redisson.getKeys().count())).isEqualTo(1);
|
||||||
|
assertThat(sync(b.get())).isEqualTo("232");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTrySet() {
|
||||||
|
RBucketReactive<String> b = redisson.getBucket("test");
|
||||||
|
sync(b.set("123"));
|
||||||
|
|
||||||
|
RTransactionReactive transaction = redisson.createTransaction(TransactionOptions.defaults());
|
||||||
|
RBucketReactive<String> bucket = transaction.getBucket("test");
|
||||||
|
assertThat(sync(bucket.trySet("0"))).isFalse();
|
||||||
|
assertThat(sync(bucket.delete())).isTrue();
|
||||||
|
assertThat(sync(bucket.trySet("324"))).isTrue();
|
||||||
|
assertThat(sync(bucket.trySet("43"))).isFalse();
|
||||||
|
|
||||||
|
sync(transaction.commit());
|
||||||
|
|
||||||
|
assertThat(sync(redisson.getKeys().count())).isEqualTo(1);
|
||||||
|
assertThat(sync(b.get())).isEqualTo("324");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetAndRemove() {
|
||||||
|
RBucketReactive<String> m = redisson.getBucket("test");
|
||||||
|
sync(m.set("123"));
|
||||||
|
|
||||||
|
RTransactionReactive transaction = redisson.createTransaction(TransactionOptions.defaults());
|
||||||
|
RBucketReactive<String> set = transaction.getBucket("test");
|
||||||
|
assertThat(sync(set.get())).isEqualTo("123");
|
||||||
|
assertThat(sync(set.size())).isEqualTo(5);
|
||||||
|
assertThat(sync(set.getAndDelete())).isEqualTo("123");
|
||||||
|
assertThat(sync(set.size())).isEqualTo(0);
|
||||||
|
assertThat(sync(set.get())).isNull();
|
||||||
|
assertThat(sync(set.getAndDelete())).isNull();
|
||||||
|
|
||||||
|
sync(transaction.commit());
|
||||||
|
|
||||||
|
assertThat(sync(redisson.getKeys().count())).isEqualTo(0);
|
||||||
|
assertThat(sync(m.get())).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testRollback() {
|
||||||
|
RBucketReactive<Object> b = redisson.getBucket("test");
|
||||||
|
sync(b.set("1234"));
|
||||||
|
|
||||||
|
RTransactionReactive transaction = redisson.createTransaction(TransactionOptions.defaults());
|
||||||
|
RBucketReactive<Object> bucket = transaction.getBucket("test");
|
||||||
|
assertThat(sync(bucket.get())).isEqualTo("1234");
|
||||||
|
assertThat(sync(bucket.getAndDelete())).isEqualTo("1234");
|
||||||
|
|
||||||
|
assertThat(sync(b.get())).isEqualTo("1234");
|
||||||
|
|
||||||
|
sync(transaction.rollback());
|
||||||
|
|
||||||
|
assertThat(sync(redisson.getKeys().count())).isEqualTo(1);
|
||||||
|
|
||||||
|
assertThat(sync(b.get())).isEqualTo("1234");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue