All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.bingoohuang.westcache.batch.Batcher Maven / Gradle / Ivy

package com.github.bingoohuang.westcache.batch;

import com.google.common.collect.Lists;
import com.google.common.util.concurrent.SettableFuture;
import lombok.AllArgsConstructor;
import lombok.Value;
import lombok.extern.slf4j.Slf4j;
import lombok.val;

import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * @author bingoohuang [[email protected]] Created on 2017/1/20.
 */
@Slf4j
public class Batcher {
    final Queue> queue;
    final ScheduledExecutorService service;
    final BatcherJob batcherJob;
    final int maxWaitItems;
    private final Runnable runner;

    public Batcher(final ScheduledExecutorService service,
                   final BatcherJob batcherJob,
                   final long maxWaitMillis,
                   final int maxWaitItems,
                   final int maxBatchNum) {
        this.queue = new ConcurrentLinkedQueue<>();
        this.service = service;
        this.batcherJob = batcherJob;
        this.maxWaitItems = maxWaitItems;
        this.runner = () -> {
            if (queue.isEmpty()) return;

            val tasks = new ArrayList>(queue.size());
            do {
                val task = queue.poll();
                if (task == null) break;

                tasks.add(task);
            } while (tasks.size() != maxBatchNum);

            if (tasks.isEmpty()) return;

            doBatchWork(tasks);
        };
        service.scheduleWithFixedDelay(
                runner, maxWaitMillis, maxWaitMillis, TimeUnit.MILLISECONDS);
    }


    /**
     * Submit a argument to the batcher.
     *
     * @param argument batch argument.
     * @return future object.
     */
    public Future submit(T argument) {
        val future = SettableFuture.create();
        queue.add(new BatcherBean(argument, future));
        if (queue.size() < maxWaitItems) return future;

        service.execute(runner);
        return future;
    }

    private void doBatchWork(List> tasks) {
        List batchArgs = Lists.transform(tasks, task -> task.getArg());

        List results = null;
        Exception ex = null;
        try {
            results = batcherJob.doBatchJob(batchArgs);
        } catch (Exception e) {
            log.error("call doBatchJob err", e);
            ex = e;
        }

        int resultsSize = results != null ? results.size() : 0;
        if (resultsSize != tasks.size()) {
            log.error("result size {} is not same with task size {}", resultsSize, tasks.size());
        }

        futuresSet(tasks, results, ex, resultsSize);
    }

    private void futuresSet(List> tasks, List results, Exception ex, int resultsSize) {
        for (int i = 0, ii = tasks.size(); i < ii; ++i) {
            val future = tasks.get(i).getFuture();
            if (i < resultsSize) {
                future.set(results.get(i));
            } else {
                future.setException(ex != null ? ex : new RuntimeException("result is not available"));
            }
        }
    }

    /**
     * @author bingoohuang [[email protected]] Created on 2017/1/22.
     */
    @AllArgsConstructor @Value private static class BatcherBean {
        private final T arg;
        private final SettableFuture future;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy