Search in sources :

Example 1 with ModelState

use of org.opensearch.ad.ml.ModelState 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 2 with ModelState

use of org.opensearch.ad.ml.ModelState 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 3 with ModelState

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

the class ADStatsTests method setup.

@Before
public void setup() {
    MockitoAnnotations.initMocks(this);
    // sampleSize * numberOfTrees has to be larger than 1. Otherwise, RCF reports errors.
    rcf = RandomCutForest.builder().dimensions(1).sampleSize(2).numberOfTrees(1).build();
    thresholdingModel = new HybridThresholdingModel(1e-8, 1e-5, 200, 10_000, 2, 5_000_000);
    List<ModelState<?>> modelsInformation = new ArrayList<>(Arrays.asList(new ModelState<>(rcf, "rcf-model-1", "detector-1", ModelManager.ModelType.RCF.getName(), clock, 0f), new ModelState<>(thresholdingModel, "thr-model-1", "detector-1", ModelManager.ModelType.RCF.getName(), clock, 0f), new ModelState<>(rcf, "rcf-model-2", "detector-2", ModelManager.ModelType.THRESHOLD.getName(), clock, 0f), new ModelState<>(thresholdingModel, "thr-model-2", "detector-2", ModelManager.ModelType.THRESHOLD.getName(), clock, 0f)));
    when(modelManager.getAllModels()).thenReturn(modelsInformation);
    ModelState<EntityModel> entityModel1 = MLUtil.randomModelState(new RandomModelStateConfig.Builder().fullModel(true).build());
    ModelState<EntityModel> entityModel2 = MLUtil.randomModelState(new RandomModelStateConfig.Builder().fullModel(true).build());
    List<ModelState<?>> entityModelsInformation = new ArrayList<>(Arrays.asList(entityModel1, entityModel2));
    EntityCache cache = mock(EntityCache.class);
    when(cacheProvider.get()).thenReturn(cache);
    when(cache.getAllModels()).thenReturn(entityModelsInformation);
    IndexUtils indexUtils = mock(IndexUtils.class);
    when(indexUtils.getIndexHealthStatus(anyString())).thenReturn("yellow");
    when(indexUtils.getNumberOfDocumentsInIndex(anyString())).thenReturn(100L);
    clusterStatName1 = "clusterStat1";
    clusterStatName2 = "clusterStat2";
    nodeStatName1 = "nodeStat1";
    nodeStatName2 = "nodeStat2";
    Settings settings = Settings.builder().put(MAX_MODEL_SIZE_PER_NODE.getKey(), 10).build();
    ClusterService clusterService = mock(ClusterService.class);
    ClusterSettings clusterSettings = new ClusterSettings(Settings.EMPTY, Collections.unmodifiableSet(new HashSet<>(Arrays.asList(MAX_MODEL_SIZE_PER_NODE))));
    when(clusterService.getClusterSettings()).thenReturn(clusterSettings);
    statsMap = new HashMap<String, ADStat<?>>() {

        {
            put(nodeStatName1, new ADStat<>(false, new CounterSupplier()));
            put(nodeStatName2, new ADStat<>(false, new ModelsOnNodeSupplier(modelManager, cacheProvider, settings, clusterService)));
            put(clusterStatName1, new ADStat<>(true, new IndexStatusSupplier(indexUtils, "index1")));
            put(clusterStatName2, new ADStat<>(true, new IndexStatusSupplier(indexUtils, "index2")));
        }
    };
    adStats = new ADStats(statsMap);
}
Also used : ModelsOnNodeSupplier(org.opensearch.ad.stats.suppliers.ModelsOnNodeSupplier) ClusterSettings(org.opensearch.common.settings.ClusterSettings) EntityCache(org.opensearch.ad.caching.EntityCache) ArrayList(java.util.ArrayList) EntityModel(org.opensearch.ad.ml.EntityModel) HybridThresholdingModel(org.opensearch.ad.ml.HybridThresholdingModel) ModelState(org.opensearch.ad.ml.ModelState) ArgumentMatchers.anyString(org.mockito.ArgumentMatchers.anyString) RandomModelStateConfig(test.org.opensearch.ad.util.RandomModelStateConfig) CounterSupplier(org.opensearch.ad.stats.suppliers.CounterSupplier) IndexUtils(org.opensearch.ad.util.IndexUtils) ClusterService(org.opensearch.cluster.service.ClusterService) IndexStatusSupplier(org.opensearch.ad.stats.suppliers.IndexStatusSupplier) ClusterSettings(org.opensearch.common.settings.ClusterSettings) Settings(org.opensearch.common.settings.Settings) HashSet(java.util.HashSet) Before(org.junit.Before)

