Search in sources :

Example 1 with EntityModel

use of org.opensearch.ad.ml.EntityModel in project anomaly-detection by opensearch-project.

the class PriorityCache method maintainInactiveCache.

private void maintainInactiveCache() {
    if (lastInActiveEntityMaintenance.plus(this.modelTtl).isAfter(clock.instant())) {
        // don't scan inactive cache too frequently as it is costly
        return;
    }
    // force maintenance of the cache. ref: https://tinyurl.com/pyy3p9v6
    inActiveEntities.cleanUp();
    // // make sure no model has been stored due to bugs
    for (ModelState<EntityModel> state : inActiveEntities.asMap().values()) {
        EntityModel model = state.getModel();
        if (model != null && model.getTrcf().isPresent()) {
            LOG.warn(new ParameterizedMessage("Inactive entity's model is null: [{}]. Maybe there are bugs.", state.getModelId()));
            state.setModel(null);
        }
    }
    lastInActiveEntityMaintenance = clock.instant();
}
Also used : EntityModel(org.opensearch.ad.ml.EntityModel) ParameterizedMessage(org.apache.logging.log4j.message.ParameterizedMessage)

Example 2 with EntityModel

use of org.opensearch.ad.ml.EntityModel in project anomaly-detection by opensearch-project.

the class PriorityCache method maintenance.

/**
 * Maintain active entity's cache and door keepers.
 *
 * inActiveEntities is a Guava's LRU cache. The data structure itself is
 * gonna evict items if they are inactive for 3 days or its maximum size
 * reached (1 million entries)
 */
@Override
public void maintenance() {
    try {
        // clean up memory if we allocate more memory than we should
        tryClearUpMemory();
        activeEnities.entrySet().stream().forEach(cacheBufferEntry -> {
            String detectorId = cacheBufferEntry.getKey();
            CacheBuffer cacheBuffer = cacheBufferEntry.getValue();
            // remove expired cache buffer
            if (cacheBuffer.expired(modelTtl)) {
                activeEnities.remove(detectorId);
                cacheBuffer.clear();
            } else {
                List<ModelState<EntityModel>> removedStates = cacheBuffer.maintenance();
                for (ModelState<EntityModel> state : removedStates) {
                    addIntoInactiveCache(state);
                }
            }
        });
        maintainInactiveCache();
        doorKeepers.entrySet().stream().forEach(doorKeeperEntry -> {
            String detectorId = doorKeeperEntry.getKey();
            DoorKeeper doorKeeper = doorKeeperEntry.getValue();
            // doorKeeper has its own state ttl
            if (doorKeeper.expired(null)) {
                doorKeepers.remove(detectorId);
            } else {
                doorKeeper.maintenance();
            }
        });
    } catch (Exception e) {
        // will be thrown to ES's transport broadcast handler
        throw new AnomalyDetectionException("Fail to maintain cache", e);
    }
}
Also used : AnomalyDetectionException(org.opensearch.ad.common.exception.AnomalyDetectionException) EntityModel(org.opensearch.ad.ml.EntityModel) ModelState(org.opensearch.ad.ml.ModelState) LimitExceededException(org.opensearch.ad.common.exception.LimitExceededException) AnomalyDetectionException(org.opensearch.ad.common.exception.AnomalyDetectionException)

Example 3 with EntityModel

use of org.opensearch.ad.ml.EntityModel in project anomaly-detection by opensearch-project.

the class PriorityCache method getModelProfile.

/**
 * Gets an entity's model state
 *
 * @param detectorId detector id
 * @param entityModelId  entity model id
 * @return the model state
 */
@Override
public Optional<ModelProfile> getModelProfile(String detectorId, String entityModelId) {
    CacheBuffer cacheBuffer = activeEnities.get(detectorId);
    if (cacheBuffer != null && cacheBuffer.getModel(entityModelId).isPresent()) {
        EntityModel model = cacheBuffer.getModel(entityModelId).get();
        Entity entity = null;
        if (model != null && model.getEntity().isPresent()) {
            entity = model.getEntity().get();
        }
        return Optional.of(new ModelProfile(entityModelId, entity, cacheBuffer.getMemoryConsumptionPerEntity()));
    }
    return Optional.empty();
}
Also used : Entity(org.opensearch.ad.model.Entity) EntityModel(org.opensearch.ad.ml.EntityModel) ModelProfile(org.opensearch.ad.model.ModelProfile)

Example 4 with EntityModel

use of org.opensearch.ad.ml.EntityModel in project anomaly-detection by opensearch-project.

the class PriorityCache method selectUpdateCandidate.

@Override
public Pair<List<Entity>, List<Entity>> selectUpdateCandidate(Collection<Entity> cacheMissEntities, String detectorId, AnomalyDetector detector) {
    List<Entity> hotEntities = new ArrayList<>();
    List<Entity> coldEntities = new ArrayList<>();
    CacheBuffer buffer = activeEnities.get(detectorId);
    if (buffer == null) {
        // Since this method is public, need to deal with this case in case of misuse.
        return Pair.of(hotEntities, coldEntities);
    }
    Iterator<Entity> cacheMissEntitiesIter = cacheMissEntities.iterator();
    // current buffer's dedicated cache has free slots
    while (cacheMissEntitiesIter.hasNext() && buffer.dedicatedCacheAvailable()) {
        addEntity(hotEntities, cacheMissEntitiesIter.next(), detectorId);
    }
    while (cacheMissEntitiesIter.hasNext() && memoryTracker.canAllocate(buffer.getMemoryConsumptionPerEntity())) {
        // can allocate in shared cache
        // race conditions can happen when multiple threads evaluating this condition.
        // This is a problem as our AD memory usage is close to full and we put
        // more things than we planned. One model in HCAD is small,
        // it is fine we exceed a little. We have regular maintenance to remove
        // extra memory usage.
        addEntity(hotEntities, cacheMissEntitiesIter.next(), detectorId);
    }
    // check if we can replace anything in dedicated or shared cache
    // have a copy since we need to do the iteration twice: one for
    // dedicated cache and one for shared cache
    List<Entity> otherBufferReplaceCandidates = new ArrayList<>();
    while (cacheMissEntitiesIter.hasNext()) {
        // can replace an entity in the same CacheBuffer living in reserved
        // or shared cache
        // thread safe as each detector has one thread at one time and only the
        // thread can access its buffer.
        Entity entity = cacheMissEntitiesIter.next();
        Optional<String> modelId = entity.getModelId(detectorId);
        if (false == modelId.isPresent()) {
            continue;
        }
        Optional<ModelState<EntityModel>> state = getStateFromInactiveEntiiyCache(modelId.get());
        if (false == state.isPresent()) {
            // not even recorded in inActiveEntities yet because of doorKeeper
            continue;
        }
        ModelState<EntityModel> modelState = state.get();
        float priority = modelState.getPriority();
        if (buffer.canReplaceWithinDetector(priority)) {
            addEntity(hotEntities, entity, detectorId);
        } else {
            // re-evaluate replacement condition in other buffers
            otherBufferReplaceCandidates.add(entity);
        }
    }
    // record current minimum priority among all detectors to save redundant
    // scanning of all CacheBuffers
    CacheBuffer bufferToRemove = null;
    float minPriority = Float.MIN_VALUE;
    // check if we can replace in other CacheBuffer
    cacheMissEntitiesIter = otherBufferReplaceCandidates.iterator();
    while (cacheMissEntitiesIter.hasNext()) {
        // If two threads try to remove the same entity and add their own state, the 2nd remove
        // returns null and only the first one succeeds.
        Entity entity = cacheMissEntitiesIter.next();
        Optional<String> modelId = entity.getModelId(detectorId);
        if (false == modelId.isPresent()) {
            continue;
        }
        Optional<ModelState<EntityModel>> inactiveState = getStateFromInactiveEntiiyCache(modelId.get());
        if (false == inactiveState.isPresent()) {
            // empty state should not stand a chance to replace others
            continue;
        }
        ModelState<EntityModel> state = inactiveState.get();
        float priority = state.getPriority();
        float scaledPriority = buffer.getPriorityTracker().getScaledPriority(priority);
        if (scaledPriority <= minPriority) {
            // not even larger than the minPriority, we can put this to coldEntities
            addEntity(coldEntities, entity, detectorId);
            continue;
        }
        // Float.MIN_VALUE means we need to re-iterate through all CacheBuffers
        if (minPriority == Float.MIN_VALUE) {
            Triple<CacheBuffer, String, Float> bufferToRemoveEntity = canReplaceInSharedCache(buffer, scaledPriority);
            bufferToRemove = bufferToRemoveEntity.getLeft();
            minPriority = bufferToRemoveEntity.getRight();
        }
        if (bufferToRemove != null) {
            addEntity(hotEntities, entity, detectorId);
            // reset minPriority after the replacement so that we need to iterate all CacheBuffer
            // again
            minPriority = Float.MIN_VALUE;
        } else {
            // after trying everything, we can now safely put this to cold entities list
            addEntity(coldEntities, entity, detectorId);
        }
    }
    return Pair.of(hotEntities, coldEntities);
}
Also used : Entity(org.opensearch.ad.model.Entity) ArrayList(java.util.ArrayList) EntityModel(org.opensearch.ad.ml.EntityModel) ModelState(org.opensearch.ad.ml.ModelState)

