package org.infinispan.client.hotrod.retry; import static org.infinispan.server.hotrod.test.HotRodTestingUtil.hotRodCacheConfiguration; import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertFalse; import static org.testng.AssertJUnit.assertNull; import static org.testng.AssertJUnit.assertTrue; import org.infinispan.client.hotrod.VersionedValue; import org.infinispan.client.hotrod.test.HotRodClientTestingUtil; import org.infinispan.configuration.cache.CacheMode; import org.infinispan.configuration.cache.ConfigurationBuilder; import org.infinispan.notifications.Listener; import org.infinispan.notifications.cachelistener.annotation.CacheEntryCreated; import org.infinispan.notifications.cachelistener.annotation.CacheEntryModified; import org.infinispan.notifications.cachelistener.annotation.CacheEntryRemoved; import org.infinispan.notifications.cachelistener.event.CacheEntryEvent; import org.infinispan.remoting.transport.jgroups.SuspectException; import org.infinispan.transaction.TransactionMode; import org.infinispan.util.ControlledConsistentHashFactory; import org.infinispan.util.concurrent.IsolationLevel; import org.testng.annotations.Test; /** * Tests that force operations to be directed to a server that applies the * change locally but fails to send it to other nodes. The failover mechanism * in the Hot Rod client will determine a different server and retry. */ @Test(groups = "functional", testName = "client.hotrod.retry.ServerFailureRetrySingleOwnerTest") public class ServerFailureRetrySingleOwnerTest extends AbstractRetryTest { public ServerFailureRetrySingleOwnerTest() { cleanup = CleanupPhase.AFTER_TEST; } @Override protected ConfigurationBuilder getCacheConfig() { ConfigurationBuilder builder = hotRodCacheConfiguration( getDefaultClusteredCacheConfig(CacheMode.DIST_SYNC, false)); builder.clustering().hash().numOwners(1).numSegments(1) .consistentHashFactory(new ControlledConsistentHashFactory(0)) .transaction().transactionMode(TransactionMode.TRANSACTIONAL).useSynchronization(true) .locking().isolationLevel(IsolationLevel.READ_COMMITTED); return builder; } public void testRetryReplaceWithVersion() { final ErrorInducingListener listener = new ErrorInducingListener(); final byte[] key = HotRodClientTestingUtil.getKeyForServer(hotRodServer1); assertNull(remoteCache.putIfAbsent(key, 1)); final VersionedValue versioned = remoteCache.getVersioned(key); assertEquals(1, versioned.getValue()); withListener(listener, () -> { assertFalse(listener.errorInduced); assertEquals(true, remoteCache.replaceWithVersion(key, 2, versioned.getVersion())); assertTrue(listener.errorInduced); assertEquals(2, remoteCache.get(key)); }); } public void testRetryRemoveWithVersion() { final ErrorInducingListener listener = new ErrorInducingListener(); final byte[] key = HotRodClientTestingUtil.getKeyForServer(hotRodServer1); assertNull(remoteCache.putIfAbsent(key, 1)); final VersionedValue versioned = remoteCache.getVersioned(key); assertEquals(1, versioned.getValue()); withListener(listener, () -> { assertFalse(listener.errorInduced); assertEquals(true, remoteCache.removeWithVersion(key, versioned.getVersion())); assertTrue(listener.errorInduced); assertNull(remoteCache.get(key)); }); } public void testRetryRemove() { final ErrorInducingListener listener = new ErrorInducingListener(); final byte[] key = HotRodClientTestingUtil.getKeyForServer(hotRodServer1); assertNull(remoteCache.putIfAbsent(key, 1)); withListener(listener, () -> { assertFalse(listener.errorInduced); assertEquals(1, remoteCache.remove(key)); assertTrue(listener.errorInduced); assertNull(remoteCache.get(key)); }); } public void testRetryReplace() { final ErrorInducingListener listener = new ErrorInducingListener(); final byte[] key = HotRodClientTestingUtil.getKeyForServer(hotRodServer1); assertNull(remoteCache.putIfAbsent(key, 1)); withListener(listener, () -> { assertFalse(listener.errorInduced); assertEquals(1, remoteCache.replace(key, 2)); assertTrue(listener.errorInduced); assertEquals(2, remoteCache.get(key)); }); } public void testRetryPutIfAbsent() { final ErrorInducingListener listener = new ErrorInducingListener(); final byte[] key = HotRodClientTestingUtil.getKeyForServer(hotRodServer1); withListener(listener, () -> { assertFalse(listener.errorInduced); assertNull(remoteCache.putIfAbsent(key, 1)); assertTrue(listener.errorInduced); assertEquals(1, remoteCache.get(key)); }); } public void testRetryPutOnNonEmpty() { final ErrorInducingListener listener = new ErrorInducingListener(); final byte[] key = HotRodClientTestingUtil.getKeyForServer(hotRodServer1); assertNull(remoteCache.put(key, 1)); withListener(listener, () -> { assertFalse(listener.errorInduced); assertEquals(1, remoteCache.put(key, 2)); assertTrue(listener.errorInduced); assertEquals(2, remoteCache.get(key)); }); } public void testRetryPutOnEmpty() { final ErrorInducingListener listener = new ErrorInducingListener(); final byte[] key = HotRodClientTestingUtil.getKeyForServer(hotRodServer1); withListener(listener, () -> { assertFalse(listener.errorInduced); assertNull(remoteCache.put(key, 1)); assertTrue(listener.errorInduced); assertEquals(1, remoteCache.get(key)); }); } private void withListener(Object listener, Runnable r) { hotRodServer1.getCacheManager().getCache().addListener(listener); try { r.run(); } finally { hotRodServer1.getCacheManager().getCache().removeListener(listener); } } @Listener public static class ErrorInducingListener { boolean errorInduced; @CacheEntryCreated @CacheEntryModified @CacheEntryRemoved @SuppressWarnings("unused") public void handleEvent(CacheEntryEvent event) throws Exception { if (!event.isPre() && event.isOriginLocal()) { errorInduced = true; throw new SuspectException("Simulated suspicion"); } } } }