package org.infinispan.query.affinity;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.hibernate.search.backend.LuceneWork;
import org.infinispan.query.logging.Log;
import org.infinispan.remoting.transport.Address;
import org.infinispan.util.logging.LogFactory;
/**
* Partitions {@link LuceneWork} based on the location it should be applied.
*
* @since 9.0
*/
class WorkPartitioner {
private static final Log log = LogFactory.getLog(WorkPartitioner.class, Log.class);
private final AffinityIndexManager affinityIndexManager;
private final ShardAllocatorManager shardAllocatorManager;
private final ShardAddress localShardAddress;
private final String indexName;
WorkPartitioner(AffinityIndexManager affinityIndexManager, ShardAllocatorManager shardAllocatorManager) {
this.indexName = affinityIndexManager.getIndexName();
this.localShardAddress = affinityIndexManager.getLocalShardAddress();
this.affinityIndexManager = affinityIndexManager;
this.shardAllocatorManager = shardAllocatorManager;
}
Map<ShardAddress, List<LuceneWork>> partitionWorkByAddress(Collection<LuceneWork> works, boolean originLocal,
boolean isRetry) {
return works.stream().collect(
Collectors.toMap(w -> this.getLocation(w, originLocal, isRetry), WorkPartitioner::newList, (w1, w2) -> {
w1.addAll(w2);
return w1;
})
);
}
private static String extractShardName(String indexName) {
int idx = indexName.lastIndexOf('.');
return idx == -1 ? "0" : indexName.substring(idx + 1);
}
private String getIndexName(String shardId) {
return indexName.substring(0, indexName.lastIndexOf('.') + 1) + shardId;
}
@SafeVarargs
private static <T> List<T> newList(T... elements) {
List<T> list = new ArrayList<>(elements.length);
Collections.addAll(list, elements);
return list;
}
private ShardAddress getLocation(LuceneWork work, boolean originLocal, boolean isRetry) {
log.debugf("Getting location for work %s at %s", work, localShardAddress);
if (work.getIdInString() == null) {
String shard = String.valueOf(extractShardName(indexName));
Address destination = isRetry ? affinityIndexManager.getLockHolder() : shardAllocatorManager.getOwner(shard);
return new ShardAddress(shard, destination);
}
Object workKey = affinityIndexManager.stringToKey(work.getIdInString());
String workShard = shardAllocatorManager.getShardFromKey(workKey);
String workIndexName = this.getIndexName(workShard);
Address workOwner = shardAllocatorManager.getOwner(workShard);
if (isRetry || !originLocal) {
Address lockHolder = affinityIndexManager.getLockHolder(workIndexName, workShard);
if (lockHolder != null && !lockHolder.equals(localShardAddress.getAddress())) {
return new ShardAddress(null, lockHolder);
}
return workIndexName.equals(indexName) ? localShardAddress : new ShardAddress(workIndexName, lockHolder);
}
if (workOwner.equals(localShardAddress.getAddress())) {
return workIndexName.equals(indexName) ? localShardAddress :
new ShardAddress(workIndexName, localShardAddress.getAddress());
} else {
return new ShardAddress(null, workOwner);
}
}
}