package org.infinispan.distexec;
import static org.infinispan.test.Exceptions.expectException;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertTrue;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import org.infinispan.AdvancedCache;
import org.infinispan.Cache;
import org.infinispan.configuration.cache.CacheMode;
import org.infinispan.configuration.cache.ConfigurationBuilder;
import org.infinispan.marshall.core.ExternalPojo;
import org.infinispan.remoting.transport.Address;
import org.testng.annotations.Test;
/**
* Tests org.infinispan.distexec.DistributedExecutorService
*
* @author Vladimir Blagojevic
* @author Anna Manukyan
*/
@Test(groups = {"functional", "smoke"}, testName = "distexec.DistributedExecutorTest")
public class DistributedExecutorTest extends LocalDistributedExecutorTest {
public DistributedExecutorTest() {
cleanup = CleanupPhase.AFTER_METHOD;
}
@Override
protected void createCacheManagers() throws Throwable {
ConfigurationBuilder builder = getDefaultClusteredCacheConfig(getCacheMode(), false);
createClusteredCaches(2, cacheName(), builder);
}
protected String cacheName() {
return "DistributedExecutorTest-DIST_SYNC";
}
protected CacheMode getCacheMode() {
return CacheMode.DIST_SYNC;
}
protected Cache<Object, Object> getCache() {
return cache(0, cacheName());
}
public void testBasicTargetLocalDistributedCallableWithTimeout() throws Exception {
Cache<Object, Object> cache1 = getCache();
Cache<Object, Object> cache2 = cache(1, cacheName());
CyclicBarrier barrier = new CyclicBarrier(2);
cache2.getAdvancedCache().getComponentRegistry().registerComponent(barrier, "barrier");
// initiate task from cache1 and execute on same node
DistributedExecutorService des = createDES(cache1);
Address target = address(0);
DistributedTaskBuilder builder = des.createDistributedTaskBuilder(new SleepingSimpleCallable());
builder.timeout(1000, TimeUnit.MILLISECONDS);
Future<Integer> future = des.submit(target, builder.build());
expectException(ExecutionException.class, () -> future.get());
}
public void testBasicTargetRemoteDistributedCallableWithException() throws Exception {
Cache<Object, Object> cache1 = cache(0, cacheName());
Cache<Object, Object> cache2 = cache(1, cacheName());
// initiate task from cache1 and execute on same node
DistributedExecutorService des = createDES(cache1);
Address target = cache2.getAdvancedCache().getRpcManager().getAddress();
DistributedTaskBuilder builder = des
.createDistributedTaskBuilder(new ExceptionThrowingCallable());
Future<Integer> future = des.submit(target, builder.build());
expectException(ExecutionException.class, () -> future.get());
}
public void testBasicTargetLocalDistributedCallableWithHighFutureAndLowTaskTimeout() throws Exception {
Cache<Object, Object> cache1 = cache(0, cacheName());
// initiate task from cache1 and execute on same node
DistributedExecutorService des = createDES(cache1);
Address target = cache1.getAdvancedCache().getRpcManager().getAddress();
DistributedTaskBuilder builder = des
.createDistributedTaskBuilder(new SleepingSimpleCallable());
builder.timeout(1000, TimeUnit.MILLISECONDS);
Future<Integer> future = des.submit(target, builder.build());
expectException(TimeoutException.class, () -> future.get(10000, TimeUnit.MILLISECONDS));
}
public void testBasicTargetLocalDistributedCallableWithLowFutureAndHighTaskTimeout() throws Exception {
Cache<Object, Object> cache1 = cache(0, cacheName());
// initiate task from cache1 and execute on same node
DistributedExecutorService des = createDES(cache1);
Address target = cache1.getAdvancedCache().getRpcManager().getAddress();
DistributedTaskBuilder builder = des
.createDistributedTaskBuilder(new SleepingSimpleCallable());
builder.timeout(10000, TimeUnit.MILLISECONDS);
Future<Integer> future = des.submit(target, builder.build());
expectException(TimeoutException.class, () -> future.get(1000, TimeUnit.MILLISECONDS));
}
public void testBasicTargetRemoteDistributedCallableWithHighFutureAndLowTaskTimeout() throws Exception {
Cache<Object, Object> cache1 = cache(0, cacheName());
Cache<Object, Object> cache2 = cache(1, cacheName());
// initiate task from cache1 and execute on same node
DistributedExecutorService des = createDES(cache1);
Address target = cache2.getAdvancedCache().getRpcManager().getAddress();
DistributedTaskBuilder builder = des
.createDistributedTaskBuilder(new SleepingSimpleCallable());
builder.timeout(1000, TimeUnit.MILLISECONDS);
Future<Integer> future = des.submit(target, builder.build());
expectException(TimeoutException.class, () -> future.get(10000, TimeUnit.MILLISECONDS));
}
public void testBasicTargetRemoteDistributedCallableWithLowFutureAndHighTaskTimeout() throws Exception {
Cache<Object, Object> cache1 = cache(0, cacheName());
Cache<Object, Object> cache2 = cache(1, cacheName());
// initiate task from cache1 and execute on same node
DistributedExecutorService des = createDES(cache1);
Address target = cache2.getAdvancedCache().getRpcManager().getAddress();
DistributedTaskBuilder builder = des
.createDistributedTaskBuilder(new SleepingSimpleCallable());
builder.timeout(10000, TimeUnit.MILLISECONDS);
Future<Integer> future = des.submit(target, builder.build());
expectException(TimeoutException.class, () -> future.get(1000, TimeUnit.MILLISECONDS));
}
public void testBasicTargetLocalDistributedCallableWithoutSpecTimeout() throws Exception {
Cache<Object, Object> cache1 = cache(0, cacheName());
// initiate task from cache1 and execute on same node
DistributedExecutorService des = createDES(cache1);
Address target = cache1.getAdvancedCache().getRpcManager().getAddress();
DistributedTaskBuilder builder = des
.createDistributedTaskBuilder(new SleepingSimpleCallable());
Future<Integer> future = des.submit(target, builder.build());
assertEquals((Integer) 1, future.get());
}
public void testTaskCancellation() throws Exception {
CyclicBarrier barrier = new CyclicBarrier(2);
cache(1, cacheName()).getAdvancedCache().getComponentRegistry().registerComponent(barrier, "barrier");
DistributedExecutorService des = createDES(getCache());
List<Address> cacheMembers = getCache().getAdvancedCache().getRpcManager().getMembers();
assertEquals(caches(cacheName()).size(), cacheMembers.size());
DistributedTaskBuilder<Integer> tb = des.createDistributedTaskBuilder(new LongRunningCallable());
final Future<Integer> future = des.submit(address(1), tb.build());
// Will unblock when LongRunningCallable starts running
barrier.await(10, TimeUnit.SECONDS);
future.cancel(true);
boolean taskCancelled = false;
try {
future.get();
} catch (Exception e) {
taskCancelled = e instanceof CancellationException;
}
assert taskCancelled : "Dist task not cancelled ";
// Will unblock when LongRunningCallable is interrupted
barrier.await(10, TimeUnit.SECONDS);
assert future.isCancelled();
assert future.isDone();
//Testing whether the cancellation already happened.
boolean isCanceled = future.cancel(true);
assert !isCanceled;
}
public void testCancelAndGet() throws Exception {
DistributedExecutorService des = createDES(getCache());
List<Address> cacheMembers = getCache().getAdvancedCache().getRpcManager().getMembers();
List<Address> members = new ArrayList<>(cacheMembers);
assertEquals(caches(cacheName()).size(), members.size());
members.remove(getCache().getAdvancedCache().getRpcManager().getAddress());
DistributedTaskBuilder<Integer> tb = des.createDistributedTaskBuilder(new SleepingSimpleCallable());
final Future<Integer> future = des.submit(members.get(0),tb.build());
future.cancel(true);
expectException(CancellationException.class, () -> future.get());
}
public void testTimeoutOnLocalNode() throws Exception {
AdvancedCache<Object, Object> localCache = getCache().getAdvancedCache();
DistributedExecutorService des = createDES(localCache);
Future<Integer> future = des.submit(localCache.getRpcManager().getAddress(), new SleepingSimpleCallable());
expectException(TimeoutException.class, () -> future.get(1000, TimeUnit.MILLISECONDS));
}
public void testBasicTargetDistributedCallableTargetSameNode() throws Exception {
Cache<Object, Object> cache1 = getCache();
//initiate task from cache1 and select cache1 as target
DistributedExecutorService des = createDES(cache1);
Address target = cache1.getAdvancedCache().getRpcManager().getAddress();
Future<Boolean> future = des.submit(target, new SimpleDistributedCallable(false));
Boolean r = future.get();
assert r;
//the same using DistributedTask API
DistributedTaskBuilder<Boolean> taskBuilder = des.createDistributedTaskBuilder(new SimpleDistributedCallable(false));
DistributedTask<Boolean> distributedTask = taskBuilder.build();
future = des.submit(target, distributedTask);
r = future.get();
assert r;
}
public void testBasicTargetDistributedCallable() throws Exception {
Cache<Object, Object> cache1 = cache(0, cacheName());
Cache<Object, Object> cache2 = cache(1, cacheName());
// initiate task from cache1 and select cache2 as target
DistributedExecutorService des = createDES(cache1);
Address target = cache2.getAdvancedCache().getRpcManager().getAddress();
Future<Boolean> future = des.submit(target, new SimpleDistributedCallable(false));
Boolean r = future.get();
assert r;
// the same using DistributedTask API
DistributedTaskBuilder<Boolean> taskBuilder = des
.createDistributedTaskBuilder(new SimpleDistributedCallable(false));
DistributedTask<Boolean> distributedTask = taskBuilder.build();
future = des.submit(target, distributedTask);
r = future.get();
assert r;
}
public void testBasicTargetDistributedCallableWithTimeout() throws Exception {
Cache<Object, Object> cache1 = getCache();
// initiate task from cache1 and select cache2 as target
DistributedExecutorService des = createDES(cache1);
Address target = cache1.getAdvancedCache().getRpcManager().getAddress();
DistributedTaskBuilder<Integer> builder = des.createDistributedTaskBuilder(new SleepingSimpleCallable());
builder.timeout(10, TimeUnit.MILLISECONDS);
Future<Integer> future = des.submit(target, builder.build());
expectException(ExecutionException.class, () -> future.get());
}
public void testBasicTargetCallableWithNullTask() {
Cache<Object, Object> cache1 = getCache();
DistributedExecutorService des = createDES(cache1);
Address target = cache1.getAdvancedCache().getRpcManager().getAddress();
expectException(IllegalArgumentException.class, () -> des.submit(target, (Callable) null));
}
public void testBasicTargetDistributedTaskWithNullTask() {
Cache<Object, Object> cache1 = getCache();
DistributedExecutorService des = createDES(cache1);
Address target = cache1.getAdvancedCache().getRpcManager().getAddress();
expectException(NullPointerException.class, () -> des.submit(target, (DistributedTask) null));
}
public void testDistributedCallableEverywhereWithKeysOnBothNodes() throws Exception {
Cache<Object, Object> c1 = getCache();
c1.put("key1", "Manik");
c1.put("key2", "Mircea");
c1.put("key3", "Galder");
c1.put("key4", "Sanne");
Cache<Object, Object> c2 = cache(1, cacheName());
c2.put("key5", "test");
c2.put("key6", "test1");
DistributedExecutorService des = createDES(getCache());
List<CompletableFuture<Boolean>> list = des.submitEverywhere(new SimpleDistributedCallable(true),
"key1", "key2", "key5", "key6");
assertTrue(list != null && !list.isEmpty());
for (Future<Boolean> f : list) {
assert f.get();
}
// Check that the futures are distinct
assertEquals(list.size(), new HashSet<>(list).size());
//the same using DistributedTask API
DistributedTaskBuilder<Boolean> taskBuilder = des.createDistributedTaskBuilder(new SimpleDistributedCallable(true));
DistributedTask<Boolean> distributedTask = taskBuilder.build();
list = des.submitEverywhere(distributedTask, "key1", "key2");
assert list != null && !list.isEmpty();
for (Future<Boolean> f : list) {
assert f.get();
}
// Check that the futures are distinct
assertEquals(list.size(), new HashSet<>(list).size());
}
static class LongRunningCallable implements DistributedCallable<Object, Object, Integer>, Serializable, ExternalPojo {
/** The serialVersionUID */
private static final long serialVersionUID = -6110011263261397071L;
private Cache<Object, Object> cache;
@Override
public void setEnvironment(Cache<Object, Object> cache, Set<Object> inputKeys) {
this.cache = cache;
}
@Override
public Integer call() throws Exception {
CyclicBarrier barrier = cache.getAdvancedCache().getComponentRegistry().getComponent("barrier");
barrier.await(10, TimeUnit.SECONDS);
try {
TimeUnit.MILLISECONDS.sleep(5000);
} catch (InterruptedException e) {
//interrupted successfully
barrier.await(10, TimeUnit.SECONDS);
Thread.currentThread().interrupt();
}
return 1;
}
}
}