@Override public void run() { pushCurrentBatch(); } };
/** Build the ThresholdBatcher. */ public ThresholdBatcher<E> build() { return new ThresholdBatcher<>(this); } }
private ThresholdBatcher(Builder<E> builder) { this.thresholds = new ArrayList<>(builder.thresholds); this.executor = Preconditions.checkNotNull(builder.executor); this.maxDelay = Preconditions.checkNotNull(builder.maxDelay); this.receiver = Preconditions.checkNotNull(builder.receiver); this.flowController = Preconditions.checkNotNull(builder.flowController); this.batchMerger = Preconditions.checkNotNull(builder.batchMerger); resetThresholds(); }
@Test public void testBatchingWithDelay() throws Exception { AccumulatingBatchReceiver<SimpleBatch> receiver = new AccumulatingBatchReceiver<>(ApiFutures.<Void>immediateFuture(null)); ThresholdBatcher<SimpleBatch> batcher = createSimpleBatcherBuidler(receiver).setMaxDelay(Duration.ofMillis(100)).build(); batcher.add(SimpleBatch.fromInteger(3)); batcher.add(SimpleBatch.fromInteger(5)); // Give time for the delay to trigger and push the batch Thread.sleep(500); assertThat(receiver.getBatches()).hasSize(1); batcher.add(SimpleBatch.fromInteger(11)); batcher.pushCurrentBatch().get(); List<List<Integer>> expected = Arrays.asList(Arrays.asList(3, 5), Arrays.asList(11)); List<List<Integer>> actual = new ArrayList<>(); for (SimpleBatch batch : receiver.getBatches()) { actual.add(batch.getIntegers()); } assertThat(actual).isEqualTo(expected); }
@Test public void testAdd() throws Exception { AccumulatingBatchReceiver<SimpleBatch> receiver = new AccumulatingBatchReceiver<>(ApiFutures.<Void>immediateFuture(null)); ThresholdBatcher<SimpleBatch> batcher = createSimpleBatcherBuidler(receiver).build(); batcher.add(SimpleBatch.fromInteger(14)); assertThat(batcher.isEmpty()).isFalse(); assertThat(receiver.getBatches()).hasSize(0); batcher.pushCurrentBatch().get(); assertThat(batcher.isEmpty()).isTrue(); assertThat(receiver.getBatches()).hasSize(1); assertThat(receiver.getBatches().get(0).getIntegers()).isEqualTo(Arrays.asList(14)); }
try { receiver.validateBatch(e); boolean anyThresholdReached = isAnyThresholdReached(e); pushCurrentBatch();
private ThresholdBatcher<Batch<RequestT, ResponseT>> createBatcher(PartitionKey partitionKey) { BatchExecutor<RequestT, ResponseT> processor = new BatchExecutor<>(batchingDescriptor, partitionKey); BatchingFlowController<Batch<RequestT, ResponseT>> batchingFlowController = new BatchingFlowController<>( flowController, new BatchElementCounter<>(batchingDescriptor), new BatchByteCounter<RequestT, ResponseT>()); return ThresholdBatcher.<Batch<RequestT, ResponseT>>newBuilder() .setThresholds(getThresholds(batchingSettings)) .setExecutor(executor) .setMaxDelay(batchingSettings.getDelayThreshold()) .setReceiver(processor) .setFlowController(batchingFlowController) .setBatchMerger(new BatchMergerImpl<RequestT, ResponseT>()) .build(); }
@Override public ApiFuture<ResponseT> futureCall(RequestT request, ApiCallContext context) { if (batcherFactory.getBatchingSettings().getIsEnabled()) { BatchedFuture<ResponseT> result = BatchedFuture.<ResponseT>create(); UnaryCallable<RequestT, ResponseT> unaryCallable = callable.withDefaultCallContext(context); Batch<RequestT, ResponseT> batchableMessage = new Batch<RequestT, ResponseT>(batchingDescriptor, request, unaryCallable, result); PartitionKey partitionKey = batchingDescriptor.getBatchPartitionKey(request); ThresholdBatcher<Batch<RequestT, ResponseT>> batcher = batcherFactory.getPushingBatcher(partitionKey); try { batcher.add(batchableMessage); return result; } catch (FlowControlException e) { throw FlowControlRuntimeException.fromFlowControlException(e); } } else { return callable.futureCall(request, context); } } }
final E batch = removeBatch(); if (batch == null) { return ApiFutures.immediateFuture(null);
@Test public void testBatching() throws Exception { AccumulatingBatchReceiver<SimpleBatch> receiver = new AccumulatingBatchReceiver<>(ApiFutures.<Void>immediateFuture(null)); ThresholdBatcher<SimpleBatch> batcher = createSimpleBatcherBuidler(receiver) .setThresholds(BatchingThresholds.<SimpleBatch>create(2)) .build(); batcher.add(SimpleBatch.fromInteger(3)); batcher.add(SimpleBatch.fromInteger(5)); // Give time for the executor to push the batch Thread.sleep(100); assertThat(receiver.getBatches()).hasSize(1); batcher.add(SimpleBatch.fromInteger(7)); batcher.add(SimpleBatch.fromInteger(9)); // Give time for the executor to push the batch Thread.sleep(100); assertThat(receiver.getBatches()).hasSize(2); batcher.add(SimpleBatch.fromInteger(11)); batcher.pushCurrentBatch().get(); List<List<Integer>> expected = Arrays.asList(Arrays.asList(3, 5), Arrays.asList(7, 9), Arrays.asList(11)); List<List<Integer>> actual = new ArrayList<>(); for (SimpleBatch batch : receiver.getBatches()) { actual.add(batch.getIntegers()); } assertThat(actual).isEqualTo(expected); }
try { receiver.validateBatch(e); boolean anyThresholdReached = isAnyThresholdReached(e); pushCurrentBatch();
private ThresholdBatcher<Batch<RequestT, ResponseT>> createBatcher(PartitionKey partitionKey) { BatchExecutor<RequestT, ResponseT> processor = new BatchExecutor<>(batchingDescriptor, partitionKey); BatchingFlowController<Batch<RequestT, ResponseT>> batchingFlowController = new BatchingFlowController<>( flowController, new BatchElementCounter<>(batchingDescriptor), new BatchByteCounter<RequestT, ResponseT>()); return ThresholdBatcher.<Batch<RequestT, ResponseT>>newBuilder() .setThresholds(getThresholds(batchingSettings)) .setExecutor(executor) .setMaxDelay(batchingSettings.getDelayThreshold()) .setReceiver(processor) .setFlowController(batchingFlowController) .setBatchMerger(new BatchMergerImpl<RequestT, ResponseT>()) .build(); }
@Override public ApiFuture<ResponseT> futureCall(RequestT request, ApiCallContext context) { if (batcherFactory.getBatchingSettings().getIsEnabled()) { BatchedFuture<ResponseT> result = BatchedFuture.<ResponseT>create(); UnaryCallable<RequestT, ResponseT> unaryCallable = callable.withDefaultCallContext(context); Batch<RequestT, ResponseT> batchableMessage = new Batch<RequestT, ResponseT>(batchingDescriptor, request, unaryCallable, result); PartitionKey partitionKey = batchingDescriptor.getBatchPartitionKey(request); ThresholdBatcher<Batch<RequestT, ResponseT>> batcher = batcherFactory.getPushingBatcher(partitionKey); try { batcher.add(batchableMessage); return result; } catch (FlowControlException e) { throw FlowControlRuntimeException.fromFlowControlException(e); } } else { return callable.futureCall(request, context); } } }
final E batch = removeBatch(); if (batch == null) { return ApiFutures.immediateFuture(null);
assertThat(trackedFlowController.getBytesReleased()).isEqualTo(0); batcher.add(SimpleBatch.fromInteger(3)); batcher.add(SimpleBatch.fromInteger(5)); batcher.add(SimpleBatch.fromInteger(7)); try { batcher.add(SimpleBatch.fromInteger(9)); Assert.fail("expected exception"); } catch (FlowControlException e) { batcher.pushCurrentBatch().get(); assertThat(receiver.getBatches()).hasSize(1); batcher.add(SimpleBatch.fromInteger(11)); batcher.add(SimpleBatch.fromInteger(13)); batcher.pushCurrentBatch().get();
private static ThresholdBatcher.Builder<SimpleBatch> createSimpleBatcherBuidler( AccumulatingBatchReceiver<SimpleBatch> receiver) { return ThresholdBatcher.<SimpleBatch>newBuilder() .setThresholds(BatchingThresholds.<SimpleBatch>create(100)) .setExecutor(EXECUTOR) .setMaxDelay(Duration.ofMillis(10000)) .setReceiver(receiver) .setFlowController(ThresholdBatcherTest.<SimpleBatch>getDisabledBatchingFlowController()) .setBatchMerger(new SimpleBatchMerger()); }
@Override public void run() { pushCurrentBatch(); } };
private E removeBatch() { lock.lock(); try { E batch = currentOpenBatch; currentOpenBatch = null; if (currentAlarmFuture != null) { currentAlarmFuture.cancel(false); currentAlarmFuture = null; } resetThresholds(); return batch; } finally { lock.unlock(); } }
/** Build the ThresholdBatcher. */ public ThresholdBatcher<E> build() { return new ThresholdBatcher<>(this); } }
assertThat(trackedFlowController.getBytesReleased()).isEqualTo(0); batcher.add(SimpleBatch.fromInteger(3)); batcher.add(SimpleBatch.fromInteger(5)); batcher.add( SimpleBatch.fromInteger(7)); // We expect to block here until the first batch is handled assertThat(receiver.getBatches()).hasSize(1); batcher.add(SimpleBatch.fromInteger(9)); batcher.add( SimpleBatch.fromInteger(11)); // We expect to block here until the second batch is handled assertThat(receiver.getBatches()).hasSize(2); batcher.pushCurrentBatch().get();