public NonBlockingIdentityHashMap<Long, TestKey> getMapMultithreaded() throws InterruptedException, ExecutionException { final int threadCount = _items.keySet().size(); final NonBlockingIdentityHashMap<Long, TestKey> map = new NonBlockingIdentityHashMap<Long, TestKey>(); // use a barrier to open the gate for all threads at once to avoid rolling start and no actual // concurrency final CyclicBarrier barrier = new CyclicBarrier(threadCount); final ExecutorService ex = Executors.newFixedThreadPool(threadCount); final CompletionService<Integer> co = new ExecutorCompletionService<Integer>(ex); for (Integer type : _items.keySet()) { // A linked-list of things to insert List<TestKey> items = _items.get(type); TestKeyFeederThread feeder = new TestKeyFeederThread(type, items, map, barrier); co.submit(feeder); } // wait for all threads to return int itemCount = 0; for (int retCount = 0; retCount < threadCount; retCount++) { final Future<Integer> result = co.take(); itemCount += result.get(); } ex.shutdown(); return map; }
/** * Test concurrent reader and writer (GH-458). * * <p><b>Test case:</b> * * <ol> * <li/>start a long running reader; * <li/>try to start a writer: it should time out; * <li/>stop the reader; * <li/>start the writer again: it should succeed. * </ol> * * @throws Exception error during request execution */ @Test @Ignore("There is no way to stop a query on the server!") public void testReaderWriter() throws Exception { final String readerQuery = "?query=(1%20to%20100000000000000)%5b.=1%5d"; final String writerQuery = "/test.xml"; final byte[] content = Token.token("<a/>"); final Get readerAction = new Get(readerQuery); final Put writerAction = new Put(writerQuery, content); final ExecutorService exec = Executors.newFixedThreadPool(2); // start reader exec.submit(readerAction); Performance.sleep(TIMEOUT); // delay in order to be sure that the reader has started // start writer Future<HTTPResponse> writer = exec.submit(writerAction); try { final HTTPResponse result = writer.get(TIMEOUT, TimeUnit.MILLISECONDS); if (result.status.isSuccess()) fail("Database modified while a reader is running"); throw new Exception(result.toString()); } catch (final TimeoutException e) { // writer is blocked by the reader: stop it writerAction.stop = true; } // stop reader readerAction.stop = true; // start the writer again writer = exec.submit(writerAction); assertEquals(HTTPCode.CREATED, writer.get().status); }
@Test public void isDone_shouldReturnFalse_whenTheThreadIsFinished_givenAFutureReturnedByExecute() throws Exception { Future<?> result = asyncExecutor.execute(() -> {}); Thread.sleep(50L); // give other thread a chance to finish... assertTrue(result.isDone()); }
@Test public void get_shouldJoinTheCreatedThread_givenAFutureReturnedByExecute() throws Exception { long startTime = System.nanoTime(); Future<?> result = asyncExecutor.execute(suppressCheckedExceptions(() -> Thread.sleep(250L))); result.get(); long endTime = System.nanoTime(); assertThat(endTime - startTime, isGreaterThan(100 * 1000000L)); }
@Test public void get_shouldSucceed_whenTheThreadThrowsAnException_givenAFutureReturnedByExecute() throws Exception { Future<?> result = asyncExecutor.execute( () -> { throw new RuntimeException("blah"); }); result.get(100, TimeUnit.MILLISECONDS); }
@Test public void testGetAsync() throws Exception { HazelcastClient hClient = getHazelcastClient(); String key = "key"; String value1 = "value1"; IMap<String, String> map = hClient.getMap("map:test:getAsync"); map.put(key, value1); Future<String> f1 = map.getAsync(key); assertEquals(value1, f1.get()); }
@Test public void cancel_shouldThrowAnUnsupportedOperationException_givenAFutureReturnedByExecute() throws Exception { Future<?> result = asyncExecutor.execute(() -> {}); try { result.cancel(true); fail("Expected an exception"); } catch (UnsupportedOperationException ignore) { } }
@Test public void getWithATimeout_shouldThrowATimeoutException_whenTheThreadDoesNotEndQuickly_givenAFutureReturnedByExecute() throws Exception { Future<?> result = asyncExecutor.execute(suppressCheckedExceptions(() -> Thread.sleep(250L))); try { result.get(50, TimeUnit.MILLISECONDS); fail("Expected an exception"); } catch (TimeoutException ignore) { } }
@Test public void isDone_shouldReturnTrue_whenTheThreadIsAlive_givenAFutureReturnedByExecute() throws Exception { CountDownLatch latch = new CountDownLatch(1); Future<?> result = asyncExecutor.execute(suppressCheckedExceptions(latch::await)); try { assertFalse(result.isDone()); } finally { latch.countDown(); // don't want a thread waiting forever... } }
/** * Test 2 concurrent readers (GH-458). * * <p><b>Test case:</b> * * <ol> * <li/>start a long running reader; * <li/>start a fast reader: it should succeed. * </ol> * * @throws Exception error during request execution */ @Test public void testMultipleReaders() throws Exception { final String number = "63177"; final String slowQuery = "?query=(1%20to%20100000000000000)%5b.=1%5d"; final String fastQuery = "?query=" + number; final Get slowAction = new Get(slowQuery); final Get fastAction = new Get(fastQuery); final ExecutorService exec = Executors.newFixedThreadPool(2); exec.submit(slowAction); Performance.sleep(TIMEOUT); // delay in order to be sure that the reader has started final Future<HTTPResponse> fast = exec.submit(fastAction); try { final HTTPResponse result = fast.get(TIMEOUT, TimeUnit.MILLISECONDS); assertEquals(HTTPCode.OK, result.status); assertEquals(number, result.data); } finally { slowAction.stop = true; } }
/** * Test concurrent writers (GH-458). * * <p><b>Test case:</b> * * <ol> * <li/>start several writers one after another; * <li/>all writers should succeed. * </ol> * * @throws Exception error during request execution */ @Test public void testMultipleWriters() throws Exception { final int count = 10; final String template = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + "<command xmlns=\"http://basex.org/rest\"><text><![CDATA[" + "ADD TO %1$d <node id=\"%1$d\"/>" + "]]></text></command>"; @SuppressWarnings("unchecked") final Future<HTTPResponse>[] tasks = new Future[count]; final ExecutorService exec = Executors.newFixedThreadPool(count); // start all writers (not at the same time, but still in parallel) for (int i = 0; i < count; i++) { final String command = String.format(template, i); tasks[i] = exec.submit(new Post("", Token.token(command))); } // check if all have finished successfully for (final Future<HTTPResponse> task : tasks) { assertEquals(HTTPCode.OK, task.get(TIMEOUT, TimeUnit.MILLISECONDS).status); } }
@Test public void getWithATimeout_shouldSucceed_whenTheThreadEndsQuickly_givenAFutureReturnedByExecute() throws Exception { Future<?> result = asyncExecutor.execute(suppressCheckedExceptions(() -> Thread.sleep(50L))); result.get(100, TimeUnit.MILLISECONDS); }
@Test public void isCancelled_shouldReturnFalse_givenAFutureReturnedByExecute() throws Exception { Future<?> result = asyncExecutor.execute(() -> {}); assertFalse(result.isCancelled()); }
public void testGet() throws Exception { cache(0).put("myKey", "myValue"); // add an interceptor on node B that will block state transfer until we are ready final CountDownLatch applyStateProceedLatch = new CountDownLatch(1); final CountDownLatch applyStateStartedLatch = new CountDownLatch(1); cacheConfigBuilder .customInterceptors() .addInterceptor() .before(InvocationContextInterceptor.class) .interceptor( new CommandInterceptor() { @Override protected Object handleDefault(InvocationContext ctx, VisitableCommand cmd) throws Throwable { // if this 'put' command is caused by state transfer we block until GET begins if (cmd instanceof PutKeyValueCommand && ((PutKeyValueCommand) cmd).hasFlag(Flag.PUT_FOR_STATE_TRANSFER)) { // signal we encounter a state transfer PUT applyStateStartedLatch.countDown(); // wait until it is ok to apply state if (!applyStateProceedLatch.await(15, TimeUnit.SECONDS)) { throw new TimeoutException(); } } return super.handleDefault(ctx, cmd); } }); // add an interceptor on node B that will block GET commands until we are ready final CountDownLatch getKeyStartedLatch = new CountDownLatch(1); final CountDownLatch getKeyProceedLatch = new CountDownLatch(1); cacheConfigBuilder .customInterceptors() .addInterceptor() .before(CallInterceptor.class) .interceptor( new CommandInterceptor() { @Override protected Object handleDefault(InvocationContext ctx, VisitableCommand cmd) throws Throwable { if (cmd instanceof GetKeyValueCommand) { // signal we encounter a GET getKeyStartedLatch.countDown(); // wait until it is ok to continue with GET if (!getKeyProceedLatch.await(15, TimeUnit.SECONDS)) { throw new TimeoutException(); } } return super.handleDefault(ctx, cmd); } }); log.info("Adding a new node .."); addClusterEnabledCacheManager(cacheConfigBuilder); log.info("Added a new node"); // state transfer is blocked, no keys should be present on node B yet assertTrue(cache(1).getAdvancedCache().withFlags(Flag.CACHE_MODE_LOCAL).keySet().isEmpty()); // wait for state transfer on node B to progress to the point where data segments are about to // be applied if (!applyStateStartedLatch.await(15, TimeUnit.SECONDS)) { throw new TimeoutException(); } // state transfer is blocked, no keys should be present on node B yet assertTrue(cache(1).getAdvancedCache().withFlags(Flag.CACHE_MODE_LOCAL).keySet().isEmpty()); // initiate a GET Future<Object> getFuture = fork( new Callable<Object>() { @Override public Object call() { return cache(1).get("myKey"); } }); // wait for GET command on node B to reach beyond *DistributionInterceptor, where it will block. // the value seen so far is null if (!getKeyStartedLatch.await(15, TimeUnit.SECONDS)) { throw new TimeoutException(); } // allow state transfer to apply state applyStateProceedLatch.countDown(); // wait for state transfer to end TestingUtil.waitForRehashToComplete(cache(0), cache(1)); assertEquals(1, cache(1).getAdvancedCache().withFlags(Flag.CACHE_MODE_LOCAL).keySet().size()); // allow GET to continue getKeyProceedLatch.countDown(); Object value = getFuture.get(15, TimeUnit.SECONDS); assertEquals("myValue", value); }
public void testReplace() throws Exception { cache(0).put("myKey", "myValue"); // add an interceptor on second node that will block REPLACE commands right after // EntryWrappingInterceptor until we are ready final CountDownLatch replaceStartedLatch = new CountDownLatch(1); final CountDownLatch replaceProceedLatch = new CountDownLatch(1); boolean isVersioningEnabled = cache(0).getCacheConfiguration().versioning().enabled(); cacheConfigBuilder .customInterceptors() .addInterceptor() .after( isVersioningEnabled ? VersionedEntryWrappingInterceptor.class : EntryWrappingInterceptor.class) .interceptor( new CommandInterceptor() { @Override protected Object handleDefault(InvocationContext ctx, VisitableCommand cmd) throws Throwable { if (cmd instanceof ReplaceCommand) { // signal we encounter a REPLACE replaceStartedLatch.countDown(); // wait until it is ok to continue with REPLACE if (!replaceProceedLatch.await(15, TimeUnit.SECONDS)) { throw new TimeoutException(); } } return super.handleDefault(ctx, cmd); } }); // do not allow coordinator to send topology updates to node B final ClusterTopologyManager ctm0 = TestingUtil.extractGlobalComponent(manager(0), ClusterTopologyManager.class); ctm0.setRebalancingEnabled(false); log.info("Adding a new node .."); addClusterEnabledCacheManager(cacheConfigBuilder); log.info("Added a new node"); // node B is not a member yet and rebalance has not started yet CacheTopology cacheTopology = advancedCache(1).getComponentRegistry().getStateTransferManager().getCacheTopology(); assertNull(cacheTopology.getPendingCH()); assertTrue(cacheTopology.getMembers().contains(address(0))); assertFalse(cacheTopology.getMembers().contains(address(1))); assertFalse(cacheTopology.getCurrentCH().getMembers().contains(address(1))); // no keys should be present on node B yet because state transfer is blocked assertTrue(cache(1).keySet().isEmpty()); // initiate a REPLACE Future<Object> getFuture = fork( new Callable<Object>() { @Override public Object call() throws Exception { try { return cache(1).replace("myKey", "newValue"); } catch (Exception e) { log.errorf(e, "REPLACE failed: %s", e.getMessage()); throw e; } } }); // wait for REPLACE command on node B to reach beyond *EntryWrappingInterceptor, where it will // block. // the value seen so far is null if (!replaceStartedLatch.await(15, TimeUnit.SECONDS)) { throw new TimeoutException(); } // paranoia, yes the value is still missing from data container assertTrue(cache(1).keySet().isEmpty()); // allow rebalance to start ctm0.setRebalancingEnabled(true); // wait for state transfer to end TestingUtil.waitForRehashToComplete(cache(0), cache(1)); // the state should be already transferred now assertEquals(1, cache(1).keySet().size()); // allow REPLACE to continue replaceProceedLatch.countDown(); Object oldVal = getFuture.get(15, TimeUnit.SECONDS); assertNotNull(oldVal); assertEquals("myValue", oldVal); assertEquals("newValue", cache(0).get("myKey")); assertEquals("newValue", cache(1).get("myKey")); }