@Override public CompletableFuture<Void> deleteAllAsync(List<K> keys) { Preconditions.checkNotNull(writeFn, "null writeFn"); return writeFn.deleteAllAsync(keys); }
@Override public CompletableFuture<Void> deleteAsync(K key) { Preconditions.checkNotNull(writeFn, "null writeFn"); return writeFn.deleteAsync(key); }
@Override public CompletableFuture<Void> putAllAsync(List<Entry<K, V>> entries) { Preconditions.checkNotNull(writeFn, "null writeFn"); return writeFn.putAllAsync(entries); }
TableReadFunction readFn = mock(TableReadFunction.class); TableWriteFunction writeFn = mock(TableWriteFunction.class); doReturn(true).when(writeFn).isRetriable(any()); doReturn(CompletableFuture.completedFuture(null)).when(writeFn).putAsync(any(), any()); doReturn(CompletableFuture.completedFuture(null)).when(writeFn).putAllAsync(any()); doReturn(CompletableFuture.completedFuture(null)).when(writeFn).deleteAsync(any()); doReturn(CompletableFuture.completedFuture(null)).when(writeFn).deleteAllAsync(any()); AsyncReadWriteTable delegate = new AsyncRemoteTable(readFn, writeFn); AsyncRetriableTable table = new AsyncRetriableTable("t1", delegate, null, policy, schedExec, readFn, writeFn); table.init(TestRemoteTable.getMockContext()); verify(readFn, times(1)).init(any()); verify(writeFn, times(1)).init(any()); table.putAsync("foo", "bar").get(); verify(writeFn, times(1)).putAsync(any(), any()); assertEquals(++times, table.writeRetryMetrics.successCount.getCount()); table.putAllAsync(Arrays.asList(new Entry("1", "2"))).get(); verify(writeFn, times(1)).putAllAsync(any()); assertEquals(++times, table.writeRetryMetrics.successCount.getCount()); table.deleteAsync("1").get(); verify(writeFn, times(1)).deleteAsync(any()); assertEquals(++times, table.writeRetryMetrics.successCount.getCount()); table.deleteAllAsync(Arrays.asList("1", "2")).get(); verify(writeFn, times(1)).deleteAllAsync(any()); assertEquals(++times, table.writeRetryMetrics.successCount.getCount()); assertEquals(0, table.writeRetryMetrics.retryCount.getCount());
TableReadFunction<String, String> readFn = mock(TableReadFunction.class); TableWriteFunction<String, String> writeFn = mock(TableWriteFunction.class); doReturn(CompletableFuture.completedFuture(null)).when(writeFn).putAsync(any(), any()); doReturn(CompletableFuture.completedFuture(null)).when(writeFn).putAllAsync(any()); doReturn(CompletableFuture.completedFuture(null)).when(writeFn).deleteAsync(any()); doReturn(CompletableFuture.completedFuture(null)).when(writeFn).deleteAllAsync(any()); AsyncReadWriteTable delegate = new AsyncRemoteTable(readFn, writeFn); AsyncRateLimitedTable table = new AsyncRateLimitedTable("t1", delegate, verify(writeFn, times(1)).putAsync(any(), any()); verify(writeRateLimiter, times(1)).throttle(anyString(), anyString()); verify(writeRateLimiter, times(0)).throttleRecords(anyList()); verify(writeFn, times(1)).putAllAsync(any()); verify(writeRateLimiter, times(1)).throttle(anyString(), anyString()); verify(writeRateLimiter, times(1)).throttleRecords(anyList()); verify(writeFn, times(1)).deleteAsync(anyString()); verify(writeRateLimiter, times(1)).throttle(anyString(), anyString()); verify(writeRateLimiter, times(1)).throttleRecords(anyList()); verify(writeFn, times(1)).deleteAllAsync(any()); verify(writeRateLimiter, times(1)).throttle(anyString(), anyString()); verify(writeRateLimiter, times(1)).throttleRecords(anyList());
@Override public CompletableFuture<Void> putAsync(K key, V value) { Preconditions.checkNotNull(writeFn, "null writeFn"); return writeFn.putAsync(key, value); }
@Test public void testFirstTimeSuccessPut() throws Exception { String tableId = "testFirstTimeSuccessPut"; TableRetryPolicy policy = new TableRetryPolicy(); policy.withFixedBackoff(Duration.ofMillis(100)); TableWriteFunction<String, String> writeFn = mock(TableWriteFunction.class); doReturn(true).when(writeFn).isRetriable(any()); doReturn(CompletableFuture.completedFuture("bar")).when(writeFn).putAsync(anyString(), anyString()); RetriableWriteFunction<String, String> retryIO = new RetriableWriteFunction<>(policy, writeFn, schedExec); retryIO.setMetrics(getMetricsUtil(tableId)); retryIO.putAsync("foo", "bar").get(); verify(writeFn, times(1)).putAsync(anyString(), anyString()); Assert.assertEquals(0, retryIO.retryMetrics.retryCount.getCount()); Assert.assertEquals(1, retryIO.retryMetrics.successCount.getCount()); Assert.assertEquals(0, retryIO.retryMetrics.retryTimer.getSnapshot().getMax()); }
@Test public void testRetryEngagedPut() throws Exception { String tableId = "testRetryEngagedPut"; TableRetryPolicy policy = new TableRetryPolicy(); policy.withFixedBackoff(Duration.ofMillis(10)); TableWriteFunction<String, String> writeFn = mock(TableWriteFunction.class); doReturn(CompletableFuture.completedFuture(null)).when(writeFn).putAllAsync(any()); doReturn(true).when(writeFn).isRetriable(any()); int [] times = new int[] {0}; List<Entry<String, String>> records = new ArrayList<>(); records.add(new Entry<>("foo1", "bar1")); records.add(new Entry<>("foo2", "bar2")); doAnswer(invocation -> { CompletableFuture<Map<String, String>> future = new CompletableFuture(); if (times[0] > 0) { future.complete(null); } else { times[0]++; future.completeExceptionally(new RuntimeException("test exception")); } return future; }).when(writeFn).putAllAsync(any()); RetriableWriteFunction<String, String> retryIO = new RetriableWriteFunction<>(policy, writeFn, schedExec); retryIO.setMetrics(getMetricsUtil(tableId)); retryIO.putAllAsync(records).get(); verify(writeFn, times(2)).putAllAsync(any()); Assert.assertEquals(1, retryIO.retryMetrics.retryCount.getCount()); Assert.assertEquals(0, retryIO.retryMetrics.successCount.getCount()); Assert.assertTrue(retryIO.retryMetrics.retryTimer.getSnapshot().getMax() > 0); }
doReturn(future).when(writeFn).putAllAsync(any()); if (hasDelete) { doReturn(future).when(writeFn).deleteAllAsync(any()); table.putAllAsync(entries).get(); verify(writeFn, times(1)).putAllAsync(argCaptor.capture()); if (hasDelete) { ArgumentCaptor<List> delArgCaptor = ArgumentCaptor.forClass(List.class); verify(writeFn, times(1)).deleteAllAsync(delArgCaptor.capture()); Assert.assertEquals(Arrays.asList("foo2"), delArgCaptor.getValue()); Assert.assertEquals(1, argCaptor.getValue().size());
@Override public boolean isRetriable(Throwable exception) { return writeFn.isRetriable(exception); }
doReturn(CompletableFuture.completedFuture(null)).when(writeFn).putAsync(any(), any()); doReturn(CompletableFuture.completedFuture(null)).when(writeFn).deleteAsync(any());
@Test public void testRetryExhaustedAttemptsPut() throws Exception { String tableId = "testRetryExhaustedAttemptsPut"; TableRetryPolicy policy = new TableRetryPolicy(); policy.withFixedBackoff(Duration.ofMillis(5)); policy.withStopAfterAttempts(10); TableWriteFunction<String, String> writeFn = mock(TableWriteFunction.class); doReturn(true).when(writeFn).isRetriable(any()); CompletableFuture<String> future = new CompletableFuture(); future.completeExceptionally(new RuntimeException("test exception")); doReturn(future).when(writeFn).deleteAllAsync(any()); RetriableWriteFunction<String, String> retryIO = new RetriableWriteFunction<>(policy, writeFn, schedExec); retryIO.setMetrics(getMetricsUtil(tableId)); try { retryIO.deleteAllAsync(Arrays.asList("foo1", "foo2")).get(); Assert.fail(); } catch (ExecutionException e) { } // 1 initial try + 10 retries verify(writeFn, times(11)).deleteAllAsync(any()); Assert.assertEquals(10, retryIO.retryMetrics.retryCount.getCount()); Assert.assertEquals(0, retryIO.retryMetrics.successCount.getCount()); Assert.assertTrue(retryIO.retryMetrics.retryTimer.getSnapshot().getMax() > 0); }
@Test public void testRetryExhaustedTimePut() throws Exception { String tableId = "testRetryExhaustedTimePut"; TableRetryPolicy policy = new TableRetryPolicy(); policy.withFixedBackoff(Duration.ofMillis(5)); policy.withStopAfterDelay(Duration.ofMillis(100)); TableWriteFunction<String, String> writeFn = mock(TableWriteFunction.class); doReturn(true).when(writeFn).isRetriable(any()); CompletableFuture<String> future = new CompletableFuture(); future.completeExceptionally(new RuntimeException("test exception")); doReturn(future).when(writeFn).deleteAsync(anyString()); RetriableWriteFunction<String, String> retryIO = new RetriableWriteFunction<>(policy, writeFn, schedExec); retryIO.setMetrics(getMetricsUtil(tableId)); try { retryIO.deleteAsync("foo").get(); Assert.fail(); } catch (ExecutionException e) { } // Conservatively: must be at least 3 attempts with 5ms backoff and 100ms maxDelay verify(writeFn, atLeast(3)).deleteAsync(anyString()); Assert.assertTrue(retryIO.retryMetrics.retryCount.getCount() >= 3); Assert.assertEquals(0, retryIO.retryMetrics.successCount.getCount()); Assert.assertTrue(retryIO.retryMetrics.retryTimer.getSnapshot().getMax() > 0); }
@Test public void testFlushAndClose() { TableRetryPolicy policy = new TableRetryPolicy(); policy.withFixedBackoff(Duration.ofMillis(100)); TableReadFunction readFn = mock(TableReadFunction.class); TableWriteFunction writeFn = mock(TableWriteFunction.class); AsyncReadWriteTable delegate = new AsyncRemoteTable(readFn, writeFn); AsyncRetriableTable table = new AsyncRetriableTable("t1", delegate, null, policy, schedExec, readFn, writeFn); table.flush(); verify(writeFn, times(1)).flush(); table.close(); verify(readFn, times(1)).close(); verify(writeFn, times(1)).close(); } }
private TableWriteFunction<?, ?> getWriteFn(JavaTableConfig tableConfig) { TableWriteFunction<?, ?> writeFn = deserializeObject(tableConfig, RemoteTableDescriptor.WRITE_FN); if (writeFn != null) { writeFn.init(this.context); } return writeFn; }
@Override public void flush() { if (writeFn != null) { writeFn.flush(); } }
@Override public void close() { readFn.close(); if (writeFn != null) { writeFn.close(); } } }
doReturn(CompletableFuture.completedFuture(null)).when(writeFn).putAsync(any(), any()); cachingTable.putAsync("foo", "baz").get(); doReturn(CompletableFuture.completedFuture(null)).when(writeFn).deleteAsync(any()); cachingTable.deleteAsync("foo").get(); doReturn(CompletableFuture.completedFuture(null)).when(writeFn).putAllAsync(any()); List<Entry<String, String>> entries = new ArrayList<>(); entries.add(new Entry<>("foo1", "bar111")); doReturn(CompletableFuture.completedFuture(null)).when(writeFn).putAllAsync(any()); doReturn(CompletableFuture.completedFuture(null)).when(writeFn).deleteAllAsync(any()); entries = new ArrayList<>(); entries.add(new Entry<>("foo1", "bar111")); doReturn(CompletableFuture.completedFuture(null)).when(writeFn).deleteAllAsync(any()); cachingTable.deleteAllAsync(Arrays.asList("foo1", "foo3")).get();
/** * Store single table {@code record} with specified {@code key}. This method must be thread-safe. * The default implementation calls putAsync and blocks on the completion afterwards. * * @param key key for the table record * @param record table record to be written */ default void put(K key, V record) { try { putAsync(key, record).get(); } catch (InterruptedException | ExecutionException e) { throw new SamzaException("PUT failed for " + key, e); } }
@Test public void testPutWithRetryDisabled() throws Exception { TableRetryPolicy policy = new TableRetryPolicy(); policy.withFixedBackoff(Duration.ofMillis(10)); policy.withStopAfterDelay(Duration.ofMillis(100)); TableReadFunction<String, String> readFn = mock(TableReadFunction.class); TableWriteFunction<String, String> writeFn = mock(TableWriteFunction.class); doReturn(false).when(writeFn).isRetriable(any()); CompletableFuture<String> future = new CompletableFuture(); future.completeExceptionally(new RuntimeException("test exception")); doReturn(future).when(writeFn).putAsync(any(), any()); AsyncReadWriteTable delegate = new AsyncRemoteTable(readFn, writeFn); AsyncRetriableTable table = new AsyncRetriableTable("t1", delegate, null, policy, schedExec, readFn, writeFn); table.init(TestRemoteTable.getMockContext()); try { table.putAsync("foo", "bar").get(); fail(); } catch (ExecutionException e) { } verify(writeFn, times(1)).putAsync(any(), any()); assertEquals(0, table.writeRetryMetrics.retryCount.getCount()); assertEquals(0, table.writeRetryMetrics.successCount.getCount()); assertEquals(0, table.writeRetryMetrics.permFailureCount.getCount()); assertEquals(0, table.writeRetryMetrics.retryTimer.getSnapshot().getMax()); }