Example 5 with EntityModel

use of org.opensearch.ad.ml.EntityModel in project anomaly-detection by opensearch-project.

the class CacheBuffer method put.

/**
 * Insert the model state associated with a model Id to the cache.  Update priority.
 * @param entityModelId the model Id
 * @param value the ModelState
 * @param priority the priority
 */
private void put(String entityModelId, ModelState<EntityModel> value, float priority) {
    ModelState<EntityModel> contentNode = items.get(entityModelId);
    if (contentNode == null) {
        priorityTracker.addPriority(entityModelId, priority);
        items.put(entityModelId, value);
        Instant now = clock.instant();
        value.setLastUsedTime(now);
        lastUsedTime = now;
        // skip bookkeeping.
        if (!sharedCacheEmpty()) {
            memoryTracker.consumeMemory(memoryConsumptionPerEntity, false, Origin.HC_DETECTOR);
        }
    } else {
        update(entityModelId);
        items.put(entityModelId, value);
    }
}
Also used : Instant(java.time.Instant) EntityModel(org.opensearch.ad.ml.EntityModel)

Aggregations

EntityModel (org.opensearch.ad.ml.EntityModel)24 ModelState (org.opensearch.ad.ml.ModelState)8 Entity (org.opensearch.ad.model.Entity)8 ArrayList (java.util.ArrayList)7 Instant (java.time.Instant)5 ParameterizedMessage (org.apache.logging.log4j.message.ParameterizedMessage)4 EntityCache (org.opensearch.ad.caching.EntityCache)4 Clock (java.time.Clock)3 HashMap (java.util.HashMap)3 Before (org.junit.Before)3 ThresholdingResult (org.opensearch.ad.ml.ThresholdingResult)3 AnomalyDetector (org.opensearch.ad.model.AnomalyDetector)3 RandomModelStateConfig (test.org.opensearch.ad.util.RandomModelStateConfig)3 ArrayDeque (java.util.ArrayDeque)2 Map (java.util.Map)2 Optional (java.util.Optional)2 Random (java.util.Random)2 ArgumentMatchers.anyString (org.mockito.ArgumentMatchers.anyString)2 CacheProvider (org.opensearch.ad.caching.CacheProvider)2 AnomalyDetectionIndices (org.opensearch.ad.indices.AnomalyDetectionIndices)2