Example 4 with ModelState

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

the class ADStatsTests method testADStatsNodeResponseWithEntity.

/**
 * Test we can serialize stats with entity
 * @throws IOException when writeTo and toXContent have errors.
 * @throws JsonPathNotFoundException when json deserialization cannot find a path
 */
@Test
public void testADStatsNodeResponseWithEntity() throws IOException, JsonPathNotFoundException {
    TreeMap<String, String> attributes = new TreeMap<>();
    String name1 = "a";
    String name2 = "b";
    String val1 = "a1";
    String val2 = "a2";
    attributes.put(name1, val1);
    attributes.put(name2, val2);
    String detectorId = "detectorId";
    Entity entity = Entity.createEntityFromOrderedMap(attributes);
    EntityModel entityModel = new EntityModel(entity, null, null);
    Clock clock = mock(Clock.class);
    when(clock.instant()).thenReturn(Instant.now());
    ModelState<EntityModel> state = new ModelState<EntityModel>(entityModel, entity.getModelId(detectorId).get(), detectorId, "entity", clock, 0.1f);
    Map<String, Object> stats = state.getModelStateAsMap();
    // Test serialization
    ADStatsNodeResponse adStatsNodeResponse = new ADStatsNodeResponse(discoveryNode1, stats);
    BytesStreamOutput output = new BytesStreamOutput();
    adStatsNodeResponse.writeTo(output);
    StreamInput streamInput = output.bytes().streamInput();
    ADStatsNodeResponse readResponse = ADStatsNodeResponse.readStats(streamInput);
    assertEquals("readStats failed", readResponse.getStatsMap(), adStatsNodeResponse.getStatsMap());
    // Test toXContent
    XContentBuilder builder = jsonBuilder();
    adStatsNodeResponse.toXContent(builder.startObject(), ToXContent.EMPTY_PARAMS).endObject();
    String json = Strings.toString(builder);
    for (Map.Entry<String, Object> stat : stats.entrySet()) {
        if (stat.getKey().equals(ModelState.LAST_CHECKPOINT_TIME_KEY) || stat.getKey().equals(ModelState.LAST_USED_TIME_KEY)) {
            assertEquals("toXContent does not work", JsonDeserializer.getLongValue(json, stat.getKey()), stat.getValue());
        } else if (stat.getKey().equals(CommonName.ENTITY_KEY)) {
            JsonArray array = JsonDeserializer.getArrayValue(json, stat.getKey());
            assertEquals(2, array.size());
            for (int i = 0; i < 2; i++) {
                JsonElement element = array.get(i);
                String entityName = JsonDeserializer.getChildNode(element, Entity.ATTRIBUTE_NAME_FIELD).getAsString();
                String entityValue = JsonDeserializer.getChildNode(element, Entity.ATTRIBUTE_VALUE_FIELD).getAsString();
                assertTrue(entityName.equals(name1) || entityName.equals(name2));
                if (entityName.equals(name1)) {
                    assertEquals(val1, entityValue);
                } else {
                    assertEquals(val2, entityValue);
                }
            }
        } else {
            assertEquals("toXContent does not work", JsonDeserializer.getTextValue(json, stat.getKey()), stat.getValue());
        }
    }
}
Also used : Entity(org.opensearch.ad.model.Entity) EntityModel(org.opensearch.ad.ml.EntityModel) ModelState(org.opensearch.ad.ml.ModelState) TreeMap(java.util.TreeMap) Clock(java.time.Clock) BytesStreamOutput(org.opensearch.common.io.stream.BytesStreamOutput) JsonArray(com.google.gson.JsonArray) JsonElement(com.google.gson.JsonElement) StreamInput(org.opensearch.common.io.stream.StreamInput) HashMap(java.util.HashMap) Map(java.util.Map) Collections.emptyMap(java.util.Collections.emptyMap) TreeMap(java.util.TreeMap) XContentBuilder(org.opensearch.common.xcontent.XContentBuilder) Test(org.junit.Test)

