├── .gitignore ├── NOTICE.txt ├── src ├── test │ └── java │ │ └── com │ │ └── github │ │ └── victormpcmun │ │ └── delayedbatchexecutor │ │ ├── simulator │ │ ├── SimulatorUtils.java │ │ ├── DBEMultipleRequestsTest.java │ │ ├── DBE3Builder.java │ │ └── ClientParallelRequestSimulator.java │ │ └── TupleListTransposerTest.java └── main │ ├── java │ └── com │ │ └── github │ │ └── victormpcmun │ │ └── delayedbatchexecutor │ │ ├── TupleBlocking.java │ │ ├── TupleListTransposer.java │ │ ├── TupleMono.java │ │ ├── Tuple.java │ │ ├── TupleListDuplicatedFinder.java │ │ ├── TupleFuture.java │ │ ├── DelayedBatchExecutor2.java │ │ ├── DelayedBatchExecutor3.java │ │ ├── DelayedBatchExecutor4.java │ │ ├── DelayedBatchExecutor5.java │ │ ├── DelayedBatchExecutor6.java │ │ ├── DelayedBatchExecutor7.java │ │ └── DelayedBatchExecutor.java │ └── javadoc │ └── doc-files │ ├── future.xml │ ├── mono.xml │ ├── blocking.xml │ ├── future.svg │ ├── blocking.svg │ └── mono.svg ├── pom.xml ├── README.md └── LICENSE.txt /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | target 3 | .idea -------------------------------------------------------------------------------- /NOTICE.txt: -------------------------------------------------------------------------------- 1 | DelayedBatchExecutor 2 | Copyright 2019 Victor Porcar 3 | 4 | This product includes "project reactor" software (https://projectreactor.io/). -------------------------------------------------------------------------------- /src/test/java/com/github/victormpcmun/delayedbatchexecutor/simulator/SimulatorUtils.java: -------------------------------------------------------------------------------- 1 | package com.github.victormpcmun.delayedbatchexecutor.simulator; 2 | 3 | public class SimulatorUtils { 4 | 5 | static void sleepCurrentThread(int milliseconds) { 6 | try { 7 | Thread.sleep(milliseconds); 8 | } catch (InterruptedException e) { 9 | e.printStackTrace(); 10 | throw new RuntimeException("InterruptedException", e); 11 | } 12 | } 13 | 14 | 15 | static String concatenateInt(long long1, int int2) { 16 | return long1 + "_" + int2; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/github/victormpcmun/delayedbatchexecutor/TupleBlocking.java: -------------------------------------------------------------------------------- 1 | package com.github.victormpcmun.delayedbatchexecutor; 2 | 3 | import java.util.concurrent.ExecutionException; 4 | 5 | class TupleBlocking extends TupleFuture { 6 | 7 | TupleBlocking(Object... argsAsArray) { 8 | super(argsAsArray); 9 | } 10 | 11 | T getValueBlocking() { 12 | try { 13 | return get(); 14 | } catch (ExecutionException e) { 15 | throw (RuntimeException) e.getCause(); 16 | } catch (InterruptedException e) { 17 | throw new RuntimeException("Interrupted waiting. it shouldn't happen ever", e); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/github/victormpcmun/delayedbatchexecutor/TupleListTransposer.java: -------------------------------------------------------------------------------- 1 | package com.github.victormpcmun.delayedbatchexecutor; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | class TupleListTransposer { 7 | 8 | static List> transposeValuesAsListOfList(List> paramList) { 9 | int argsCounter = paramList.get(0).getArgsSize(); 10 | List> listOfListsOfArgs = buildEmptyListOfListsOfArgs(argsCounter); 11 | for (Tuple tuple : paramList) { 12 | for (int argPosition = 0; argPosition < argsCounter; argPosition++) { 13 | Object object = tuple.getArgumentByPosition(argPosition); 14 | listOfListsOfArgs.get(argPosition).add(object); 15 | } 16 | } 17 | return listOfListsOfArgs; 18 | } 19 | 20 | private static List> buildEmptyListOfListsOfArgs(int argsCounter) { 21 | List> listOfListsOfArgs = new ArrayList<>(); 22 | 23 | for (int argPosition = 0; argPosition < argsCounter; argPosition++) { 24 | listOfListsOfArgs.add(argPosition, new ArrayList<>()); 25 | } 26 | return listOfListsOfArgs; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/com/github/victormpcmun/delayedbatchexecutor/TupleMono.java: -------------------------------------------------------------------------------- 1 | package com.github.victormpcmun.delayedbatchexecutor; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | class TupleMono extends Tuple { 6 | 7 | private Mono mono; 8 | private boolean done; 9 | 10 | private Object locker = new Object(); 11 | 12 | TupleMono(Object... argsAsArray) { 13 | super(argsAsArray); 14 | this.done = false; 15 | this.mono = Mono.create(monoSink -> { 16 | if (!done) { 17 | try { 18 | // TODO: avoid this wait, any good suggestion of how to do it ? 19 | synchronized (locker) { 20 | locker.wait(); 21 | } 22 | } catch (InterruptedException e) { 23 | throw new RuntimeException("Can not get Value from thread"); 24 | } 25 | } 26 | if (hasRuntimeException()) { 27 | monoSink.error(getRuntimeException()); 28 | } else { 29 | monoSink.success(result); 30 | } 31 | }); 32 | } 33 | 34 | Mono getMono() { 35 | return mono; 36 | } 37 | 38 | @Override 39 | void continueIfIsWaiting() { 40 | synchronized (locker) { 41 | this.done = true; 42 | locker.notify(); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/test/java/com/github/victormpcmun/delayedbatchexecutor/TupleListTransposerTest.java: -------------------------------------------------------------------------------- 1 | package com.github.victormpcmun.delayedbatchexecutor; 2 | 3 | import org.junit.Assert; 4 | import org.junit.Test; 5 | 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | 9 | public class TupleListTransposerTest { 10 | 11 | private static final String ARG11 = "ARG11"; 12 | private static final String ARG12 = "ARG11"; 13 | private static final String ARG21 = "ARG11"; 14 | private static final String ARG22 = "ARG11"; 15 | 16 | @Test 17 | public void tupleListTest() { 18 | List> tupleList = new ArrayList<>(); 19 | Tuple tuple1 = new TupleFuture<>(ARG11, ARG12); 20 | Tuple tuple2 = new TupleFuture<>(ARG21, ARG22); 21 | 22 | tupleList.add(tuple1); 23 | tupleList.add(tuple2); 24 | 25 | List> tranposedTupleAsListOfList = TupleListTransposer.transposeValuesAsListOfList(tupleList); 26 | 27 | Assert.assertEquals(tranposedTupleAsListOfList.get(0).get(0), ARG11); 28 | Assert.assertEquals(tranposedTupleAsListOfList.get(0).get(0), ARG21); 29 | Assert.assertEquals(tranposedTupleAsListOfList.get(0).get(0), ARG12); 30 | Assert.assertEquals(tranposedTupleAsListOfList.get(0).get(0), ARG22); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/github/victormpcmun/delayedbatchexecutor/Tuple.java: -------------------------------------------------------------------------------- 1 | package com.github.victormpcmun.delayedbatchexecutor; 2 | 3 | import java.util.Arrays; 4 | 5 | abstract class Tuple { 6 | protected T result; 7 | protected final Object[] argsAsArray; 8 | protected RuntimeException runtimeException; 9 | private int hashCode; 10 | 11 | Tuple(Object... argsAsArray) { 12 | this.result = null; 13 | this.argsAsArray = argsAsArray; 14 | this.hashCode = Arrays.hashCode(argsAsArray); 15 | 16 | } 17 | 18 | void copyResultAndRuntimeExceptionFromTuple(Tuple tuple) { 19 | this.result = tuple.getResult(); 20 | this.runtimeException = tuple.getRuntimeException(); 21 | } 22 | 23 | public void setResult(T result) { 24 | this.result = result; 25 | } 26 | 27 | public void setRuntimeException(RuntimeException runtimeException) { 28 | this.runtimeException = runtimeException; 29 | } 30 | 31 | int getArgsSize() { 32 | return argsAsArray.length; 33 | } 34 | 35 | Object getArgumentByPosition(int argPosition) { 36 | return argsAsArray[argPosition]; 37 | } 38 | 39 | public T getResult() { 40 | return result; 41 | } 42 | 43 | abstract void continueIfIsWaiting(); 44 | 45 | RuntimeException getRuntimeException() { 46 | return runtimeException; 47 | } 48 | 49 | boolean hasRuntimeException() { 50 | return runtimeException != null; 51 | } 52 | 53 | @Override 54 | public boolean equals(Object o) { 55 | if (o == this) 56 | return true; 57 | if (!(o instanceof Tuple)) 58 | return false; 59 | Tuple tuple = (Tuple) o; 60 | return Arrays.equals(argsAsArray, tuple.argsAsArray); 61 | } 62 | 63 | @Override 64 | public int hashCode() { 65 | return hashCode; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/com/github/victormpcmun/delayedbatchexecutor/TupleListDuplicatedFinder.java: -------------------------------------------------------------------------------- 1 | package com.github.victormpcmun.delayedbatchexecutor; 2 | 3 | import java.util.ArrayList; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | class TupleListDuplicatedFinder { 9 | 10 | private List> allTupleList; 11 | private List> tupleListUnique; 12 | private Map duplicatedMapIndex; 13 | Map> hashCodeByTuplesIndexInList = new HashMap<>(); 14 | 15 | TupleListDuplicatedFinder(List> allTupleList) { 16 | this.allTupleList = allTupleList; 17 | duplicatedMapIndex = new HashMap<>(); 18 | tupleListUnique = new ArrayList<>(); 19 | 20 | for (int index = 0; index < allTupleList.size(); index++) { 21 | Tuple tuple = allTupleList.get(index); 22 | int tupleHashCode = tuple.hashCode(); 23 | List listOfIndexesMatchingHashCode = hashCodeByTuplesIndexInList.get(tupleHashCode); 24 | 25 | if (listOfIndexesMatchingHashCode == null) { 26 | List listOfIndexes = new ArrayList<>(); 27 | listOfIndexes.add(index); 28 | hashCodeByTuplesIndexInList.put(tupleHashCode, listOfIndexes); 29 | tupleListUnique.add(tuple); 30 | } else { 31 | Integer matchingIndex = listOfIndexesMatchingHashCode.stream() 32 | .filter(indexInList -> tuple.equals(allTupleList.get(indexInList))).findAny().orElse(null); 33 | if (matchingIndex != null) { 34 | duplicatedMapIndex.put(index, matchingIndex); 35 | } else { 36 | listOfIndexesMatchingHashCode.add(index); 37 | tupleListUnique.add(tuple); 38 | } 39 | } 40 | 41 | } 42 | } 43 | 44 | List> getAllTupleList() { 45 | return allTupleList; 46 | } 47 | 48 | List> getTupleListUnique() { 49 | return tupleListUnique; 50 | } 51 | 52 | Map getDuplicatedMapIndex() { 53 | return duplicatedMapIndex; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/test/java/com/github/victormpcmun/delayedbatchexecutor/simulator/DBEMultipleRequestsTest.java: -------------------------------------------------------------------------------- 1 | package com.github.victormpcmun.delayedbatchexecutor.simulator; 2 | 3 | import com.github.victormpcmun.delayedbatchexecutor.DelayedBatchExecutor3; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | import java.util.function.BiFunction; 8 | 9 | public class DBEMultipleRequestsTest { 10 | 11 | @Test 12 | public void manyRequestsPerSecondTest() { 13 | // tests parameters 14 | int requestsPerSecond = 200; 15 | int testDurationSeconds = 10; 16 | // delayed-batch-executor 17 | long dbeDurationWindowInMillis=200; 18 | int dbeMaxSize = 200; 19 | int dbeCallBackToCompleteInMillis=50; 20 | 21 | launchTest(requestsPerSecond,testDurationSeconds,dbeDurationWindowInMillis,dbeMaxSize,dbeCallBackToCompleteInMillis); 22 | 23 | } 24 | 25 | 26 | 27 | private void launchTest( 28 | int requestsPerSecond, 29 | int testDurationInSeconds, 30 | long dbeDurationWindowInMillis, 31 | int dbeMaxSize, 32 | int msToCompleteDbeCallBack) { 33 | 34 | 35 | //----------- 36 | 37 | DelayedBatchExecutor3 dbe3 = DBE3Builder.buildDBE(dbeDurationWindowInMillis, dbeMaxSize, msToCompleteDbeCallBack); 38 | 39 | BiFunction biFunction = (duration,maxSize) -> dbe3.execute(duration,maxSize); 40 | 41 | ClientParallelRequestSimulator clientParallelRequestSimulator = new ClientParallelRequestSimulator( 42 | requestsPerSecond, 43 | testDurationInSeconds, 44 | biFunction 45 | ); 46 | 47 | clientParallelRequestSimulator.go(); 48 | 49 | // wait two seconds to allow threads to finish 50 | SimulatorUtils.sleepCurrentThread((testDurationInSeconds+2)*1000); 51 | 52 | Assert.assertEquals( 53 | clientParallelRequestSimulator.getTotalExpectedRequests(), 54 | clientParallelRequestSimulator.getTotalRequestsSinceBeginning()); 55 | 56 | clientParallelRequestSimulator.logExecutionReport(); 57 | } 58 | 59 | 60 | 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/com/github/victormpcmun/delayedbatchexecutor/TupleFuture.java: -------------------------------------------------------------------------------- 1 | package com.github.victormpcmun.delayedbatchexecutor; 2 | 3 | import java.time.Duration; 4 | import java.time.Instant; 5 | import java.util.concurrent.ExecutionException; 6 | import java.util.concurrent.Future; 7 | import java.util.concurrent.TimeUnit; 8 | import java.util.concurrent.TimeoutException; 9 | 10 | class TupleFuture extends Tuple implements Future { 11 | 12 | private final Instant initInstant; 13 | private Instant endInstant; 14 | private boolean done; 15 | 16 | TupleFuture(Object... argsAsArray) { 17 | super(argsAsArray); 18 | this.done = false; 19 | this.initInstant = Instant.now(); 20 | } 21 | 22 | Future getFuture() { 23 | return this; 24 | } 25 | 26 | @Override 27 | void continueIfIsWaiting() { 28 | synchronized (this) { 29 | this.endInstant = Instant.now(); 30 | this.done = true; 31 | this.notify(); 32 | } 33 | } 34 | 35 | @Override 36 | public boolean isDone() { 37 | return done; 38 | } 39 | 40 | @Override 41 | public boolean cancel(boolean mayInterruptIfRunning) { 42 | return false; 43 | } 44 | 45 | @Override 46 | public boolean isCancelled() { 47 | return false; 48 | } 49 | 50 | @Override 51 | public T get() throws InterruptedException, ExecutionException { 52 | try { 53 | return get(0L); 54 | } catch (TimeoutException te) { 55 | throw new RuntimeException("This RuntimeException should never thrown at this point.", te); 56 | } 57 | } 58 | 59 | @Override 60 | public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { 61 | long milliseconds = TimeUnit.MILLISECONDS.convert(timeout, unit); 62 | return get(milliseconds); 63 | } 64 | 65 | public Duration getDelayedTime() { 66 | return Duration.between(initInstant, endInstant); 67 | } 68 | 69 | private T get(long millisecondsWait) throws InterruptedException, ExecutionException, TimeoutException { 70 | synchronized (this) { 71 | if (!done) { 72 | if (millisecondsWait == 0L) { 73 | this.wait(); 74 | } else { 75 | this.wait(millisecondsWait); 76 | if (!done) { 77 | throw new TimeoutException("can not get the result in " + millisecondsWait); 78 | } 79 | } 80 | } 81 | } 82 | if (hasRuntimeException()) { 83 | throw new ExecutionException(getRuntimeException()); 84 | } 85 | return result; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/test/java/com/github/victormpcmun/delayedbatchexecutor/simulator/DBE3Builder.java: -------------------------------------------------------------------------------- 1 | package com.github.victormpcmun.delayedbatchexecutor.simulator; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.time.Duration; 7 | import java.util.ArrayList; 8 | import java.util.List; 9 | 10 | public class DBE3Builder { 11 | 12 | 13 | private static final Logger log = LoggerFactory.getLogger(DBE3Builder.class); 14 | 15 | public static com.github.victormpcmun.delayedbatchexecutor.DelayedBatchExecutor3 buildDBE( 16 | long delayedBatchExecutorWindow, 17 | int delayedBatchExecutorMaxSize, 18 | int msToCompleteDbeCallBack) { 19 | 20 | Duration delayedBatchExecutorWindowDuration = Duration.ofMillis(delayedBatchExecutorWindow); 21 | 22 | com.github.victormpcmun.delayedbatchexecutor.DelayedBatchExecutor3 dbe3 = com.github.victormpcmun.delayedbatchexecutor.DelayedBatchExecutor3.create( 23 | delayedBatchExecutorWindowDuration, 24 | delayedBatchExecutorMaxSize /*, 25 | Executors.newFixedThreadPool(4), 26 | 8192, 27 | true*/, 28 | (timestampLongList, threadNumberIntegerList) -> { 29 | long now = System.currentTimeMillis(); 30 | SimulatorUtils.sleepCurrentThread(msToCompleteDbeCallBack); 31 | 32 | long maxDurationWaiting = -1; 33 | 34 | for (long timestamp:timestampLongList) { 35 | long duration = now - timestamp; 36 | if (duration>maxDurationWaiting) { 37 | maxDurationWaiting=duration; 38 | } 39 | } 40 | 41 | 42 | String currentThreadName = Thread.currentThread().getName(); 43 | 44 | log.info("DBE Callback executed in thread {}. Size of list {}. Max wait to be invoked: {} ms", 45 | currentThreadName, 46 | threadNumberIntegerList.size() , 47 | maxDurationWaiting); 48 | 49 | List result = new ArrayList<>(); 50 | for (int i=0; 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 -------------------------------------------------------------------------------- /src/main/javadoc/doc-files/mono.xml: -------------------------------------------------------------------------------- 1 | 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 -------------------------------------------------------------------------------- /src/main/javadoc/doc-files/blocking.xml: -------------------------------------------------------------------------------- 1 | 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 -------------------------------------------------------------------------------- /src/test/java/com/github/victormpcmun/delayedbatchexecutor/simulator/ClientParallelRequestSimulator.java: -------------------------------------------------------------------------------- 1 | package com.github.victormpcmun.delayedbatchexecutor.simulator; 2 | 3 | 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import java.util.*; 8 | import java.util.concurrent.*; 9 | import java.util.concurrent.atomic.AtomicLong; 10 | import java.util.function.BiFunction; 11 | 12 | public class ClientParallelRequestSimulator extends TimerTask { 13 | 14 | private final Logger log = LoggerFactory.getLogger(getClass()); 15 | 16 | int currentSecond; 17 | 18 | int requestsPerSecond; 19 | 20 | BiFunction requestFunction; 21 | 22 | int testDurationInSeconds; 23 | int totalRequestsSinceBeginning; 24 | 25 | ExecutorService executorService; 26 | 27 | Set allRequestsSet; 28 | AtomicLong maxTimeInFinishingCall; 29 | 30 | int totalExpectedRequests; 31 | 32 | public ClientParallelRequestSimulator( 33 | int requestsPerSecond, 34 | int testDurationInSecond, 35 | BiFunction requestFunction) { 36 | 37 | this.totalRequestsSinceBeginning =0; 38 | this.currentSecond =0; 39 | this.requestsPerSecond = requestsPerSecond; 40 | this.requestFunction = requestFunction; 41 | 42 | this.testDurationInSeconds=testDurationInSecond; 43 | this.executorService = Executors.newFixedThreadPool(10000); 44 | this.allRequestsSet = Collections.synchronizedSet(new HashSet<>()); 45 | this.maxTimeInFinishingCall= new AtomicLong(-1); 46 | this.totalExpectedRequests = requestsPerSecond * testDurationInSeconds; 47 | } 48 | 49 | 50 | public void run() { 51 | 52 | if (currentSecond > (testDurationInSeconds-1)) { 53 | // do nothing 54 | return; 55 | } 56 | 57 | List> threads = new ArrayList<>(); 58 | int threadCounter; 59 | for (threadCounter = 0; threadCounter < requestsPerSecond; threadCounter++) { 60 | Callable callable = getCallableForThread( 61 | threadCounter, 62 | this.requestFunction, 63 | this); 64 | threads.add(executorService.submit(callable)); 65 | } 66 | 67 | log.info("begin second {} => created {} requests", currentSecond, threadCounter); 68 | 69 | currentSecond++; 70 | 71 | } 72 | 73 | public void go() { 74 | Timer timer = new Timer(); 75 | timer.scheduleAtFixedRate(this, 0, 1000); 76 | } 77 | 78 | 79 | public Set getCallsNotFinished() { 80 | return allRequestsSet; 81 | } 82 | 83 | 84 | private Callable getCallableForThread( 85 | int threadNumber, 86 | BiFunction biFunction, 87 | ClientParallelRequestSimulator clientParallelRequestSimulator) { 88 | return () -> { 89 | 90 | int delay = getRandomIntegerFromInterval(0, 999); 91 | SimulatorUtils.sleepCurrentThread(delay); 92 | 93 | long initTimestamp = System.currentTimeMillis(); 94 | 95 | String callIdentifier = SimulatorUtils.concatenateInt(initTimestamp,threadNumber); 96 | 97 | clientParallelRequestSimulator.allRequestsSet.add(callIdentifier); 98 | clientParallelRequestSimulator.totalRequestsSinceBeginning++; 99 | 100 | String result = biFunction.apply(initTimestamp, threadNumber); 101 | 102 | 103 | clientParallelRequestSimulator.allRequestsSet.remove(callIdentifier); 104 | long callDuration = System.currentTimeMillis() - initTimestamp; 105 | 106 | 107 | if (clientParallelRequestSimulator.maxTimeInFinishingCall.longValue() 2 | 3 | 4.0.0 4 | 5 | com.github.victormpcmun 6 | delayed-batch-executor 7 | 3.2-SNAPSHOT 8 | jar 9 | 10 | ${project.groupId}:${project.artifactId} 11 | A component to decrease the number of required queries by batching them in Java multi-threaded applications 12 | https://github.com/victormpcmun/delayed-batch-executor 13 | 14 | 15 | 16 | The Apache Software License, Version 2.0 17 | http://www.apache.org/licenses/LICENSE-2.0.txt 18 | 19 | 20 | 21 | 22 | 23 | Victor Porcar 24 | victormpcmun@gmail.com 25 | Victor Porcar 26 | https://github.com/victormpcmun 27 | 28 | 29 | 30 | 31 | scm:git:git://github.com/victormpcmun/delayed-batch-executor.git 32 | scm:git:https://github.com/victormpcmun/delayed-batch-executor.git 33 | http://github.com/victormpcmun/delayed-batch-executor/tree/master 34 | HEAD 35 | 36 | 37 | 38 | 39 | ossrh 40 | https://oss.sonatype.org/content/repositories/snapshots 41 | 42 | 43 | ossrh 44 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 45 | 46 | 47 | 48 | 49 | 3.2.11.RELEASE 50 | UTF-8 51 | UTF-8 52 | 53 | 54 | 55 | 56 | io.projectreactor 57 | reactor-core 58 | ${reactor-core.version} 59 | 60 | 61 | 62 | junit 63 | junit 64 | 4.13.1 65 | test 66 | 67 | 68 | 69 | ch.qos.logback 70 | logback-classic 71 | 1.3.12 72 | 73 | 74 | 75 | 76 | 77 | 78 | org.apache.maven.plugins 79 | maven-compiler-plugin 80 | 3.8.1 81 | 82 | 1.8 83 | 1.8 84 | 85 | 86 | 87 | org.apache.maven.plugins 88 | maven-javadoc-plugin 89 | 3.1.1 90 | 91 | public 92 | true 93 | 94 | 95 | 96 | attach-javadocs 97 | 98 | jar 99 | 100 | 101 | 102 | 103 | 104 | org.apache.maven.plugins 105 | maven-source-plugin 106 | 3.2.0 107 | 108 | 109 | attach-sources 110 | 111 | jar 112 | 113 | 114 | 115 | 116 | 117 | 118 | org.apache.maven.plugins 119 | maven-gpg-plugin 120 | 1.6 121 | 122 | 123 | sign-artifacts 124 | verify 125 | 126 | sign 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## ARTICLE IN DZONE 3 | 4 | https://dzone.com/articles/delayedbatchexecutor-how-to-optimize-database-usag 5 | 6 | 7 | ## LATEST VERSION 8 | 9 | https://search.maven.org/artifact/com.github.victormpcmun/delayed-batch-executor 10 | 11 | ## Rationale behind of DelayeBatchExecutor 12 | 13 | There are several scenarios in which concurrent threads execute many times the same query (with different parameters) to a database at almost the same time. 14 | 15 | For example, a REST endpoint serving tens or hundreds requests per second in which each one requires to retrieve a row from table by a different Id. 16 | 17 | In a similar way, another typical scenario is a message listener that consumes a large number of messages per second and requires to execute a query by a different Id to process each one. 18 | 19 | In these cases, the database executes many times the same query in a short interval of time (say few milliseconds) like these: 20 | ```sql 21 | SELECT * FROM TABLE WHERE ID = 22 | SELECT * FROM TABLE WHERE ID = 23 | ... 24 | SELECT * FROM TABLE WHERE ID = 25 | ``` 26 | DelayedBatchExecutor is a component that allows easily to *convert* these n queries of 1 parameter into just one single query with n parameters, like this one: 27 | 28 | ```sql 29 | SELECT * FROM TABLE WHERE ID IN (, , ..., ) 30 | ``` 31 | 32 | The advantages of executing one query with n parameters instead of n queries of 1 parameter are the following: 33 | 34 | * The usage of network resources is reduced dramatically: The number of round-trips to the database is 1 instead of n. 35 | 36 | * Optimization of database server resources: you would be surprised how well databases optimize queries of n parameters. Pick any large table of your schema and analyse the execution time and CPU usage a for a single query of n parameters versus n queries of 1 parameter. 37 | 38 | * The usage of database connections from the connection pool is reduced: there are more available connections overall, which means less waiting time for a connection on peak times. 39 | 40 | In short, it is much more efficient executing 1 query of n parameters than n queries of one parameter, which means that the system as a whole requires less resources. 41 | 42 | ## DelayedBatchExecutor In Action 43 | 44 | It basically works by creating *time windows* where the parameters of the queries executed during the *time window* are collected in a list. 45 | As soon as the *time window* finishes, the list is passed (via callback) to a method that executes one single query with all the parameters in the list and returns another list with the results. Each thread receives their corresponding result from the result list according to one of the following policies as explained below: blocking , non-blocking (Future) and non-blocking (Reactive). 46 | 47 | A DelayedBatchExecutor is defined by three parameters: 48 | 49 | * TimeWindow: defined as java.time.Duration 50 | * max size: it is the max number of items to be collected in the list 51 | * batchCallback: it receives the parameters list to perform a single query and must return a list with the corresponding results. 52 | - It can be implemented as method reference or lambda expression. 53 | - It is invoked automatically as soon as the TimeWindow is finished OR the collection list is full. 54 | - The returned list must have a correspondence in elements with the parameters list, this means that the value of position 0 of the returned list must be the one corresponding to parameter in position 0 of the param list and so on... 55 | - By default, duplicated parameters (by [hashCode](https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#hashCode--) and [equals](https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#equals-java.lang.Object-)) are removed from the parameters list automatically. This is optimal in most cases although there is a way for having all parameters (including duplicates) if it is required (See Advanced Usage) 56 | 57 | Now, Let's define a DelayedBatchExecutor to receive an Integer value as parameter and return a String, and having a time window = 50 milliseconds, a max size = 100 elements and having the batchCallback defined as method reference: 58 | 59 | ```java 60 | DelayedBatchExecutor2 dbe = DelayedBatchExecutor2.create(Duration.ofMillis(50), 100, this::myBatchCallBack); 61 | 62 | ... 63 | 64 | List myBatchCallBack(List listOfIntegers) { 65 | List resultList = ...// execute query:SELECT * FROM TABKE WHERE ID IN (listOfIntegers.get(0), ..., listOfIntegers.get(n)); 66 | // use your favourite API: JDBC, JPA, Hibernate,... 67 | ... 68 | return resultList; 69 | } 70 | ``` 71 | 72 | The same DelayedBatchExecutor2 but having the callback defined as lambda expression would be: 73 | 74 | ```java 75 | DelayedBatchExecutor2 dbe = DelayedBatchExecutor2.create(Duration.ofMillis(50), 100, listOfIntegers-> 76 | { 77 | List resultList = ...// execute query:SELECT * FROM TABLE WHERE ID IN (listOfIntegers.get(0), ..., listOfIntegers.get(n)); 78 | // use your favourite API: JDBC, JPA, Hibernate,... 79 | ... 80 | return resultList; 81 | 82 | }); 83 | ``` 84 | NOTE: the instance `dbe` must be accesible from the code being executed by the threads (it is often declared as instance variable of a singleton DAO). 85 | Once defined the DelayedBatchExecutor instance, it is easy to use it from the code executed in each thread 86 | 87 | ```java 88 | // this code is executed in one of the multiple threads 89 | int param=...; 90 | String result = dbe.execute(param); // all the threads executing this code within a interval of 50 ms will have 91 | // their parameters (an integer value) collected in a list (list of integers) 92 | // that will be passed to the callback method, and from the list returned from this 93 | // method, each thread will receive its corresponding value 94 | // all this managed behind the scenes by the DelayedBatchExecutor 95 | } 96 | ``` 97 | NOTE: 98 | - To create a DelayedBatchExecutor for taking more than one argument see FootNote 1 99 | - In the example above, the thread is stopped when the execute(...) method is executed until the result is available (blocking behaviour). This is one of the three execution policies of the DelayedBatchExecutor 100 | 101 | 102 | ### Execution Policies 103 | 104 | There are three policies to use a DelayedBatchExecutor from the code being executed from the threads 105 | 106 | #### Blocking 107 | 108 | The thread is blocked until the result is available, it is implemented by using the method `execute(...)` 109 | 110 | ```java 111 | int param = ... 112 | ... 113 | String result = dbe.execute(param); // this thread will be blocked until the result is available 114 | // compute with result 115 | ``` 116 | The following diagram depicts how blocking policy works: 117 | 118 | ![Blocking image](/src/main/javadoc/doc-files/blocking.svg) 119 | 120 | 121 | #### Non-blocking (java.util.concurrent.Future) 122 | 123 | The thread is not blocked, it is implemented by using the method `executeAsFuture(...)` 124 | 125 | ```java 126 | int param = ... 127 | ... 128 | Future resultFuture = dbe.executeAsFuture(param); // the thread will not be blocked 129 | // compute something else 130 | String result = resultFuture.get(); // Blocks the thread until the result is available (if necessary) 131 | // compute with result 132 | ``` 133 | 134 | The following diagram depicts how Future policy works: 135 | 136 | ![Future image](/src/main/javadoc/doc-files/future.svg) 137 | 138 | 139 | #### Non-blocking (Reactive using Reactor framework): 140 | 141 | The thread is not blocked, it is implemented by using the method `executeAsMono(...)` 142 | 143 | ```java 144 | int param =... 145 | ... 146 | reactor.core.publisher.Mono resultMono = dbe.executeAsMono(param); // the thread will not be blocked 147 | // compute something else 148 | resultMono.subscribe(stringResult -> { 149 | // compute with stringResult 150 | }); 151 | ``` 152 | The following diagram depicts how Reactive policy works: 153 | 154 | ![Reactive image](/src/main/javadoc/doc-files/mono.svg) 155 | 156 | ### Advanced Usage 157 | 158 | There are three parameters of a DelayedBatchExecutor that must be known to get the most of it: 159 | 160 | - ExecutorService: the callback method is actually executed in a parallel thread, which is provided by an java.util.concurrent.ExecutorService. By default this Executor is `Executors.newFixedThreadPool(4)`. 161 | 162 | - bufferQueueSize: it is the max size of the internal list, by default its value is 8192. 163 | 164 | - removeDuplicates: if false, then DelayedBatchExecutor won't removed all duplicated parameters from the parameters list before invoking the batchCallback. By default its value is true. 165 | 166 | These parameters can be set by using the following constructor: 167 | 168 | ```java 169 | ... 170 | int maxSize=20; 171 | ExecutorService executorService= Executors.newFixedThreadPool(10); 172 | int bufferQueueSize= 16384; 173 | boolean removeDuplicates = false; 174 | 175 | DelayedBatchExecutor2 dbe = DelayedBatchExecutor2.create( 176 | Duration.ofMillis(200), 177 | maxSize, 178 | executorService, 179 | bufferQueueSize, 180 | removeDuplicates, 181 | this::myBatchCallBack); 182 | ``` 183 | At any time, the configuration paramaters can be updated by using this thread safe method 184 | 185 | ```java 186 | ... 187 | dbe.updateConfig( 188 | Duration.ofMillis(200), 189 | maxSize, 190 | executorService, 191 | bufferQueueSize, 192 | removeDuplicates, 193 | this::myBatchCallBack); 194 | ``` 195 | 196 | ----- 197 | -Foot Note 1: The example shows a DelayedBatchExecutor for a parameter of type Integer and a return type of String, hence DelayedBatchExecutor2 198 | 199 | For a DelayedBatchExecutor for two parameters (say Integer and Date) and a returning type String, the definition would be: 200 | DelayedBatchExecutor3 and so on... 201 | -------------------------------------------------------------------------------- /src/main/java/com/github/victormpcmun/delayedbatchexecutor/DelayedBatchExecutor2.java: -------------------------------------------------------------------------------- 1 | package com.github.victormpcmun.delayedbatchexecutor; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | import java.time.Duration; 6 | import java.util.List; 7 | import java.util.concurrent.ExecutionException; 8 | import java.util.concurrent.ExecutorService; 9 | import java.util.concurrent.Future; 10 | 11 | /** 12 | * Delayed Batch Executor for one argument of type A and return type Z
13 | * 14 | *
 15 |  * {@code
 16 |  * DelayedBatchExecutor2 dbe = DelayedBatchExecutor2.create(Duration.ofMillis(50), 10, this::myBatchCallback);
 17 |  *
 18 |  * ...
 19 |  *
 20 |  * public void usingDelayedBatchExecutor(Integer param1) {
 21 |  *
 22 |  *    // using blocking behaviour
 23 |  *    String stringResult1 = dbe.execute(param1); // the thread will be blocked until the result is available
 24 |  *    // compute with stringResult1
 25 |  *
 26 |  *
 27 |  *    // using Future
 28 |  *    Future resultAsFutureString = dbe.executeAsFuture(param1); // the thread will not  be blocked
 29 |  *    // compute something else
 30 |  *    String stringResult2 = resultAsFutureString.get();  // Blocks the thread if necessary for the computation to complete, and then retrieves its result.
 31 |  *    // compute with stringResult2
 32 |  *
 33 |  *
 34 |  *    // using Mono
 35 |  *    Mono monoResult = dbe.executeAsMono(param1); // the thread will not  be blocked
 36 |  *    // compute something else
 37 |  *    monoResult.subscribe(stringResult3 -> {
 38 |  *     // compute with stringResult
 39 |  *    });
 40 |  *
 41 |  * }
 42 |  *
 43 |  * ...
 44 |  *
 45 |  * List myBatchCallback(List arg1List) {
 46 |  *   List result = ...
 47 |  *   ...
 48 |  *   return result;
 49 |  *}
 50 |  *}
 51 |  * 
52 | * 53 | * @author Victor Porcar 54 | * 55 | */ 56 | 57 | public class DelayedBatchExecutor2 extends DelayedBatchExecutor { 58 | 59 | /** 60 | * Receive as argument a List of type A and returns a List of type Z. It can be 61 | * implemented as a lambda expression or method reference
62 | *
63 | * 64 | *
 65 | 	 * Lambda expression
 66 | 	 * {@code
 67 | 	 * DelayedBatchExecutor2 dbe = DelayedBatchExecutor2.create(Duration.ofMillis(50), 10, arg1List ->
 68 | 	 * {
 69 | 	 *      //arg1List is a List
 70 | 	 *      List result = ...
 71 | 	 *	    ...
 72 | 	 *      return result;
 73 | 	 *});
 74 | 	 *}
 75 | 	 * Method reference
 76 | 	 * {@code
 77 | 	 * DelayedBatchExecutor2 dbe = DelayedBatchExecutor2.create(Duration.ofMillis(50), 10, this::myBatchCallBack);
 78 | 	 * ...
 79 | 	 * List myBatchCallBack(List arg1List) {
 80 | 	 *      List result = ...
 81 | 	 *	    ...
 82 | 	 *      return result;
 83 | 	 *}
 84 | 	 *}
 85 | 	 * 
86 | * 87 | * @author Victor Porcar 88 | * 89 | */ 90 | @FunctionalInterface 91 | public interface BatchCallBack2 { 92 | List apply(List firstParam); 93 | } 94 | 95 | private final BatchCallBack2 batchCallBack; 96 | 97 | /** 98 | * Factory method to create an instance of a Delayed Batch Executor for one 99 | * argument of type A and return type Z. Similar to 100 | * {@link DelayedBatchExecutor2#create(Duration, int, ExecutorService, int, boolean, BatchCallBack2)} 101 | * defaulting to:
102 | *
103 | * -executorService: the one returned by static method 104 | * {@link #getDefaultExecutorService()}
105 | * -bufferQueueSize: the value of constant {@link #DEFAULT_BUFFER_QUEUE_SIZE} 106 | *
107 | * -removeDuplicates:true
108 | * 109 | * @param the return type 110 | * @param
the type of the argument 111 | * @param duration the time window, defined as {@link Duration }. 112 | * @param size the max collected size. As soon as the count of 113 | * collected parameters reaches this size, the 114 | * batchCallBack method is executed 115 | * @param batchCallback2 the method reference or lambda expression that receives 116 | * a list of type A and returns a list of Type Z (see 117 | * {@link BatchCallBack2}) 118 | * @return an instance of {@link DelayedBatchExecutor2} 119 | * 120 | */ 121 | 122 | public static DelayedBatchExecutor2 create(Duration duration, int size, 123 | BatchCallBack2 batchCallback2) { 124 | return new DelayedBatchExecutor2<>(duration, size, null, DEFAULT_BUFFER_QUEUE_SIZE, true, batchCallback2); 125 | } 126 | 127 | /** 128 | * Factory method to create an instance of a Delayed Batch Executor for one 129 | * argument of type A and return type Z
130 | * 131 | * @param the return type 132 | * @param
the type of the argument 133 | * @param duration the time window, defined as {@link Duration }. 134 | * @param size the max collected size. As soon as the count of 135 | * collected parameters reaches this size, the 136 | * batchCallBack method is executed 137 | * @param executorService to define the pool of threads to executed the 138 | * batchCallBack method in asynchronous mode 139 | * @param bufferQueueSize max size of the internal queue to buffer values 140 | * @param removeDuplicates if true then duplicated arguments from execute*(...) 141 | * methods are not passed to the batchCallBack 142 | * (considering same {@link Object#hashCode()} and being 143 | * {@link Object#equals(Object)}) 144 | * @param batchCallback2 the method reference or lambda expression that 145 | * receives a list of type A and returns a list of Type 146 | * Z (see {@link BatchCallBack2}) 147 | * @return an instance of {@link DelayedBatchExecutor2} 148 | * 149 | */ 150 | 151 | public static DelayedBatchExecutor2 create(Duration duration, int size, 152 | ExecutorService executorService, int bufferQueueSize, boolean removeDuplicates, 153 | BatchCallBack2 batchCallback2) { 154 | return new DelayedBatchExecutor2<>(duration, size, executorService, bufferQueueSize, removeDuplicates, 155 | batchCallback2); 156 | } 157 | 158 | private DelayedBatchExecutor2(Duration duration, int size, ExecutorService executorService, int bufferQueueSize, 159 | boolean removeDuplicates, BatchCallBack2 batchCallBack) { 160 | super(duration, size, executorService, bufferQueueSize, removeDuplicates); 161 | this.batchCallBack = batchCallBack; 162 | } 163 | 164 | /** 165 | * Return the result of type Z (blocking the thread until the result is 166 | * available), which is obtained from the returned list of the batchCallBack 167 | * method for the given argument
168 | *
169 | * It will throw any {@link RuntimeException} thrown inside of the 170 | * {@link BatchCallBack2 }
171 | * It will throw a {@link RuntimeException} if the internal buffer Queue of this 172 | * Delayed Batch Executor is full.
173 | *
174 | * blocking 175 | * 176 | * @param arg1 value of the argument of type A defined for this Delayed Batch 177 | * Executor 178 | * @return the result of type Z 179 | * 180 | * 181 | */ 182 | public Z execute(A arg1) { 183 | TupleBlocking tupleBlocking = new TupleBlocking<>(arg1); 184 | enlistTuple(tupleBlocking); 185 | Z value = tupleBlocking.getValueBlocking(); 186 | return value; 187 | } 188 | 189 | /** 190 | * Return a {@link Future } containing the corresponding value from the returned 191 | * list of the batchCallBack method for the given argument
192 | *
193 | * The invoking thread is not blocked. The result will be available by invoking 194 | * method {@link Future#get()} of the {@link Future }. This method will block 195 | * the thread until the result is available
196 | *
197 | * future
198 | * It will throw a {@link RuntimeException} if the internal buffer Queue of this 199 | * Delayed Batch Executor is full.
200 | * If a {@link RuntimeException} is thrown inside of the {@link BatchCallBack2 201 | * }, then it will be the cause of the checked Exception 202 | * {@link ExecutionException} thrown by {@link Future#get()} as per contract of 203 | * {@link Future#get()}
204 | *
205 | * 206 | * @param arg1 value of the argument of type A defined for this Delayed Batch 207 | * Executor 208 | * @return a {@link Future } for the result of type Z 209 | * 210 | */ 211 | public Future executeAsFuture(A arg1) { 212 | TupleFuture tupleFuture = new TupleFuture<>(arg1); 213 | enlistTuple(tupleFuture); 214 | Future future = tupleFuture.getFuture(); 215 | return future; 216 | } 217 | 218 | /** 219 | * Return a
Mono, 221 | * publishing the value obtained from the returned list of the batchCallBack 222 | * method for the given parameter.
223 | *
224 | * The invoking thread is not blocked
225 | *
226 | * mono
227 | * It will throw a {@link RuntimeException} if the internal buffer Queue of this 228 | * Delayed Batch Executor is full.
229 | * If a {@link RuntimeException} is thrown inside of the {@link BatchCallBack2 230 | * }, then it will be the propagated as any {@link RuntimeException } thrown 231 | * from Mono 233 | *
234 | *
235 | * 236 | * @param arg1 value of the argument of type A defined for this Delayed Batch 237 | * Executor 238 | * @return a Mono 240 | * for the result of type Z 241 | * 242 | * 243 | */ 244 | public Mono executeAsMono(A arg1) { 245 | TupleMono tupleMono = new TupleMono<>(arg1); 246 | enlistTuple(tupleMono); 247 | Mono mono = tupleMono.getMono(); 248 | return mono; 249 | } 250 | 251 | @SuppressWarnings("unchecked") 252 | @Override 253 | protected List getResultListFromBatchCallBack(List> transposedTupleList) { 254 | return batchCallBack.apply((List) transposedTupleList.get(0)); 255 | } 256 | } -------------------------------------------------------------------------------- /src/main/java/com/github/victormpcmun/delayedbatchexecutor/DelayedBatchExecutor3.java: -------------------------------------------------------------------------------- 1 | package com.github.victormpcmun.delayedbatchexecutor; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | import java.time.Duration; 6 | import java.util.List; 7 | import java.util.concurrent.ExecutionException; 8 | import java.util.concurrent.ExecutorService; 9 | import java.util.concurrent.Future; 10 | 11 | /** 12 | * Delayed Batch Executor for two arguments (of types A and B) and return type Z 13 | *
14 | * 15 | *
 16 |  * {@code
 17 |  * DelayedBatchExecutor3 dbe = DelayedBatchExecutor3.create(Duration.ofMillis(50), 10, this::myBatchCallback);
 18 |  *
 19 |  * ...
 20 |  *
 21 |  * public void usingDelayedBatchExecutor(Integer param1, Integer param2) {
 22 |  *
 23 |  *    // using blocking behaviour
 24 |  *    String stringResult1 = dbe.execute(param1, param2); // the thread will be blocked until the result is available
 25 |  *    // compute with stringResult1
 26 |  *
 27 |  *
 28 |  *    // using Future
 29 |  *    Future resultAsFutureString = dbe.executeAsFuture(param1, param2); // the thread will not  be blocked
 30 |  *    // compute something else
 31 |  *    String stringResult2 = resultAsFutureString.get();  // Blocks the thread if necessary for the computation to complete, and then retrieves its result.
 32 |  *    // compute with stringResult2
 33 |  *
 34 |  *
 35 |  *    // using Mono
 36 |  *    Mono monoResult = dbe.executeAsMono(param1, param2); // the thread will not  be blocked
 37 |  *    // compute something else
 38 |  *    monoResult.subscribe(stringResult3 -> {
 39 |  *     // compute with stringResult
 40 |  *    });
 41 |  * }
 42 |  *
 43 |  * ...
 44 |  *
 45 |  * List myBatchCallback(List arg1List, List arg2List) {
 46 |  *   List result = ...
 47 |  *   ...
 48 |  *   return result;
 49 |  *}
 50 |  *}
 51 |  * 
52 | * 53 | * @author Victor Porcar 54 | * 55 | */ 56 | public class DelayedBatchExecutor3 extends DelayedBatchExecutor { 57 | 58 | /** 59 | * Receive as argument two Lists of type A and B and returns a List of type Z. 60 | * It can be implemented as a lambda expression or method reference
61 | *
62 | * 63 | *
 64 | 	 * Lambda expression
 65 | 	 * {@code
 66 | 	 * DelayedBatchExecutor3 dbe = DelayedBatchExecutor3.create(Duration.ofMillis(50), 10, (arg1List, arg2List) ->
 67 | 	 * {
 68 | 	 *      //arg1List and arg2List are List
 69 | 	 *      List result = ...
 70 | 	 *	    ...
 71 | 	 *      return result;
 72 | 	 *});
 73 | 	 *}
 74 | 	 * Method reference
 75 | 	 * {@code
 76 | 	 * DelayedBatchExecutor3 dbe = DelayedBatchExecutor3.create(Duration.ofMillis(50), 10, this::myBatchCallBack);
 77 | 	 * ...
 78 | 	 * List myBatchCallBack(List arg1List, List arg2List) {
 79 | 	 *      List result = ...
 80 | 	 *	    ...
 81 | 	 *      return result;
 82 | 	 *}
 83 | 	 *}
 84 | 	 * 
85 | * 86 | * @author Victor Porcar 87 | * 88 | */ 89 | @FunctionalInterface 90 | public interface BatchCallBack3 { 91 | List apply(List
firstParam, List secondParam); 92 | } 93 | 94 | private final BatchCallBack3 batchCallBack; 95 | 96 | /** 97 | * Factory method to create an instance of a Delayed Batch Executor for two 98 | * arguments (of types A and B) and return type Z. Similar to 99 | * {@link DelayedBatchExecutor3#create(Duration, int, ExecutorService, int, boolean, BatchCallBack3)} 100 | * defaulting to:
101 | *
102 | * -executorService: the one returned by static method 103 | * {@link #getDefaultExecutorService()}
104 | * -bufferQueueSize: the value of constant {@link #DEFAULT_BUFFER_QUEUE_SIZE} 105 | *
106 | * -removeDuplicates:true
107 | * 108 | * @param the return type 109 | * @param
the type of the first argument 110 | * @param the type of the second argument 111 | * @param duration the time window, defined as {@link Duration }. 112 | * @param size the max collected size. As soon as the count of 113 | * collected parameters reaches this size, the 114 | * batchCallBack method is executed 115 | * @param batchCallback3 the method reference or lambda expression that receives 116 | * a list of type A and returns a list of Type Z (see 117 | * {@link BatchCallBack3}) 118 | * @return an instance of {@link DelayedBatchExecutor3} 119 | * 120 | */ 121 | 122 | public static DelayedBatchExecutor3 create(Duration duration, int size, 123 | BatchCallBack3 batchCallback3) { 124 | return new DelayedBatchExecutor3<>(duration, size, null, DEFAULT_BUFFER_QUEUE_SIZE, true, batchCallback3); 125 | } 126 | 127 | /** 128 | * Factory method to create an instance of a Delayed Batch Executor for two 129 | * arguments (of types A and B) and return type Z
130 | * 131 | * @param the return type 132 | * @param
the type of the first argument 133 | * @param the type of the second argument 134 | * @param duration the time window, defined as {@link Duration }. 135 | * @param size the max collected size. As soon as the count of 136 | * collected parameters reaches this size, the 137 | * batchCallBack method is executed 138 | * @param executorService to define the pool of threads to executed the 139 | * batchCallBack method in asynchronous mode 140 | * @param bufferQueueSize max size of the internal queue to buffer values 141 | * @param removeDuplicates if true then duplicated arguments from execute*(...) 142 | * methods are not passed to the batchCallBack 143 | * (considering same {@link Object#hashCode()} and being 144 | * {@link Object#equals(Object)}) 145 | * @param batchCallback3 the method reference or lambda expression that 146 | * receives a list of type A and returns a list of Type 147 | * Z (see {@link BatchCallBack3}) 148 | * @return an instance of {@link DelayedBatchExecutor3} 149 | * 150 | */ 151 | 152 | public static DelayedBatchExecutor3 create(Duration duration, int size, 153 | ExecutorService executorService, int bufferQueueSize, boolean removeDuplicates, 154 | BatchCallBack3 batchCallback3) { 155 | return new DelayedBatchExecutor3<>(duration, size, executorService, bufferQueueSize, removeDuplicates, 156 | batchCallback3); 157 | } 158 | 159 | private DelayedBatchExecutor3(Duration duration, int size, ExecutorService executorService, int bufferQueueSize, 160 | boolean removeDuplicates, BatchCallBack3 batchCallBack) { 161 | super(duration, size, executorService, bufferQueueSize, removeDuplicates); 162 | this.batchCallBack = batchCallBack; 163 | } 164 | 165 | /** 166 | * Return the result of type Z (blocking the thread until the result is 167 | * available), which is obtained from the returned list of the batchCallBack 168 | * method for the given argument
169 | *
170 | * It will throw any {@link RuntimeException} thrown inside of the 171 | * {@link BatchCallBack3 }
172 | * It will throw a {@link RuntimeException} if the internal buffer Queue of this 173 | * Delayed Batch Executor is full.
174 | *
175 | * blocking 176 | * 177 | * @param arg1 value of the first argument of type A defined for this Delayed 178 | * Batch Executor 179 | * @param arg2 value of the second argument of type B defined for this Delayed 180 | * Batch Executor 181 | * @return the result of type Z 182 | * 183 | * 184 | */ 185 | public Z execute(A arg1, B arg2) { 186 | TupleBlocking tupleBlocking = new TupleBlocking<>(arg1, arg2); 187 | enlistTuple(tupleBlocking); 188 | Z value = tupleBlocking.getValueBlocking(); 189 | return value; 190 | } 191 | 192 | /** 193 | * Return a {@link Future } containing the corresponding value from the returned 194 | * list of the batchCallBack method for the given argument
195 | *
196 | * The invoking thread is not blocked. The result will be available by invoking 197 | * method {@link Future#get()} of the {@link Future }. This method will block 198 | * the thread until the result is available
199 | *
200 | * future
201 | * It will throw a {@link RuntimeException} if the internal buffer Queue of this 202 | * Delayed Batch Executor is full.
203 | * If a {@link RuntimeException} is thrown inside of the {@link BatchCallBack3 204 | * }, then it will be the cause of the checked Exception 205 | * {@link ExecutionException} thrown by {@link Future#get()} as per contract of 206 | * {@link Future#get()}
207 | *
208 | * 209 | * @param arg1 value of the first argument of type A defined for this Delayed 210 | * Batch Executor 211 | * @param arg2 value of the second argument of type B defined for this Delayed 212 | * Batch Executor 213 | * @return a {@link Future } for the result of type Z 214 | * 215 | */ 216 | public Future executeAsFuture(A arg1, B arg2) { 217 | TupleFuture tupleFuture = new TupleFuture<>(arg1, arg2); 218 | enlistTuple(tupleFuture); 219 | Future future = tupleFuture.getFuture(); 220 | return future; 221 | } 222 | 223 | /** 224 | * Return a
Mono, 226 | * publishing the value obtained from the returned list of the batchCallBack 227 | * method for the given parameter.
228 | *
229 | * The invoking thread is not blocked
230 | *
231 | * mono
232 | * It will throw a {@link RuntimeException} if the internal buffer Queue of this 233 | * Delayed Batch Executor is full.
234 | * If a {@link RuntimeException} is thrown inside of the {@link BatchCallBack3}, 235 | * then it will be the propagated as any {@link RuntimeException } thrown from 236 | * Mono 238 | *
239 | *
240 | * 241 | * @param arg1 value of the first argument of type A defined for this Delayed 242 | * Batch Executor 243 | * @param arg2 value of the second argument of type B defined for this Delayed 244 | * Batch Executor 245 | * @return a Mono 247 | * for the result of type Z 248 | * 249 | * 250 | */ 251 | public Mono executeAsMono(A arg1, B arg2) { 252 | TupleMono tupleMono = new TupleMono<>(arg1, arg2); 253 | enlistTuple(tupleMono); 254 | Mono mono = tupleMono.getMono(); 255 | return mono; 256 | } 257 | 258 | @SuppressWarnings("unchecked") 259 | @Override 260 | protected List getResultListFromBatchCallBack(List> transposedTupleList) { 261 | return batchCallBack.apply((List) transposedTupleList.get(0), (List) transposedTupleList.get(1)); 262 | } 263 | } -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | -------------------------------------------------------------------------------- /src/main/java/com/github/victormpcmun/delayedbatchexecutor/DelayedBatchExecutor4.java: -------------------------------------------------------------------------------- 1 | package com.github.victormpcmun.delayedbatchexecutor; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | import java.time.Duration; 6 | import java.util.List; 7 | import java.util.concurrent.ExecutionException; 8 | import java.util.concurrent.ExecutorService; 9 | import java.util.concurrent.Future; 10 | 11 | /** 12 | * Delayed Batch Executor for three arguments (of types A,B and C) and return 13 | * type Z
14 | * 15 | *
 16 |  * {@code
 17 |  * DelayedBatchExecutor4 dbe = DelayedBatchExecutor4.create(Duration.ofMillis(50), 10, this::myBatchCallback);
 18 |  *
 19 |  * ...
 20 |  *
 21 |  * public void usingDelayedBatchExecutor(Integer param1, Integer param2, Integer param3) {
 22 |  *
 23 |  *    // using blocking behaviour
 24 |  *    String stringResult1 = dbe.execute(param1, param2, param3); // the thread will be blocked until the result is available
 25 |  *    // compute with stringResult1
 26 |  *
 27 |  *
 28 |  *    // using Future
 29 |  *    Future resultAsFutureString = dbe.executeAsFuture(param1, param2, param3); // the thread will not  be blocked
 30 |  *    // compute something else
 31 |  *    String stringResult2 = resultAsFutureString.get();  // Blocks the thread if necessary for the computation to complete, and then retrieves its result.
 32 |  *    // compute with stringResult2
 33 |  *
 34 |  *
 35 |  *    // using Mono
 36 |  *    Mono monoResult = dbe.executeAsMono(param1, param2, param3); // the thread will not  be blocked
 37 |  *    // compute something else
 38 |  *    monoResult.subscribe(stringResult -> {
 39 |  *     // compute with stringResult
 40 |  *    });
 41 |  * }
 42 |  *
 43 |  * ...
 44 |  *
 45 |  * List myBatchCallback(List arg1List, List arg2List, List arg3List) {
 46 |  *   List result = ...
 47 |  *   ...
 48 |  *   return result;
 49 |  *}
 50 |  *}
 51 |  * 
52 | * 53 | * @author Victor Porcar 54 | * 55 | */ 56 | 57 | public class DelayedBatchExecutor4 extends DelayedBatchExecutor { 58 | 59 | /** 60 | * Receive as argument three Lists of type A,B,C and returns a List of type Z. 61 | * It can be implemented as a lambda expression or method reference
62 | *
63 | * 64 | *
 65 | 	 * Lambda expression
 66 | 	 * {@code
 67 | 	 * DelayedBatchExecutor4 dbe = DelayedBatchExecutor4.create(Duration.ofMillis(50), 10, (arg1List, arg2List, arg3list) ->
 68 | 	 * {
 69 | 	 *      //arg1List,arg2List and arg3List are List
 70 | 	 *      List result = ...
 71 | 	 *	    ...
 72 | 	 *      return result;
 73 | 	 *});
 74 | 	 *}
 75 | 	 * Method reference
 76 | 	 * {@code
 77 | 	 * DelayedBatchExecutor4 dbe = DelayedBatchExecutor4.create(Duration.ofMillis(50), 10, this::myBatchCallBack);
 78 | 	 * ...
 79 | 	 * List myBatchCallBack(List arg1List, List arg2List, List arg3List) {
 80 | 	 *      List result = ...
 81 | 	 *	    ...
 82 | 	 *      return result;
 83 | 	 *}
 84 | 	 *}
 85 | 	 * 
86 | * 87 | * @author Victor Porcar 88 | * 89 | */ 90 | @FunctionalInterface 91 | public interface BatchCallBack4 { 92 | List apply(List
firstParam, List secondParam, List thirdParam); 93 | } 94 | 95 | private final BatchCallBack4 batchCallBack; 96 | 97 | /** 98 | * Factory method to create an instance of a Delayed Batch Executor for two 99 | * arguments (of types A,B and C) and return type Z. Similar to 100 | * {@link DelayedBatchExecutor4#create(Duration, int, ExecutorService, int, boolean, BatchCallBack4)} 101 | * defaulting to:
102 | *
103 | * -executorService: the one returned by static method 104 | * {@link #getDefaultExecutorService()}
105 | * -bufferQueueSize: the value of constant {@link #DEFAULT_BUFFER_QUEUE_SIZE} 106 | *
107 | * -removeDuplicates:true
108 | * 109 | * @param the return type 110 | * @param
the type of the first argument 111 | * @param the type of the second argument 112 | * @param the type of the third argument 113 | * @param duration the time window, defined as {@link Duration }. 114 | * @param size the max collected size. As soon as the count of 115 | * collected parameters reaches this size, the 116 | * batchCallBack method is executed 117 | * @param batchCallback4 the method reference or lambda expression that receives 118 | * a list of type A and returns a list of Type Z (see 119 | * {@link BatchCallBack4}) 120 | * @return an instance of {@link DelayedBatchExecutor4} 121 | * 122 | */ 123 | public static DelayedBatchExecutor4 create(Duration duration, int size, 124 | BatchCallBack4 batchCallback4) { 125 | return new DelayedBatchExecutor4<>(duration, size, null, DEFAULT_BUFFER_QUEUE_SIZE, true, batchCallback4); 126 | } 127 | 128 | /** 129 | * Factory method to create an instance of a Delayed Batch Executor for two 130 | * arguments (of types A,B and C) and return type Z
131 | * 132 | * @param the return type 133 | * @param
the type of the first argument 134 | * @param the type of the second argument 135 | * @param the type of the third argument 136 | * @param duration the time window, defined as {@link Duration }. 137 | * @param size the max collected size. As soon as the count of 138 | * collected parameters reaches this size, the 139 | * batchCallBack method is executed 140 | * @param executorService to define the pool of threads to executed the 141 | * batchCallBack method in asynchronous mode 142 | * @param bufferQueueSize max size of the internal queue to buffer values. 143 | * @param removeDuplicates if true then duplicated arguments from execute*(...) 144 | * methods are not passed to the batchCallBack 145 | * (considering same {@link Object#hashCode()} and being 146 | * {@link Object#equals(Object)}) 147 | * @param batchCallback4 the method reference or lambda expression that 148 | * receives a list of type A and returns a list of Type 149 | * Z (see {@link BatchCallBack4}) 150 | * @return an instance of {@link DelayedBatchExecutor4} 151 | * 152 | */ 153 | 154 | public static DelayedBatchExecutor4 create(Duration duration, int size, 155 | ExecutorService executorService, int bufferQueueSize, boolean removeDuplicates, 156 | BatchCallBack4 batchCallback4) { 157 | return new DelayedBatchExecutor4<>(duration, size, executorService, bufferQueueSize, removeDuplicates, 158 | batchCallback4); 159 | } 160 | 161 | private DelayedBatchExecutor4(Duration duration, int size, ExecutorService executorService, int bufferQueueSize, 162 | boolean removeDuplicates, BatchCallBack4 batchCallBack) { 163 | super(duration, size, executorService, bufferQueueSize, removeDuplicates); 164 | this.batchCallBack = batchCallBack; 165 | } 166 | 167 | /** 168 | * Return the result of type Z (blocking the thread until the result is 169 | * available), which is obtained from the returned list of the batchCallBack 170 | * method for the given argument
171 | *
172 | * It will throw any {@link RuntimeException} thrown inside of the 173 | * {@link BatchCallBack4}
174 | * It will throw a {@link RuntimeException} if the internal buffer Queue of this 175 | * Delayed Batch Executor is full.
176 | *
177 | * blocking 178 | * 179 | * @param arg1 value of the first argument of type A defined for this Delayed 180 | * Batch Executor 181 | * @param arg2 value of the second argument of type B defined for this Delayed 182 | * Batch Executor 183 | * @param arg3 value of the third argument of type C defined for this Delayed 184 | * Batch Executor 185 | * @return the result of type Z 186 | * 187 | * 188 | */ 189 | public Z execute(A arg1, B arg2, C arg3) { 190 | TupleBlocking tupleBlocking = new TupleBlocking<>(arg1, arg2, arg3); 191 | enlistTuple(tupleBlocking); 192 | Z value = tupleBlocking.getValueBlocking(); 193 | return value; 194 | } 195 | 196 | /** 197 | * Return a {@link Future } containing the corresponding value from the returned 198 | * list of the batchCallBack method for the given argument
199 | *
200 | * The invoking thread is not blocked. The result will be available by invoking 201 | * method {@link Future#get()} of the {@link Future }. This method will block 202 | * the thread until the result is available
203 | *
204 | * future
205 | * It will throw a {@link RuntimeException} if the internal buffer Queue of this 206 | * Delayed Batch Executor is full.
207 | * If a {@link RuntimeException} is thrown inside of the {@link BatchCallBack4}, 208 | * then it will be the cause of the checked Exception {@link ExecutionException} 209 | * thrown by {@link Future#get()} as per contract of {@link Future#get()}
210 | *
211 | * 212 | * @param arg1 value of the first argument of type A defined for this Delayed 213 | * Batch Executor 214 | * @param arg2 value of the second argument of type B defined for this Delayed 215 | * Batch Executor 216 | * @param arg3 value of the third argument of type C defined for this Delayed 217 | * Batch Executor 218 | * @return a {@link Future } for the result of type Z 219 | * 220 | */ 221 | public Future executeAsFuture(A arg1, B arg2, C arg3) { 222 | TupleFuture tupleFuture = new TupleFuture<>(arg1, arg2, arg3); 223 | enlistTuple(tupleFuture); 224 | Future future = tupleFuture.getFuture(); 225 | return future; 226 | } 227 | 228 | /** 229 | * Return a
Mono, 231 | * publishing the value obtained from the returned list of the batchCallBack 232 | * method for the given parameter.
233 | *
234 | * The invoking thread is not blocked
235 | *
236 | * mono
237 | * It will throw a {@link RuntimeException} if the internal buffer Queue of this 238 | * Delayed Batch Executor is full.
239 | * If a {@link RuntimeException} is thrown inside of the {@link BatchCallBack4}, 240 | * then it will be the propagated as any {@link RuntimeException } thrown from 241 | * Mono 243 | *
244 | *
245 | * 246 | * @param arg1 value of the first argument of type A defined for this Delayed 247 | * Batch Executor 248 | * @param arg2 value of the second argument of type B defined for this Delayed 249 | * Batch Executor 250 | * @param arg3 value of the third argument of type C defined for this Delayed 251 | * Batch Executor 252 | * @return a Mono 254 | * for the result of type Z 255 | * 256 | * 257 | */ 258 | public Mono executeAsMono(A arg1, B arg2, C arg3) { 259 | TupleMono tupleMono = new TupleMono<>(arg1, arg2, arg3); 260 | enlistTuple(tupleMono); 261 | Mono mono = tupleMono.getMono(); 262 | return mono; 263 | } 264 | 265 | @SuppressWarnings("unchecked") 266 | @Override 267 | protected List getResultListFromBatchCallBack(List> transposedTupleList) { 268 | return batchCallBack.apply((List) transposedTupleList.get(0), (List) transposedTupleList.get(1), 269 | (List) transposedTupleList.get(2)); 270 | } 271 | } -------------------------------------------------------------------------------- /src/main/java/com/github/victormpcmun/delayedbatchexecutor/DelayedBatchExecutor5.java: -------------------------------------------------------------------------------- 1 | package com.github.victormpcmun.delayedbatchexecutor; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | import java.time.Duration; 6 | import java.util.List; 7 | import java.util.concurrent.ExecutionException; 8 | import java.util.concurrent.ExecutorService; 9 | import java.util.concurrent.Future; 10 | 11 | /** 12 | * Delayed Batch Executor for four arguments (of types A,B,C and D) and return 13 | * type Z
14 | * 15 | *
 16 |  * {@code
 17 |  * DelayedBatchExecutor5 dbe = DelayedBatchExecutor5.create(Duration.ofMillis(50), 10, this::myBatchCallback);
 18 |  *
 19 |  * ...
 20 |  *
 21 |  * public void usingDelayedBatchExecutor(Integer param1, Integer param2, Integer param3, Integer param4) {
 22 |  *
 23 |  *    // using blocking behaviour
 24 |  *    String stringResult1 = dbe.execute(param1, param2, param3,param4); // the thread will be blocked until the result is available
 25 |  *    // compute with stringResult1
 26 |  *
 27 |  *
 28 |  *    // using Future
 29 |  *    Future resultAsFutureString = dbe.executeAsFuture(param1, param2, param3, param4); // the thread will not  be blocked
 30 |  *    // compute something else
 31 |  *    String stringResult2 = resultAsFutureString.get();  // Blocks the thread if necessary for the computation to complete, and then retrieves its result.
 32 |  *    // compute with stringResult2
 33 |  *
 34 |  *
 35 |  *    // using Mono
 36 |  *    Mono monoResult = dbe.executeAsMono(param1, param2, param3, param4); // the thread will not  be blocked
 37 |  *    // compute something else
 38 |  *    monoResult.subscribe(stringResult -> {
 39 |  *     // compute with stringResult
 40 |  *    });
 41 |  * }
 42 |  *
 43 |  * ...
 44 |  *
 45 |  * List myBatchCallback(List arg1List, List arg2List, List arg3List, List arg4List) {
 46 |  *   List result = ...
 47 |  *   ...
 48 |  *   return result;
 49 |  *}
 50 |  *}
 51 |  * 
52 | * 53 | * @author Victor Porcar 54 | * 55 | */ 56 | 57 | public class DelayedBatchExecutor5 extends DelayedBatchExecutor { 58 | 59 | /** 60 | * Receive as argument four Lists of type A,B,C,D and returns a List of type Z. 61 | * It can be implemented as a lambda expression or method reference
62 | *
63 | * 64 | *
 65 | 	 * Lambda expression
 66 | 	 * {@code
 67 | 	 * DelayedBatchExecutor5 dbe = DelayedBatchExecutor5.create(Duration.ofMillis(50), 10, (arg1List, arg2List, arg3list,arg4List) ->
 68 | 	 * {
 69 | 	 *      //arg1List,arg2List,arg3List and arg4List are List
 70 | 	 *      List result = ...
 71 | 	 *	    ...
 72 | 	 *      return result;
 73 | 	 *});
 74 | 	 *}
 75 | 	 * Method reference
 76 | 	 * {@code
 77 | 	 * DelayedBatchExecutor5 dbe = DelayedBatchExecutor5.create(Duration.ofMillis(50), 10, this::myBatchCallBack);
 78 | 	 * ...
 79 | 	 * List myBatchCallBack(List arg1List, List arg2List, List arg3List, List arg4List) {
 80 | 	 *      List result = ...
 81 | 	 *	    ...
 82 | 	 *      return result;
 83 | 	 *}
 84 | 	 *}
 85 | 	 * 
86 | * 87 | * @author Victor Porcar 88 | * 89 | */ 90 | @FunctionalInterface 91 | public interface BatchCallBack5 { 92 | List apply(List
firstParam, List secondParam, List thirdParam, List fourthParam); 93 | } 94 | 95 | private final BatchCallBack5 batchCallBack; 96 | 97 | /** 98 | * Factory method to create an instance of a Delayed Batch Executor for two 99 | * arguments (of types A,B,C and D) and return type Z. Similar to 100 | * {@link DelayedBatchExecutor5#create(Duration, int, ExecutorService, int, boolean, BatchCallBack5)} 101 | * defaulting to:
102 | *
103 | * -executorService: the one returned by static method 104 | * {@link #getDefaultExecutorService()}
105 | * -bufferQueueSize: the value of constant {@link #DEFAULT_BUFFER_QUEUE_SIZE} 106 | *
107 | * -removeDuplicates:true
108 | * 109 | * @param the return type 110 | * @param
the type of the first argument 111 | * @param the type of the second argument 112 | * @param the type of the third argument 113 | * @param the type of the fourth argument 114 | * @param duration the time window, defined as {@link Duration }. 115 | * @param size the max collected size. As soon as the count of 116 | * collected parameters reaches this size, the 117 | * batchCallBack method is executed 118 | * @param batchCallback5 the method reference or lambda expression that receives 119 | * a list of type A and returns a list of Type Z (see 120 | * {@link BatchCallBack5}) 121 | * @return an instance of {@link DelayedBatchExecutor5} 122 | * 123 | */ 124 | public static DelayedBatchExecutor5 create(Duration duration, int size, 125 | BatchCallBack5 batchCallback5) { 126 | return new DelayedBatchExecutor5<>(duration, size, null, DEFAULT_BUFFER_QUEUE_SIZE, true, batchCallback5); 127 | } 128 | 129 | /** 130 | * Factory method to create an instance of a Delayed Batch Executor for two 131 | * arguments (of types A,B,C and D) and return type Z
132 | * 133 | * @param the return type 134 | * @param
the type of the first argument 135 | * @param the type of the second argument 136 | * @param the type of the third argument 137 | * @param the type of the fourth argument 138 | * @param duration the time window, defined as {@link Duration }. 139 | * @param size the max collected size. As soon as the count of 140 | * collected parameters reaches this size, the 141 | * batchCallBack method is executed 142 | * @param executorService to define the pool of threads to executed the 143 | * batchCallBack method in asynchronous mode 144 | * @param bufferQueueSize max size of the internal queue to buffer values 145 | * @param removeDuplicates if true then duplicated arguments from execute*(...) 146 | * methods are not passed to the batchCallBack 147 | * (considering same {@link Object#hashCode()} and being 148 | * {@link Object#equals(Object)}) 149 | * @param batchCallback5 the method reference or lambda expression that 150 | * receives a list of type A and returns a list of Type 151 | * Z (see {@link BatchCallBack5}) 152 | * @return an instance of {@link DelayedBatchExecutor5} 153 | * 154 | */ 155 | public static DelayedBatchExecutor5 create(Duration duration, int size, 156 | ExecutorService executorService, int bufferQueueSize, boolean removeDuplicates, 157 | BatchCallBack5 batchCallback5) { 158 | return new DelayedBatchExecutor5<>(duration, size, executorService, bufferQueueSize, removeDuplicates, 159 | batchCallback5); 160 | } 161 | 162 | private DelayedBatchExecutor5(Duration duration, int size, ExecutorService executorService, int bufferQueueSize, 163 | boolean removeDuplicates, BatchCallBack5 batchCallBack) { 164 | super(duration, size, executorService, bufferQueueSize, removeDuplicates); 165 | this.batchCallBack = batchCallBack; 166 | } 167 | 168 | /** 169 | * Return the result of type Z (blocking the thread until the result is 170 | * available), which is obtained from the returned list of the batchCallBack 171 | * method for the given argument
172 | *
173 | * It will throw any {@link RuntimeException} thrown inside of the 174 | * {@link BatchCallBack5 }
175 | * It will throw a {@link RuntimeException} if the internal buffer Queue of this 176 | * Delayed Batch Executor is full.
177 | *
178 | * blocking 179 | * 180 | * @param arg1 value of the first argument of type A defined for this Delayed 181 | * Batch Executor 182 | * @param arg2 value of the second argument of type B defined for this Delayed 183 | * Batch Executor 184 | * @param arg3 value of the third argument of type C defined for this Delayed 185 | * Batch Executor 186 | * @param arg4 value of the fourth argument of type D defined for this Delayed 187 | * Batch Executor 188 | * @return the result of type Z 189 | * 190 | * 191 | */ 192 | public Z execute(A arg1, B arg2, C arg3, D arg4) { 193 | TupleBlocking tupleBlocking = new TupleBlocking<>(arg1, arg2, arg3, arg4); 194 | enlistTuple(tupleBlocking); 195 | Z value = tupleBlocking.getValueBlocking(); 196 | return value; 197 | } 198 | 199 | /** 200 | * Return a {@link Future } containing the corresponding value from the returned 201 | * list of the batchCallBack method for the given argument
202 | *
203 | * The invoking thread is not blocked. The result will be available by invoking 204 | * method {@link Future#get()} of the {@link Future }. This method will block 205 | * the thread until the result is available
206 | *
207 | * future
208 | * It will throw a {@link RuntimeException} if the internal buffer Queue of this 209 | * Delayed Batch Executor is full.
210 | * If a {@link RuntimeException} is thrown inside of the {@link BatchCallBack5 211 | * }, then it will be the cause of the checked Exception 212 | * {@link ExecutionException} thrown by {@link Future#get()} as per contract of 213 | * {@link Future#get()}
214 | *
215 | * 216 | * @param arg1 value of the first argument of type A defined for this Delayed 217 | * Batch Executor 218 | * @param arg2 value of the second argument of type B defined for this Delayed 219 | * Batch Executor 220 | * @param arg3 value of the third argument of type C defined for this Delayed 221 | * Batch Executor 222 | * @param arg4 value of the fourth argument of type D defined for this Delayed 223 | * Batch Executor 224 | * @return a {@link Future } for the result of type Z 225 | * 226 | */ 227 | public Future executeAsFuture(A arg1, B arg2, C arg3, D arg4) { 228 | TupleFuture tupleFuture = new TupleFuture<>(arg1, arg2, arg3, arg4); 229 | enlistTuple(tupleFuture); 230 | Future future = tupleFuture.getFuture(); 231 | return future; 232 | } 233 | 234 | /** 235 | * Return a
Mono, 237 | * publishing the value obtained from the returned list of the batchCallBack 238 | * method for the given parameter.
239 | *
240 | * The invoking thread is not blocked
241 | *
242 | * mono
243 | * It will throw a {@link RuntimeException} if the internal buffer Queue of this 244 | * Delayed Batch Executor is full.
245 | * If a {@link RuntimeException} is thrown inside of the {@link BatchCallBack5}, 246 | * then it will be the propagated as any {@link RuntimeException } thrown from 247 | * Mono 249 | *
250 | *
251 | * 252 | * @param arg1 value of the first argument of type A defined for this Delayed 253 | * Batch Executor 254 | * @param arg2 value of the second argument of type B defined for this Delayed 255 | * Batch Executor 256 | * @param arg3 value of the third argument of type C defined for this Delayed 257 | * Batch Executor 258 | * @param arg4 value of the fourth argument of type D defined for this Delayed 259 | * Batch Executor 260 | * @return a Mono 262 | * for the result of type Z 263 | * 264 | * 265 | */ 266 | public Mono executeAsMono(A arg1, B arg2, C arg3, D arg4) { 267 | TupleMono tupleMono = new TupleMono<>(arg1, arg2, arg3, arg4); 268 | enlistTuple(tupleMono); 269 | Mono mono = tupleMono.getMono(); 270 | return mono; 271 | } 272 | 273 | @SuppressWarnings("unchecked") 274 | @Override 275 | protected List getResultListFromBatchCallBack(List> transposedTupleList) { 276 | return batchCallBack.apply((List) transposedTupleList.get(0), (List) transposedTupleList.get(1), 277 | (List) transposedTupleList.get(2), (List) transposedTupleList.get(3)); 278 | } 279 | } -------------------------------------------------------------------------------- /src/main/java/com/github/victormpcmun/delayedbatchexecutor/DelayedBatchExecutor6.java: -------------------------------------------------------------------------------- 1 | package com.github.victormpcmun.delayedbatchexecutor; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | import java.time.Duration; 6 | import java.util.List; 7 | import java.util.concurrent.ExecutionException; 8 | import java.util.concurrent.ExecutorService; 9 | import java.util.concurrent.Future; 10 | 11 | /** 12 | * Delayed Batch Executor for five arguments (of types A,B,C,D and E) and return 13 | * type Z
14 | * 15 | *
 16 |  * {@code
 17 |  * DelayedBatchExecutor6 dbe = DelayedBatchExecutor6.create(Duration.ofMillis(50), 10, this::myBatchCallback);
 18 |  *
 19 |  * ...
 20 |  *
 21 |  * public void usingDelayedBatchExecutor(Integer param1, Integer param2, Integer param3, Integer param4,Integer param5) {
 22 |  *
 23 |  *    // using blocking behaviour
 24 |  *    String stringResult1 = dbe.execute(param1, param2, param3, param4, param5); // the thread will be blocked until the result is available
 25 |  *    // compute with stringResult1
 26 |  *
 27 |  *
 28 |  *    // using Future
 29 |  *    Future resultAsFutureString = dbe.executeAsFuture(param1, param2, param3, param4, param5); // the thread will not  be blocked
 30 |  *    // compute something else
 31 |  *    String stringResult2 = resultAsFutureString.get();  // Blocks the thread if necessary for the computation to complete, and then retrieves its result.
 32 |  *    // compute with stringResult2
 33 |  *
 34 |  *
 35 |  *    // using Mono
 36 |  *    Mono monoResult = dbe.executeAsMono(param1, param2, param3, param4, param5); // the thread will not  be blocked
 37 |  *    // compute something else
 38 |  *    monoResult.subscribe(stringResult3 -> {
 39 |  *     // compute with stringResult3
 40 |  *    });
 41 |  * }
 42 |  *
 43 |  * ...
 44 |  *
 45 |  * List myBatchCallback(List arg1List, List arg2List, List arg3List, List arg4List, List arg5List) {
 46 |  *   List result = ...
 47 |  *   ...
 48 |  *   return result;
 49 |  *}
 50 |  *}
 51 |  * 
52 | * 53 | * @author Victor Porcar 54 | * 55 | */ 56 | 57 | public class DelayedBatchExecutor6 extends DelayedBatchExecutor { 58 | 59 | /** 60 | * Receive as argument four Lists of type A,B,C,D,E and returns a List of type 61 | * Z. It can be implemented as a lambda expression or method reference
62 | *
63 | * 64 | *
 65 | 	 * Lambda expression
 66 | 	 * {@code
 67 | 	 * DelayedBatchExecutor6 dbe = DelayedBatchExecutor6.create(Duration.ofMillis(50), 10, (arg1List, arg2List, arg3list,arg4List,arg5List) ->
 68 | 	 * {
 69 | 	 *      //arg1List,arg2List,arg3List,arg4List and arg5List are List
 70 | 	 *      List result = ...
 71 | 	 *	    ...
 72 | 	 *      return result;
 73 | 	 *});
 74 | 	 *}
 75 | 	 * Method reference
 76 | 	 * {@code
 77 | 	 * DelayedBatchExecutor6 dbe = DelayedBatchExecutor6.create(Duration.ofMillis(50), 10, this::myBatchCallBack);
 78 | 	 * ...
 79 | 	 * List myBatchCallBack(List arg1List, List arg2List, List arg3List, List arg4List, List arg5List) {
 80 | 	 *      List result = ...
 81 | 	 *	    ...
 82 | 	 *      return result;
 83 | 	 *}
 84 | 	 *}
 85 | 	 * 
86 | * 87 | * @author Victor Porcar 88 | * 89 | */ 90 | @FunctionalInterface 91 | public interface BatchCallBack6 { 92 | List apply(List
firstParam, List secondParam, List thirdParam, List fourthParam, 93 | List fifthParam); 94 | } 95 | 96 | private final BatchCallBack6 batchCallBack; 97 | 98 | /** 99 | * Factory method to create an instance of a Delayed Batch Executor for two 100 | * arguments (of types A,B,C,D and E) and return type Z. Similar to 101 | * {@link DelayedBatchExecutor6#create(Duration, int, ExecutorService, int, boolean, BatchCallBack6)} 102 | * defaulting to:
103 | *
104 | * -executorService: the one returned by static method 105 | * {@link #getDefaultExecutorService()}
106 | * -bufferQueueSize: the value of constant {@link #DEFAULT_BUFFER_QUEUE_SIZE} 107 | *
108 | * -removeDuplicates:true
109 | * 110 | * @param the return type 111 | * @param
the type of the first argument 112 | * @param the type of the second argument 113 | * @param the type of the third argument 114 | * @param the type of the fourth argument 115 | * @param the type of the fifth argument 116 | * @param duration the time window, defined as {@link Duration }. 117 | * @param size the max collected size. As soon as the count of 118 | * collected parameters reaches this size, the 119 | * batchCallBack method is executed 120 | * @param batchCallback6 the method reference or lambda expression that receives 121 | * a list of type A and returns a list of Type Z (see 122 | * {@link BatchCallBack6}) 123 | * @return an instance of {@link DelayedBatchExecutor6} 124 | * 125 | */ 126 | public static DelayedBatchExecutor6 create(Duration duration, int size, 127 | BatchCallBack6 batchCallback6) { 128 | return new DelayedBatchExecutor6<>(duration, size, null, DEFAULT_BUFFER_QUEUE_SIZE, true, batchCallback6); 129 | } 130 | 131 | /** 132 | * Factory method to create an instance of a Delayed Batch Executor for two 133 | * arguments (of types A,B,C,D and E) and return type Z
134 | * 135 | * @param the return type 136 | * @param
the type of the first argument 137 | * @param the type of the second argument 138 | * @param the type of the third argument 139 | * @param the type of the fourth argument 140 | * @param the type of the fifth argument 141 | * @param duration the time window, defined as {@link Duration }. 142 | * @param size the max collected size. As soon as the count of 143 | * collected parameters reaches this size, the 144 | * batchCallBack method is executed 145 | * @param executorService to define the pool of threads to executed the 146 | * batchCallBack method in asynchronous mode 147 | * @param bufferQueueSize max size of the internal queue to buffer values. 148 | * @param removeDuplicates if true then duplicated arguments from execute*(...) 149 | * methods are not passed to the batchCallBack 150 | * (considering same {@link Object#hashCode()} and being 151 | * {@link Object#equals(Object)}) 152 | * @param batchCallback6 the method reference or lambda expression that 153 | * receives a list of type A and returns a list of Type 154 | * Z (see {@link BatchCallBack6}) 155 | * @return an instance of {@link DelayedBatchExecutor6} 156 | * 157 | */ 158 | 159 | public static DelayedBatchExecutor6 create(Duration duration, int size, 160 | ExecutorService executorService, int bufferQueueSize, boolean removeDuplicates, 161 | BatchCallBack6 batchCallback6) { 162 | return new DelayedBatchExecutor6<>(duration, size, executorService, bufferQueueSize, removeDuplicates, 163 | batchCallback6); 164 | } 165 | 166 | private DelayedBatchExecutor6(Duration duration, int size, ExecutorService executorService, int bufferQueueSize, 167 | boolean removeDuplicates, BatchCallBack6 batchCallBack) { 168 | super(duration, size, executorService, bufferQueueSize, removeDuplicates); 169 | this.batchCallBack = batchCallBack; 170 | } 171 | 172 | /** 173 | * Return the result of type Z (blocking the thread until the result is 174 | * available), which is obtained from the returned list of the batchCallBack 175 | * method for the given argument
176 | *
177 | * It will throw any {@link RuntimeException} thrown inside of the 178 | * {@link BatchCallBack6 }
179 | * It will throw a {@link RuntimeException} if the internal buffer Queue of this 180 | * Delayed Batch Executor is full.
181 | *
182 | * blocking 183 | * 184 | * @param arg1 value of the first argument of type A defined for this Delayed 185 | * Batch Executor 186 | * @param arg2 value of the second argument of type B defined for this Delayed 187 | * Batch Executor 188 | * @param arg3 value of the third argument of type C defined for this Delayed 189 | * Batch Executor 190 | * @param arg4 value of the fourth argument of type D defined for this Delayed 191 | * Batch Executor 192 | * @param arg5 value of the fifth argument of type E defined for this Delayed 193 | * Batch Executor 194 | * @return the result of type Z 195 | * 196 | * 197 | */ 198 | public Z execute(A arg1, B arg2, C arg3, D arg4, E arg5) { 199 | TupleBlocking tupleBlocking = new TupleBlocking<>(arg1, arg2, arg3, arg4, arg5); 200 | enlistTuple(tupleBlocking); 201 | Z value = tupleBlocking.getValueBlocking(); 202 | return value; 203 | } 204 | 205 | /** 206 | * Return a {@link Future } containing the corresponding value from the returned 207 | * list of the batchCallBack method for the given argument
208 | *
209 | * The invoking thread is not blocked. The result will be available by invoking 210 | * method {@link Future#get()} of the {@link Future }. This method will block 211 | * the thread until the result is available
212 | *
213 | * future
214 | * It will throw a {@link RuntimeException} if the internal buffer Queue of this 215 | * Delayed Batch Executor is full.
216 | * If a {@link RuntimeException} is thrown inside of the {@link BatchCallBack6 217 | * }, then it will be the cause of the checked Exception 218 | * {@link ExecutionException} thrown by {@link Future#get()} as per contract of 219 | * {@link Future#get()}
220 | *
221 | * 222 | * @param arg1 value of the first argument of type A defined for this Delayed 223 | * Batch Executor 224 | * @param arg2 value of the second argument of type B defined for this Delayed 225 | * Batch Executor 226 | * @param arg3 value of the third argument of type C defined for this Delayed 227 | * Batch Executor 228 | * @param arg4 value of the fourth argument of type D defined for this Delayed 229 | * Batch Executor 230 | * @param arg5 value of the fifth argument of type E defined for this Delayed 231 | * Batch Executor 232 | * @return a {@link Future } for the result of type Z 233 | * 234 | */ 235 | public Future executeAsFuture(A arg1, B arg2, C arg3, D arg4, E arg5) { 236 | TupleFuture tupleFuture = new TupleFuture<>(arg1, arg2, arg3, arg4, arg5); 237 | enlistTuple(tupleFuture); 238 | Future future = tupleFuture.getFuture(); 239 | return future; 240 | } 241 | 242 | /** 243 | * Return a
Mono, 245 | * publishing the value obtained from the returned list of the batchCallBack 246 | * method for the given parameter.
247 | *
248 | * The invoking thread is not blocked
249 | *
250 | * mono
251 | * It will throw a {@link RuntimeException} if the internal buffer Queue of this 252 | * Delayed Batch Executor is full.
253 | * If a {@link RuntimeException} is thrown inside of the {@link BatchCallBack6}, 254 | * then it will be the propagated as any {@link RuntimeException } thrown from 255 | * Mono 257 | *
258 | *
259 | * 260 | * @param arg1 value of the first argument of type A defined for this Delayed 261 | * Batch Executor 262 | * @param arg2 value of the second argument of type B defined for this Delayed 263 | * Batch Executor 264 | * @param arg3 value of the third argument of type C defined for this Delayed 265 | * Batch Executor 266 | * @param arg4 value of the fourth argument of type D defined for this Delayed 267 | * Batch Executor 268 | * @param arg5 value of the fifth argument of type E defined for this Delayed 269 | * Batch Executor 270 | * @return a Mono 272 | * for the result of type Z 273 | * 274 | * 275 | */ 276 | public Mono executeAsMono(A arg1, B arg2, C arg3, D arg4, E arg5) { 277 | TupleMono tupleMono = new TupleMono<>(arg1, arg2, arg3, arg4, arg5); 278 | enlistTuple(tupleMono); 279 | Mono mono = tupleMono.getMono(); 280 | return mono; 281 | } 282 | 283 | @SuppressWarnings("unchecked") 284 | @Override 285 | protected List getResultListFromBatchCallBack(List> transposedTupleList) { 286 | return batchCallBack.apply((List) transposedTupleList.get(0), (List) transposedTupleList.get(1), 287 | (List) transposedTupleList.get(2), (List) transposedTupleList.get(3), 288 | (List) transposedTupleList.get(4)); 289 | } 290 | } -------------------------------------------------------------------------------- /src/main/java/com/github/victormpcmun/delayedbatchexecutor/DelayedBatchExecutor7.java: -------------------------------------------------------------------------------- 1 | package com.github.victormpcmun.delayedbatchexecutor; 2 | 3 | import reactor.core.publisher.Mono; 4 | 5 | import java.time.Duration; 6 | import java.util.List; 7 | import java.util.concurrent.ExecutionException; 8 | import java.util.concurrent.ExecutorService; 9 | import java.util.concurrent.Future; 10 | 11 | /** 12 | * Delayed Batch Executor for six arguments (of types A,B,C,D,E,F) and return 13 | * type Z
14 | * 15 | *
 16 |  * {@code
 17 |  * DelayedBatchExecutor7 dbe = DelayedBatchExecutor7.create(Duration.ofMillis(50), 10, this::myBatchCallback);
 18 |  *
 19 |  * ...
 20 |  *
 21 |  * public void usingDelayedBatchExecutor(Integer param1, Integer param2, Integer param3, Integer param4,Integer param5,Integer param6) {
 22 |  *
 23 |  *    // using blocking behaviour
 24 |  *    String stringResult1 = dbe.execute(param1, param2, param3, param4, param5, param6); // the thread will be blocked until the result is available
 25 |  *    // compute with stringResult1
 26 |  *
 27 |  *
 28 |  *    // using Future
 29 |  *    Future resultAsFutureString = dbe.executeAsFuture(param1, param2, param3, param4, param5, param6); // the thread will not  be blocked
 30 |  *    // compute something else
 31 |  *    String stringResult2 = resultAsFutureString.get();  // Blocks the thread if necessary for the computation to complete, and then retrieves its result.
 32 |  *    // compute with stringResult2
 33 |  *
 34 |  *
 35 |  *    // using Mono
 36 |  *    Mono monoResult = dbe.executeAsMono(param1, param2, param3, param4, param5, param6); // the thread will not  be blocked
 37 |  *    // compute something else
 38 |  *    monoResult.subscribe(stringResult3 -> {
 39 |  *     // compute with stringResult3
 40 |  *    });
 41 |  * }
 42 |  *
 43 |  * ...
 44 |  *
 45 |  * List myBatchCallback(List arg1List, List arg2List, List arg3List, List arg4List, List arg5List, List arg6List) {
 46 |  *   List result = ...
 47 |  *   ...
 48 |  *   return result;
 49 |  *}
 50 |  *}
 51 |  * 
52 | * 53 | * @author Victor Porcar 54 | * 55 | */ 56 | 57 | public class DelayedBatchExecutor7 extends DelayedBatchExecutor { 58 | 59 | /** 60 | * Receive as argument six Lists of type A,B,C,D,E,F and returns a List of type 61 | * Z. It can be implemented as a lambda expression or method reference
62 | *
63 | * 64 | *
 65 | 	 * Lambda expression
 66 | 	 * {@code
 67 | 	 * DelayedBatchExecutor7 dbe = DelayedBatchExecutor7.create(Duration.ofMillis(50), 10, (arg1List, arg2List, arg3list, arg4List, arg5List, arg6List) ->
 68 | 	 * {
 69 | 	 *      //arg1List,arg2List,arg3List,arg4List, arg5List and arg6List are List
 70 | 	 *      List result = ...
 71 | 	 *	    ...
 72 | 	 *      return result;
 73 | 	 *});
 74 | 	 *}
 75 | 	 * Method reference
 76 | 	 * {@code
 77 | 	 * DelayedBatchExecutor7 dbe = DelayedBatchExecutor7.create(Duration.ofMillis(50), 10, this::myBatchCallBack);
 78 | 	 * ...
 79 | 	 * List myBatchCallBack(List arg1List, List arg2List, List arg3List, List arg4List, List arg5List, List arg6List) {
 80 | 	 *      List result = ...
 81 | 	 *	    ...
 82 | 	 *      return result;
 83 | 	 *}
 84 | 	 *}
 85 | 	 * 
86 | * 87 | * @author Victor Porcar 88 | * 89 | */ 90 | @FunctionalInterface 91 | public interface BatchCallBack7 { 92 | List apply(List
firstParam, List secondParam, List thirdParam, List fourthParam, 93 | List fifthParam, List sixthParam); 94 | } 95 | 96 | private final BatchCallBack7 batchCallBack; 97 | 98 | /** 99 | * Factory method to create an instance of a Delayed Batch Executor for six 100 | * arguments (of types A,B,C,D,E,F) and return type Z. Similar to 101 | * {@link DelayedBatchExecutor7#create(Duration, int, ExecutorService, int, boolean, BatchCallBack7)} 102 | * defaulting to:
103 | *
104 | * -executorService: the one returned by static method 105 | * {@link #getDefaultExecutorService()}
106 | * -bufferQueueSize: the value of constant {@link #DEFAULT_BUFFER_QUEUE_SIZE} 107 | *
108 | * -removeDuplicates:true
109 | * 110 | * @param the return type 111 | * @param
the type of the first argument 112 | * @param the type of the second argument 113 | * @param the type of the third argument 114 | * @param the type of the fourth argument 115 | * @param the type of the fifth argument 116 | * @param the type of the sixth argument 117 | * @param duration the time window, defined as {@link Duration }. 118 | * @param size the max collected size. As soon as the count of 119 | * collected parameters reaches this size, the 120 | * batchCallBack method is executed 121 | * @param batchCallback7 the method reference or lambda expression that receives 122 | * a list of type A and returns a list of Type Z (see 123 | * {@link BatchCallBack7}) 124 | * @return an instance of {@link DelayedBatchExecutor7} 125 | * 126 | */ 127 | 128 | public static DelayedBatchExecutor7 create(Duration duration, int size, 129 | BatchCallBack7 batchCallback7) { 130 | return new DelayedBatchExecutor7<>(duration, size, null, DEFAULT_BUFFER_QUEUE_SIZE, true, batchCallback7); 131 | } 132 | 133 | /** 134 | * Factory method to create an instance of a Delayed Batch Executor for two 135 | * arguments (of types A,B,C,D and E) and return type Z
136 | * 137 | * @param the return type 138 | * @param
the type of the first argument 139 | * @param the type of the second argument 140 | * @param the type of the third argument 141 | * @param the type of the fourth argument 142 | * @param the type of the fifth argument 143 | * @param the type of the sixth argument 144 | * @param duration the time window, defined as {@link Duration }. 145 | * @param size the max collected size. As soon as the count of 146 | * collected parameters reaches this size, the 147 | * batchCallBack method is executed 148 | * @param executorService to define the pool of threads to executed the 149 | * batchCallBack method in asynchronous mode 150 | * @param bufferQueueSize max size of the internal queue to buffer values. 151 | * @param removeDuplicates if true then duplicated arguments from execute*(...) 152 | * methods are not passed to the batchCallBack 153 | * (considering same {@link Object#hashCode()} and being 154 | * {@link Object#equals(Object)}) 155 | * @param batchCallback7 the method reference or lambda expression that 156 | * receives a list of type A and returns a list of Type 157 | * Z (see {@link BatchCallBack7}) 158 | * @return an instance of {@link DelayedBatchExecutor7} 159 | * 160 | */ 161 | 162 | public static DelayedBatchExecutor7 create(Duration duration, int size, 163 | ExecutorService executorService, int bufferQueueSize, boolean removeDuplicates, 164 | BatchCallBack7 batchCallback7) { 165 | return new DelayedBatchExecutor7<>(duration, size, executorService, bufferQueueSize, removeDuplicates, 166 | batchCallback7); 167 | } 168 | 169 | private DelayedBatchExecutor7(Duration duration, int size, ExecutorService executorService, int bufferQueueSize, 170 | boolean removeDuplicates, BatchCallBack7 batchCallBack) { 171 | super(duration, size, executorService, bufferQueueSize, removeDuplicates); 172 | this.batchCallBack = batchCallBack; 173 | } 174 | 175 | /** 176 | * Return the result of type Z (blocking the thread until the result is 177 | * available), which is obtained from the returned list of the batchCallBack 178 | * method for the given argument
179 | *
180 | * It will throw any {@link RuntimeException} thrown inside of the 181 | * {@link BatchCallBack7 }
182 | * It will throw a {@link RuntimeException} if the internal buffer Queue of this 183 | * Delayed Batch Executor is full.
184 | *
185 | * blocking 186 | * 187 | * @param arg1 value of the first argument of type A defined for this Delayed 188 | * Batch Executor 189 | * @param arg2 value of the second argument of type B defined for this Delayed 190 | * Batch Executor 191 | * @param arg3 value of the third argument of type C defined for this Delayed 192 | * Batch Executor 193 | * @param arg4 value of the fourth argument of type D defined for this Delayed 194 | * Batch Executor 195 | * @param arg5 value of the fifth argument of type E defined for this Delayed 196 | * Batch Executor 197 | * @param arg6 value of the sixth argument of type F defined for this Delayed 198 | * Batch Executor 199 | * @return the result of type Z 200 | * 201 | * 202 | */ 203 | public Z execute(A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) { 204 | TupleBlocking tupleBlocking = new TupleBlocking<>(arg1, arg2, arg3, arg4, arg5, arg6); 205 | enlistTuple(tupleBlocking); 206 | Z value = tupleBlocking.getValueBlocking(); 207 | return value; 208 | } 209 | 210 | /** 211 | * Return a {@link Future } containing the corresponding value from the returned 212 | * list of the batchCallBack method for the given argument
213 | *
214 | * The invoking thread is not blocked. The result will be available by invoking 215 | * method {@link Future#get()} of the {@link Future }. This method will block 216 | * the thread until the result is available
217 | *
218 | * future
219 | * It will throw a {@link RuntimeException} if the internal buffer Queue of this 220 | * Delayed Batch Executor is full.
221 | * If a {@link RuntimeException} is thrown inside of the {@link BatchCallBack7 222 | * }, then it will be the cause of the checked Exception 223 | * {@link ExecutionException} thrown by {@link Future#get()} as per contract of 224 | * {@link Future#get()}
225 | *
226 | * 227 | * @param arg1 value of the first argument of type A defined for this Delayed 228 | * Batch Executor 229 | * @param arg2 value of the second argument of type B defined for this Delayed 230 | * Batch Executor 231 | * @param arg3 value of the third argument of type C defined for this Delayed 232 | * Batch Executor 233 | * @param arg4 value of the fourth argument of type D defined for this Delayed 234 | * Batch Executor 235 | * @param arg5 value of the fifth argument of type E defined for this Delayed 236 | * Batch Executor 237 | * @param arg6 value of the sixth argument of type F defined for this Delayed 238 | * Batch Executor 239 | * @return a {@link Future } for the result of type Z 240 | * 241 | */ 242 | public Future executeAsFuture(A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) { 243 | TupleFuture tupleFuture = new TupleFuture<>(arg1, arg2, arg3, arg4, arg5, arg6); 244 | enlistTuple(tupleFuture); 245 | Future future = tupleFuture.getFuture(); 246 | return future; 247 | } 248 | 249 | /** 250 | * Return a
Mono, 252 | * publishing the value obtained from the returned list of the batchCallBack 253 | * method for the given parameter.
254 | *
255 | * The invoking thread is not blocked
256 | *
257 | * mono
258 | * It will throw a {@link RuntimeException} if the internal buffer Queue of this 259 | * Delayed Batch Executor is full.
260 | * If a {@link RuntimeException} is thrown inside of the {@link BatchCallBack7}, 261 | * then it will be the propagated as any {@link RuntimeException } thrown from 262 | * Mono 264 | *
265 | *
266 | * 267 | * @param arg1 value of the first argument of type A defined for this Delayed 268 | * Batch Executor 269 | * @param arg2 value of the second argument of type B defined for this Delayed 270 | * Batch Executor 271 | * @param arg3 value of the third argument of type C defined for this Delayed 272 | * Batch Executor 273 | * @param arg4 value of the fourth argument of type D defined for this Delayed 274 | * Batch Executor 275 | * @param arg5 value of the fifth argument of type E defined for this Delayed 276 | * Batch Executor 277 | * @param arg6 value of the sixth argument of type F defined for this Delayed 278 | * Batch Executor 279 | * @return a Mono 281 | * for the result of type Z 282 | * 283 | * 284 | */ 285 | public Mono executeAsMono(A arg1, B arg2, C arg3, D arg4, E arg5, F arg6) { 286 | TupleMono tupleMono = new TupleMono<>(arg1, arg2, arg3, arg4, arg5, arg6); 287 | enlistTuple(tupleMono); 288 | Mono mono = tupleMono.getMono(); 289 | return mono; 290 | } 291 | 292 | @SuppressWarnings("unchecked") 293 | @Override 294 | protected List getResultListFromBatchCallBack(List> transposedTupleList) { 295 | return batchCallBack.apply((List) transposedTupleList.get(0), (List) transposedTupleList.get(1), 296 | (List) transposedTupleList.get(2), (List) transposedTupleList.get(3), 297 | (List) transposedTupleList.get(4), (List) transposedTupleList.get(5)); 298 | } 299 | } -------------------------------------------------------------------------------- /src/main/java/com/github/victormpcmun/delayedbatchexecutor/DelayedBatchExecutor.java: -------------------------------------------------------------------------------- 1 | package com.github.victormpcmun.delayedbatchexecutor; 2 | 3 | import java.time.Duration; 4 | import java.util.ArrayList; 5 | import java.util.Collections; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.Queue; 9 | import java.util.concurrent.ArrayBlockingQueue; 10 | import java.util.concurrent.CompletableFuture; 11 | import java.util.concurrent.ExecutorService; 12 | import java.util.concurrent.Executors; 13 | import java.util.concurrent.ThreadFactory; 14 | import java.util.concurrent.atomic.AtomicInteger; 15 | import java.util.concurrent.atomic.AtomicLong; 16 | 17 | import reactor.core.publisher.UnicastProcessor; 18 | 19 | abstract class DelayedBatchExecutor implements AutoCloseable { 20 | 21 | private static final String TO_STRING_FORMAT = "DelayedBatchExecutor {invocationsCounter=%d, callBackExecutionsCounter=%d, duration=%d, size=%d, bufferQueueSize=%d}"; 22 | 23 | /** 24 | * {@value com.github.victormpcmun.delayedbatchexecutor.DelayedBatchExecutor#MIN_TIME_WINDOW_TIME_IN_MILLISECONDS} 25 | */ 26 | public static final int MIN_TIME_WINDOW_TIME_IN_MILLISECONDS = 1; 27 | 28 | /** 29 | * {@value com.github.victormpcmun.delayedbatchexecutor.DelayedBatchExecutor#MAX_TIME_WINDOW_TIME_IN_MILLISECONDS} 30 | */ 31 | public static final int MAX_TIME_WINDOW_TIME_IN_MILLISECONDS = 60 * 60 * 1000; 32 | 33 | /** 34 | * {@value com.github.victormpcmun.delayedbatchexecutor.DelayedBatchExecutor#DEFAULT_FIXED_THREAD_POOL_COUNTER} 35 | */ 36 | public static final int DEFAULT_FIXED_THREAD_POOL_COUNTER = 4; 37 | 38 | /** 39 | * {@value com.github.victormpcmun.delayedbatchexecutor.DelayedBatchExecutor#DEFAULT_FIXED_THREAD_NAME_PREFIX} 40 | */ 41 | public static final String DEFAULT_FIXED_THREAD_NAME_PREFIX = "delayed-batch-executor-"; 42 | 43 | /** 44 | * {@value com.github.victormpcmun.delayedbatchexecutor.DelayedBatchExecutor#DEFAULT_BUFFER_QUEUE_SIZE} 45 | */ 46 | public static final int DEFAULT_BUFFER_QUEUE_SIZE = 8192; 47 | 48 | private final AtomicLong invocationsCounter; 49 | private final AtomicLong callBackExecutionsCounter; 50 | 51 | private Duration duration; 52 | private int maxSize; 53 | private ExecutorService executorService; 54 | private int bufferQueueSize; 55 | private UnicastProcessor> source; 56 | private boolean removeDuplicates; 57 | 58 | private final boolean defaultExecutorServiceCreated; 59 | 60 | protected DelayedBatchExecutor(Duration duration, int maxSize, ExecutorService executorService, int bufferQueueSize, 61 | boolean removeDuplicates) { 62 | if (executorService == null) { 63 | executorService = getDefaultExecutorService(); 64 | defaultExecutorServiceCreated = true; 65 | } else { 66 | defaultExecutorServiceCreated = false; 67 | } 68 | boolean configurationSuccessful = updateConfig(duration, maxSize, executorService, bufferQueueSize, 69 | removeDuplicates); 70 | if (!configurationSuccessful) { 71 | throw new RuntimeException("Illegal configuration parameters"); 72 | } 73 | this.invocationsCounter = new AtomicLong(0); 74 | this.callBackExecutionsCounter = new AtomicLong(0); 75 | this.removeDuplicates = removeDuplicates; 76 | } 77 | 78 | /** 79 | * Update the Duration and maxSize params of this Delayed Batch Executor, 80 | * keeping the current existing value for executorService, bufferQueueSize and 81 | * removeDuplicates
82 | *
83 | * This method is thread safe
84 | * 85 | * @param duration the new {@link Duration} for this Delayed Batch Executor 86 | * @param maxSize the new maxsize for this Delayed Batch Executor 87 | * @return true if the configuration was successful updated, false otherwise 88 | * 89 | */ 90 | public boolean updateConfig(Duration duration, int maxSize) { 91 | return updateConfig(duration, maxSize, executorService, bufferQueueSize, removeDuplicates); 92 | } 93 | 94 | /** 95 | * Update the Duration, maxsize, ExecutorService, bufferQueueSize and 96 | * removeDuplicates params of this Delayed Batch Executor
97 | *
98 | * This method is thread safe
99 | * 100 | * @param duration the new {@link Duration} for this Delayed Batch 101 | * Executor 102 | * @param maxSize the new maxsize for this Delayed Batch Executor 103 | * @param executorService the new {@link ExecutorService} for this Delayed 104 | * Batch Executor 105 | * @param bufferQueueSize max size of the internal queue to buffer values 106 | * @param removeDuplicates if true then duplicated arguments from execute*(...) 107 | * methods are not passed to the batchCallBack 108 | * (considering same {@link Object#hashCode()} and being 109 | * {@link Object#equals(Object)}) 110 | * @return true if the configuration was successful updated, false otherwise 111 | * 112 | */ 113 | public synchronized boolean updateConfig(Duration duration, int maxSize, ExecutorService executorService, 114 | int bufferQueueSize, boolean removeDuplicates) { 115 | boolean validateConfig = validateConfigurationParameters(duration, maxSize, executorService, bufferQueueSize); 116 | if (validateConfig) { 117 | boolean parameterAreEqualToCurrentOnes = parameterAreEqualToCurrentOnes(duration, maxSize, executorService, 118 | bufferQueueSize); 119 | if (!parameterAreEqualToCurrentOnes) { 120 | this.maxSize = maxSize; 121 | this.duration = duration; 122 | this.executorService = executorService; 123 | this.bufferQueueSize = bufferQueueSize; 124 | this.source = createBufferedTimeoutUnicastProcessor(duration, maxSize, bufferQueueSize, 125 | removeDuplicates); 126 | 127 | } 128 | } 129 | return validateConfig; 130 | } 131 | 132 | /** 133 | * The count of invocations of all of the execute methods of this Delayed Batch 134 | * Executor: execute(...), executeAsFuture(...) or executeAsMono(...) since the 135 | * creation of this Delayed Batch Executor 136 | * 137 | * @return the count of invocations of all blocking, Future and Mono invocations 138 | * since the creation of this Delayed Batch Executor 139 | * 140 | */ 141 | public Long getInvocationsCounter() { 142 | return invocationsCounter.get(); 143 | } 144 | 145 | /** 146 | * The count of executions of the batchCallBack method since the creation of 147 | * this Delayed Batch Executor 148 | * 149 | * @return the count of executions of the batchCallBack method since the 150 | * creation of this Delayed Batch Executor 151 | * 152 | */ 153 | public Long getCallBackExecutionsCounter() { 154 | return callBackExecutionsCounter.get(); 155 | } 156 | 157 | /** 158 | * The current {@link Duration} of this Delayed Batch Executor 159 | * 160 | * @return the current {@link Duration} of this Delayed Batch Executor 161 | * 162 | */ 163 | public Duration getDuration() { 164 | return duration; 165 | } 166 | 167 | /** 168 | * The current size of this Delayed Batch Executor 169 | * 170 | * @return the current size of this Delayed Batch Executor 171 | * 172 | */ 173 | public Integer getMaxSize() { 174 | return maxSize; 175 | } 176 | 177 | /** 178 | * The current max size of the internal queue to buffer values 179 | * 180 | * @return the current max size of the internal queue to buffer values 181 | * 182 | */ 183 | public Integer getBufferQueueSize() { 184 | return bufferQueueSize; 185 | } 186 | 187 | /** 188 | * The current ExecutorService 189 | * 190 | * @return the current ExecutorService 191 | * 192 | */ 193 | public ExecutorService getExecutorService() { 194 | return executorService; 195 | } 196 | 197 | /** 198 | * The removeDuplicates behaviour flag 199 | * 200 | * @return the removeDuplicates behaviour flag 201 | * 202 | */ 203 | 204 | public boolean isRemoveDuplicates() { 205 | return removeDuplicates; 206 | } 207 | 208 | /** 209 | * static method that creates the default Executor Service, which is a 210 | * {@link java.util.concurrent.Executors#newFixedThreadPool(int)} with the 211 | * following number of threads given by constant 212 | * {@link #DEFAULT_FIXED_THREAD_POOL_COUNTER} 213 | * 214 | * @return the default Executor Service 215 | */ 216 | 217 | protected ExecutorService getDefaultExecutorService() { 218 | return getDefaultExecutorService(DEFAULT_FIXED_THREAD_POOL_COUNTER); 219 | } 220 | 221 | /** 222 | * static method that creates the default Executor Service, which is a 223 | * {@link java.util.concurrent.Executors#newFixedThreadPool(int)} with the given 224 | * number of threads 225 | * 226 | * @param threads number of threads of the FixedThreadPool 227 | * @return the default Executor Service 228 | */ 229 | 230 | private ExecutorService getDefaultExecutorService(int threads) { 231 | return Executors.newFixedThreadPool(threads, new ThreadFactory() { 232 | private final AtomicInteger threadNumber = new AtomicInteger(1); 233 | 234 | @Override 235 | public Thread newThread(Runnable runnable) { 236 | return new Thread(runnable, DEFAULT_FIXED_THREAD_NAME_PREFIX + threadNumber.getAndIncrement()); 237 | } 238 | }); 239 | } 240 | 241 | @Override 242 | public void close() { 243 | if (defaultExecutorServiceCreated && !executorService.isShutdown()) { 244 | executorService.shutdown(); 245 | } 246 | } 247 | 248 | @Override 249 | public String toString() { 250 | return String.format(TO_STRING_FORMAT, invocationsCounter.get(), callBackExecutionsCounter.get(), 251 | duration.toMillis(), maxSize, bufferQueueSize); 252 | } 253 | 254 | protected void enlistTuple(Tuple param) { 255 | invocationsCounter.incrementAndGet(); 256 | source.onNext(param); 257 | } 258 | 259 | protected abstract List getResultListFromBatchCallBack(List> transposedTupleList); 260 | 261 | private void invokeBatchCallBackAndContinue(List> tupleList) { 262 | List rawResultList = null; 263 | List resultFromCallBack; 264 | RuntimeException runtimeException = null; 265 | try { 266 | List> transposedTupleList = TupleListTransposer.transposeValuesAsListOfList(tupleList); 267 | rawResultList = getResultListFromBatchCallBack(transposedTupleList); 268 | } catch (RuntimeException re) { 269 | runtimeException = re; 270 | } finally { 271 | resultFromCallBack = resizeListFillingWithNullsIfNecessary(rawResultList, tupleList.size()); 272 | } 273 | 274 | for (int indexTuple = 0; indexTuple < tupleList.size(); indexTuple++) { 275 | Tuple tuple = tupleList.get(indexTuple); 276 | tuple.setResult(resultFromCallBack.get(indexTuple)); 277 | tuple.setRuntimeException(runtimeException); 278 | tuple.continueIfIsWaiting(); 279 | } 280 | } 281 | 282 | private void assignValuesToDuplicatesAndContinue(TupleListDuplicatedFinder tupleListDuplicatedFinder) { 283 | Map duplicatedMapIndex = tupleListDuplicatedFinder.getDuplicatedMapIndex(); 284 | List> allTupleList = tupleListDuplicatedFinder.getAllTupleList(); 285 | for (Integer duplicatedIndex : duplicatedMapIndex.keySet()) { 286 | Tuple duplicatedTuple = allTupleList.get(duplicatedIndex); 287 | Tuple uniqueTuple = allTupleList.get(duplicatedMapIndex.get(duplicatedIndex)); 288 | duplicatedTuple.copyResultAndRuntimeExceptionFromTuple(uniqueTuple); 289 | duplicatedTuple.continueIfIsWaiting(); 290 | } 291 | } 292 | 293 | private void executeBatchCallBackRemovingDuplicates(List> tupleList) { 294 | callBackExecutionsCounter.incrementAndGet(); 295 | CompletableFuture.runAsync(() -> { 296 | TupleListDuplicatedFinder tupleListDuplicatedFinder = new TupleListDuplicatedFinder<>(tupleList); 297 | List> tupleListUnique = tupleListDuplicatedFinder.getTupleListUnique(); 298 | invokeBatchCallBackAndContinue(tupleListUnique); 299 | assignValuesToDuplicatesAndContinue(tupleListDuplicatedFinder); 300 | }, this.executorService); 301 | } 302 | 303 | private void executeBatchCallBackNotRemovingDuplicates(List> tupleList) { 304 | callBackExecutionsCounter.incrementAndGet(); 305 | CompletableFuture.runAsync(() -> { 306 | invokeBatchCallBackAndContinue(tupleList); 307 | }, this.executorService); 308 | } 309 | 310 | private UnicastProcessor> createBufferedTimeoutUnicastProcessor(Duration duration, int maxSize, 311 | int bufferQueueSize, boolean removeDuplicates) { 312 | Queue> blockingQueue = new ArrayBlockingQueue<>(bufferQueueSize); // => 313 | // https://github.com/reactor/reactor-core/issues/469#issuecomment-286040390 314 | UnicastProcessor> newSource = UnicastProcessor.create(blockingQueue); 315 | if (removeDuplicates) { 316 | newSource.publish().autoConnect().bufferTimeout(maxSize, duration) 317 | .subscribe(this::executeBatchCallBackRemovingDuplicates); 318 | } else { 319 | newSource.publish().autoConnect().bufferTimeout(maxSize, duration) 320 | .subscribe(this::executeBatchCallBackNotRemovingDuplicates); 321 | } 322 | return newSource; 323 | } 324 | 325 | private boolean validateConfigurationParameters(Duration duration, int maxSize, ExecutorService executorService, 326 | int bufferQueueSize) { 327 | boolean sizeValidation = (maxSize >= 1); 328 | boolean durationValidation = duration != null && duration.toMillis() >= MIN_TIME_WINDOW_TIME_IN_MILLISECONDS 329 | && duration.toMillis() <= MAX_TIME_WINDOW_TIME_IN_MILLISECONDS; 330 | boolean executorServiceValidation = (executorService != null); 331 | boolean bufferQueueSizeValidation = (bufferQueueSize >= 1); 332 | return sizeValidation && durationValidation && executorServiceValidation && bufferQueueSizeValidation; 333 | } 334 | 335 | private boolean parameterAreEqualToCurrentOnes(Duration duration, int size, ExecutorService executorService, 336 | int bufferQueueSize) { 337 | boolean sameDuration = this.duration != null && this.duration.compareTo(duration) == 0; 338 | boolean sameSize = (this.maxSize == size); 339 | boolean sameExecutorService = this.executorService != null && this.executorService == executorService; // same 340 | // reference 341 | // is 342 | // enough 343 | boolean sameBufferQueueSize = (this.bufferQueueSize == bufferQueueSize); 344 | return sameDuration && sameSize && sameExecutorService && sameBufferQueueSize; 345 | } 346 | 347 | private List resizeListFillingWithNullsIfNecessary(List list, int desiredSize) { 348 | if (list == null) { 349 | list = Collections.nCopies(desiredSize, null); 350 | } else if (list.size() < desiredSize) { 351 | list = new ArrayList<>(list); // make it mutable in case it isn't 352 | list.addAll(Collections.nCopies(desiredSize - list.size(), null)); 353 | } 354 | return list; 355 | } 356 | } 357 | -------------------------------------------------------------------------------- /src/main/javadoc/doc-files/future.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 |
Thread 2
Thread 2
Thread n
Thread n
Thread 1
Thread 1
blocked
blocked
result = f.get()
result = f.get()
DelayedBatchExecutor
DelayedBatchExecutor
Time Window
Time Window
batchCallback(args)
batchCallback(args)
val1
val1
...
...
val2
val2
valn
valn
Thread is unblocked and Future result is returned
Thread is unblocked and Future result is returned
Future f = executeAsFuture(val1)
Future f = executeAsFuture(va...
Future f = executeAsFuture(valn)
Future f = executeAsFuture(valn)
Future f = executeAsFuture(val2)
Future f = executeAsFuture(val2)
result = f.get()
result = f.get()
result = f.get()
result = f.get()
Viewer does not support full SVG 1.1
-------------------------------------------------------------------------------- /src/main/javadoc/doc-files/blocking.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 |
Thread n
Thread n
blocked
blocked
DelayedBatchExecutor
DelayedBatchExecutor
Time Window
Time Window
batchCallback(args)
batchCallback(args)
val1
val1
...
...
val2
val2
val3
val3
Thread 1
Thread 1
blocked
blocked
Thread 2
Thread 2
blocked
blocked
Thread is unblocked and
result is returned

Thread is unblocked and...
Thread is unblocked and result is returned
Thread is unblocked and result is returned
Thread is unblocked and result is returned
Thread is unblocked and result is returned
result = execute(val1)
result = execute(val1)
result = execute(val3)
result = execute(val3)
result = execute(val2)
result = execute(val2)
Viewer does not support full SVG 1.1
-------------------------------------------------------------------------------- /src/main/javadoc/doc-files/mono.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 |
Thread 2
Thread 2
Thread n
Thread n
Thread 1
Thread 1
   m.subscribe(result->{..})
m.subscribe(resul...
DelayedBatchExecutor
DelayedBatchExecutor
Time Window
Time Window
batchCallback(args)
batchCallback(args)
val1
val1
...
...
val2
val2
valn
valn
result is emitted
result is emitted
Mono m = executeAsMono(val1)
Mono m = executeAsMono(val1)
Mono m = executeAsMono(valn)
Mono m = executeAsMono(valn)
Mono m = executeAsMono(val2)
Mono m = executeAsMono(val2)
   m.subscribe(result->{..})
m.subscribe(res...
   m.subscribe(result->{..})
m.subscribe(res...
result is emitted
result is emitted
result is emitted
result is emitted
Viewer does not support full SVG 1.1
--------------------------------------------------------------------------------