In this situation you would likely use sharding/partitioning.
public interface HashFunction<T> {
/**
* Calculate a hash that is
* - repeatable (same value always gets the same hash)
* - evenly distributed (so jobs are distributed fairly across the threads)
*/
int accept(T value);
}
/**
* Maintains threadCount single threaded executors
* Ensures that tasks for the same ID execute serially, on a single thread
* whilst jobs for different ID's can execute in parallel
*/
public class PartitionedExecutor<ID> {
private final int threadCount;
private final HashFunction<ID> hashFunction;
private final ExecutorService[] executors;
public PartitionedExecutor(int threadCount, HashFunction<ID> hashFunction) {
this.threadCount = threadCount;
this.hashFunction = hashFunction;
this.executors = IntStream.range(0, threadCount)
.mapToObj(i -> Executors.newSingleThreadedExecutor())
.toArray(Executors[]::new);
}
public Future<V> submit(ID identifier, Callable<V> task) {
int threadIndex = hashFunction.accept(identifier) % threadCount;
return executors[threadIndex].submit(task);
}
}
public class PassengerService {
private final PartitionedExecutor<Long> executor;
public PassengerService(int threadCount) {
// assuming passengerId is a Long here
// Using Long.hashCode() as the hash function
this.executor = new PartitionedExecutor<>(threadCount, Long::hashCode);
}
public Future<Result> processOrder(PassengerOrder order) {
return executor.submit(order.getPassengerId(), () -> doProcessOrder(order));
}
public Future<Result> processAmend(PassengerAmend amend) {
return executor.submit(amend.getPassengerId(), () -> doProcessAmend(amend));
}
public Future<Result> processDelete(Long passengerId) {
return executor.submit(passengerId, () -> doProcessDelete(passengerId));
}
private Result doProcessOrder(PassengerOrder order) {
// TODO: implement
}
private Result doProcessAmend(PassengerAmend amend) {
// TODO: implement
}
private Result doProcessDelete(Long passengerId) {
// TODO: implement
}
}
Apache Kafka has this concept at it's heart (you can't send a message without first assigning it a partition)