Example 5 with ModelState

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

the class PriorityCacheTests method replaceInOtherCacheSetUp.

private void replaceInOtherCacheSetUp() {
    Entity entity5 = Entity.createSingleAttributeEntity("attributeName1", "attributeVal5");
    Entity entity6 = Entity.createSingleAttributeEntity("attributeName1", "attributeVal6");
    ModelState<EntityModel> modelState5 = new ModelState<>(new EntityModel(entity5, new ArrayDeque<>(), null), entity5.getModelId(detectorId2).get(), detectorId2, ModelType.ENTITY.getName(), clock, 0);
    ModelState<EntityModel> modelState6 = new ModelState<>(new EntityModel(entity6, new ArrayDeque<>(), null), entity6.getModelId(detectorId2).get(), detectorId2, ModelType.ENTITY.getName(), clock, 0);
    for (int i = 0; i < 3; i++) {
        // bypass doorkeeper and leave room for lower frequency entity in testSelectToCold
        cacheProvider.get(entity5.getModelId(detectorId2).get(), detector2);
        cacheProvider.get(entity6.getModelId(detectorId2).get(), detector2);
    }
    for (int i = 0; i < 10; i++) {
        // entity1 cannot replace entity2 due to frequency
        cacheProvider.get(entity2.getModelId(detectorId).get(), detector);
    }
    // put modelState5 in dedicated and modelState6 in shared cache
    when(memoryTracker.canAllocate(anyLong())).thenReturn(true);
    cacheProvider.hostIfPossible(detector2, modelState5);
    cacheProvider.hostIfPossible(detector2, modelState6);
    // fill in dedicated cache
    cacheProvider.hostIfPossible(detector, modelState2);
    // don't allow to use shared cache afterwards
    when(memoryTracker.canAllocate(anyLong())).thenReturn(false);
}
Also used : Entity(org.opensearch.ad.model.Entity) EntityModel(org.opensearch.ad.ml.EntityModel) ModelState(org.opensearch.ad.ml.ModelState) ArrayDeque(java.util.ArrayDeque)

Aggregations

ModelState (org.opensearch.ad.ml.ModelState)9 EntityModel (org.opensearch.ad.ml.EntityModel)8 ArrayList (java.util.ArrayList)4 Entity (org.opensearch.ad.model.Entity)4 Clock (java.time.Clock)2 ArrayDeque (java.util.ArrayDeque)2 HashMap (java.util.HashMap)2 JsonArray (com.google.gson.JsonArray)1 JsonElement (com.google.gson.JsonElement)1 Instant (java.time.Instant)1 Collections.emptyMap (java.util.Collections.emptyMap)1 HashSet (java.util.HashSet)1 Map (java.util.Map)1 TreeMap (java.util.TreeMap)1 Before (org.junit.Before)1 Test (org.junit.Test)1 ArgumentMatchers.anyString (org.mockito.ArgumentMatchers.anyString)1 BulkItemResponse (org.opensearch.action.bulk.BulkItemResponse)1 BulkResponse (org.opensearch.action.bulk.BulkResponse)1 IndexResponse (org.opensearch.action.index.IndexResponse)1