package org.molgenis.data.elasticsearch.index.job;
import com.google.common.util.concurrent.AtomicLongMap;
import org.molgenis.data.meta.model.Attribute;
import org.molgenis.data.meta.model.EntityType;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import static java.util.stream.Collectors.toSet;
import static java.util.stream.StreamSupport.stream;
/**
* Keeps track of outstanding index actions.
*/
public class IndexStatus
{
private final Lock lock = new ReentrantLock();
private final Condition allEntitiesStable = lock.newCondition();
private final Condition singleEntityStable = lock.newCondition();
/**
* Counts how many actions are pending for each entity.
*/
private AtomicLongMap<String> actionCountsPerEntity = AtomicLongMap.create();
private static final Logger LOG = LoggerFactory.getLogger(IndexStatus.class);
void addActionCounts(Map<String, Long> actionsRegistered)
{
LOG.debug("addActionCounts {}", actionsRegistered);
lock.lock();
try
{
for (Map.Entry<String, Long> addedForEntity : actionsRegistered.entrySet())
{
final String entityName = addedForEntity.getKey();
final Long numberOfActions = addedForEntity.getValue();
actionCountsPerEntity.addAndGet(entityName, numberOfActions);
}
}
finally
{
lock.unlock();
}
}
void removeActionCounts(Map<String, Long> actionsPerformed)
{
LOG.debug("removeActionCount {}", actionsPerformed);
lock.lock();
try
{
for (Map.Entry<String, Long> actionsPerEntity : actionsPerformed.entrySet())
{
final String entityName = actionsPerEntity.getKey();
Long numberOfActions = actionsPerEntity.getValue();
if (actionCountsPerEntity.addAndGet(entityName, -numberOfActions) == 0)
{
actionCountsPerEntity.removeAllZeros();
LOG.debug("Entity {} is stable.", entityName);
singleEntityStable.signalAll();
}
}
if (isAllIndicesStable())
{
LOG.debug("All entities stable.");
allEntitiesStable.signalAll();
}
}
finally
{
lock.unlock();
}
}
void waitForAllEntitiesToBeStable() throws InterruptedException
{
lock.lock();
try
{
while (!isAllIndicesStable())
{
allEntitiesStable.await();
}
}
finally
{
lock.unlock();
}
}
private boolean isIndexStableIncludingReferences(EntityType emd)
{
if (isAllIndicesStable())
{
return true;
}
Set<String> referencedEntityNames = stream(emd.getAtomicAttributes().spliterator(), false)
.map(Attribute::getRefEntity).filter(e -> e != null).map(EntityType::getName).collect(toSet());
referencedEntityNames.add(emd.getName());
return referencedEntityNames.stream().noneMatch(actionCountsPerEntity::containsKey);
}
void waitForIndexToBeStableIncludingReferences(EntityType emd) throws InterruptedException
{
lock.lock();
try
{
while (!isIndexStableIncludingReferences(emd))
{
singleEntityStable.await();
}
}
finally
{
lock.unlock();
}
}
private boolean isAllIndicesStable()
{
return actionCountsPerEntity.isEmpty();
}
}