All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opensearch.index.ShardIndexingPressureStore Maven / Gradle / Ivy

There is a newer version: 2.18.0
Show newest version
/*
 * Copyright OpenSearch Contributors.
 * SPDX-License-Identifier: Apache-2.0
 */

package org.opensearch.index;

import org.opensearch.common.settings.ClusterSettings;
import org.opensearch.common.settings.Setting;
import org.opensearch.common.settings.Settings;
import org.opensearch.common.util.concurrent.ConcurrentCollections;
import org.opensearch.core.index.shard.ShardId;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BooleanSupplier;

import static java.util.Objects.isNull;

/**
 * Shard indexing pressure store acts as a central repository for all the shard-level tracker objects currently being
 * used at the Node level, for tracking indexing pressure requests.
 * Store manages the tracker lifecycle, from creation, access, until it is evicted to be collected.
 * 

* Trackers are maintained at two levels for access simplicity and better memory management: *

* 1. shardIndexingPressureHotStore : As the name suggests, it is hot store for tracker objects which are currently live i.e. being used * to track an ongoing request. *

* 2. shardIndexingPressureColdStore : This acts as the store for all the shard tracking objects which are currently being used * by the framework. In addition to hot trackers, the recently used trackers which are although not currently live, but again can be used * in near future, are also part of this store. To limit any memory implications, this store has an upper limit on the maximum number of * trackers its can hold at any given time, which is a configurable dynamic setting. *

* Tracking objects when created are part of both the hot store as well as cold store. However, once the object * is no more live it is removed from the hot store. Objects in the cold store are evicted once the cold store * reaches its maximum limit. Think of it like a periodic purge when upper limit is hit. * During get if tracking object is not present in the hot store, a lookup is made into the cache store. If found, * object is brought into the hot store again, until it remains active. If not present in the either store, a fresh * object is instantiated and registered in both the stores for concurrent accesses. *

* Note: The implementation of shardIndexingPressureColdStore methods is such that get, * update and evict operations can be abstracted out to support any other strategy such as LRU, if * discovered a need later. * * @opensearch.internal */ public class ShardIndexingPressureStore { // This represents the maximum value for the cold store size. public static final Setting MAX_COLD_STORE_SIZE = Setting.intSetting( "shard_indexing_pressure.cache_store.max_size", 200, 100, 1000, Setting.Property.NodeScope, Setting.Property.Dynamic ); private final Map shardIndexingPressureHotStore = ConcurrentCollections .newConcurrentMapWithAggressiveConcurrency(); private final Map shardIndexingPressureColdStore = new HashMap<>(); private final ShardIndexingPressureSettings shardIndexingPressureSettings; private volatile int maxColdStoreSize; public ShardIndexingPressureStore( ShardIndexingPressureSettings shardIndexingPressureSettings, ClusterSettings clusterSettings, Settings settings ) { this.shardIndexingPressureSettings = shardIndexingPressureSettings; this.maxColdStoreSize = MAX_COLD_STORE_SIZE.get(settings).intValue(); clusterSettings.addSettingsUpdateConsumer(MAX_COLD_STORE_SIZE, this::setMaxColdStoreSize); } public ShardIndexingPressureTracker getShardIndexingPressureTracker(ShardId shardId) { ShardIndexingPressureTracker tracker = shardIndexingPressureHotStore.get(shardId); if (isNull(tracker)) { // Attempt from Indexing pressure cold store tracker = shardIndexingPressureColdStore.get(shardId); // If not already present in cold store instantiate a new one if (isNull(tracker)) { tracker = shardIndexingPressureHotStore.computeIfAbsent( shardId, (k) -> new ShardIndexingPressureTracker( shardId, this.shardIndexingPressureSettings.getShardPrimaryAndCoordinatingBaseLimits(), this.shardIndexingPressureSettings.getShardReplicaBaseLimits() ) ); // Write through into the cold store for future reference updateShardIndexingPressureColdStore(tracker); } else { // Attempt update tracker to the hot store and return the tracker which finally made to the hot store to avoid any race ShardIndexingPressureTracker newTracker = shardIndexingPressureHotStore.putIfAbsent(shardId, tracker); tracker = newTracker == null ? tracker : newTracker; } } return tracker; } public Map getShardIndexingPressureHotStore() { return Collections.unmodifiableMap(shardIndexingPressureHotStore); } public Map getShardIndexingPressureColdStore() { return Collections.unmodifiableMap(shardIndexingPressureColdStore); } public void tryTrackerCleanupFromHotStore(ShardIndexingPressureTracker tracker, BooleanSupplier condition) { if (condition.getAsBoolean()) { // Try inserting into cold store again in case there was an eviction triggered shardIndexingPressureColdStore.putIfAbsent(tracker.getShardId(), tracker); // Remove from the hot store shardIndexingPressureHotStore.remove(tracker.getShardId(), tracker); } } /** * This is used to update the reference of tracker in cold store, to be re-used later of tracker is removed from hot store upon request * completion. When the cold store size reaches maximum, all the tracker objects in cold store are flushed. Flush is a less frequent * (periodic) operation, can be sized based on workload. It is okay to not to synchronize counters being flushed, as * objects in the cold store are only empty references, and can be re-initialized if needed. */ private void updateShardIndexingPressureColdStore(ShardIndexingPressureTracker tracker) { if (shardIndexingPressureColdStore.size() > maxColdStoreSize) { shardIndexingPressureColdStore.clear(); } shardIndexingPressureColdStore.put(tracker.getShardId(), tracker); } private void setMaxColdStoreSize(int maxColdStoreSize) { this.maxColdStoreSize = maxColdStoreSize; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy