package org.infinispan.query.affinity;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import org.hibernate.search.backend.LuceneWork;
import org.hibernate.search.indexes.spi.IndexManager;
import org.hibernate.search.spi.SearchIntegrator;
import org.infinispan.query.backend.KeyTransformationHandler;
import org.infinispan.query.indexmanager.LuceneWorkConverter;
import org.infinispan.query.logging.Log;
import org.infinispan.remoting.responses.Response;
import org.infinispan.remoting.rpc.RpcManager;
import org.infinispan.remoting.transport.Address;
import org.infinispan.util.ByteString;
import org.infinispan.util.logging.LogFactory;
/**
* Dispatches {@link LuceneWork} locally (to other shard in the same node) or remotely.
*
* @since 9.0
*/
class LuceneWorkDispatcher {
private static final Log log = LogFactory.getLog(AffinityIndexManager.class, Log.class);
private final AffinityIndexManager indexManager;
private final RpcManager rpcManager;
LuceneWorkDispatcher(AffinityIndexManager affinityIndexManager, RpcManager rpcManager) {
this.indexManager = affinityIndexManager;
this.rpcManager = rpcManager;
}
void dispatch(List<LuceneWork> works, ShardAddress destination, boolean originLocal) {
if(destination.getAddress().equals(indexManager.getLocalShardAddress().getAddress())) {
this.performLocally(works, destination.getShard(), indexManager.getKeyTransformationHandler(),
indexManager.getSearchIntegrator(), originLocal);
} else {
this.sendRemotely(works, destination, originLocal);
}
}
private void performLocally(Collection<LuceneWork> luceneWorks, String shard, KeyTransformationHandler handler,
SearchIntegrator integrator, boolean originLocal) {
List<LuceneWork> workToApply = LuceneWorkConverter.transformKeysToString(luceneWorks, handler);
for (LuceneWork luceneWork : workToApply) {
AffinityIndexManager im = (AffinityIndexManager) this.getIndexManagerByName(luceneWork, shard, integrator);
if (log.isDebugEnabled())
log.debugf("Performing local redirected for work %s on index %s", workToApply, im.getIndexName());
im.performOperations(Collections.singletonList(luceneWork), null, originLocal, false);
}
}
private IndexManager getIndexManagerByName(LuceneWork luceneWork, String name, SearchIntegrator searchFactory) {
Class<?> entityClass = luceneWork.getEntityClass();
IndexManager[] indexManagersForAllShards =
searchFactory.getIndexBinding(entityClass).getSelectionStrategy().getIndexManagersForAllShards();
return Arrays.stream(indexManagersForAllShards).filter(im -> im.getIndexName().equals(name)).iterator().next();
}
private boolean shouldSendSync(boolean originLocal) {
return !indexManager.isAsync() && originLocal;
}
private void sendRemotely(List<LuceneWork> works, ShardAddress destination, boolean originLocal) {
String cacheName = indexManager.getCacheName();
AffinityUpdateCommand indexUpdateCommand = new AffinityUpdateCommand(ByteString.fromString(cacheName));
byte[] serializedModel = indexManager.getSerializer().toSerializedModel(works);
indexUpdateCommand.setSerializedWorkList(serializedModel);
indexUpdateCommand.setIndexName(destination.getShard());
List<Address> dest = Collections.singletonList(destination.getAddress());
if (this.shouldSendSync(originLocal)) {
log.debugf("Sending sync works %s to %s", works, dest);
Map<Address, Response> response = rpcManager.invokeRemotely(dest, indexUpdateCommand,
rpcManager.getDefaultRpcOptions(true));
log.debugf("Response %s obtained for command %s", response, works);
} else {
log.debugf("Sending async works %s to %s", works, dest);
CompletableFuture<Map<Address, Response>> result
= rpcManager.invokeRemotelyAsync(dest, indexUpdateCommand,
rpcManager.getDefaultRpcOptions(this.shouldSendSync(originLocal)));
result.whenComplete((responses, error) -> {
if (error != null) {
log.error("Error forwarding index job", error);
}
log.debugf("Async work %s applied successfully with response %s", works, responses);
});
}
}
}