From 60d53ce44e846a9a55f0451480a8876516739b66 Mon Sep 17 00:00:00 2001 From: Peter Alfonsi Date: Mon, 18 Nov 2024 17:54:29 -0800 Subject: [PATCH] First draft (settings not functional) Signed-off-by: Peter Alfonsi --- .../opensearch/cache/EhcacheCachePlugin.java | 13 +- .../cache/EhcacheDiskCacheSettings.java | 2 +- .../cache/EhcacheTieredCacheSettings.java | 98 ++++++++++++++ .../cache/store/disk/EhcacheDiskCache.java | 72 +++++++--- .../cache/store/disk/EhcacheTieredCache.java | 126 ++++++++++++++++++ 5 files changed, 288 insertions(+), 23 deletions(-) create mode 100644 plugins/cache-ehcache/src/main/java/org/opensearch/cache/EhcacheTieredCacheSettings.java create mode 100644 plugins/cache-ehcache/src/main/java/org/opensearch/cache/store/disk/EhcacheTieredCache.java diff --git a/plugins/cache-ehcache/src/main/java/org/opensearch/cache/EhcacheCachePlugin.java b/plugins/cache-ehcache/src/main/java/org/opensearch/cache/EhcacheCachePlugin.java index ceda96e4a7d7d..227ffe9075486 100644 --- a/plugins/cache-ehcache/src/main/java/org/opensearch/cache/EhcacheCachePlugin.java +++ b/plugins/cache-ehcache/src/main/java/org/opensearch/cache/EhcacheCachePlugin.java @@ -9,6 +9,7 @@ package org.opensearch.cache; import org.opensearch.cache.store.disk.EhcacheDiskCache; +import org.opensearch.cache.store.disk.EhcacheTieredCache; import org.opensearch.common.cache.CacheType; import org.opensearch.common.cache.ICache; import org.opensearch.common.settings.Setting; @@ -35,13 +36,21 @@ public EhcacheCachePlugin() {} @Override public Map getCacheFactoryMap() { - return Map.of(EhcacheDiskCache.EhcacheDiskCacheFactory.EHCACHE_DISK_CACHE_NAME, new EhcacheDiskCache.EhcacheDiskCacheFactory()); + return Map.of( + EhcacheDiskCache.EhcacheDiskCacheFactory.EHCACHE_DISK_CACHE_NAME, new EhcacheDiskCache.EhcacheDiskCacheFactory(), + EhcacheTieredCache.EhcacheTieredCacheFactory.EHCACHE_TIERED_CACHE_NAME, new EhcacheTieredCache.EhcacheTieredCacheFactory() + ); } @Override public List> getSettings() { List> settingList = new ArrayList<>(); - for (Map.Entry>> entry : CACHE_TYPE_MAP.entrySet()) { + for (Map.Entry>> entry : EhcacheDiskCacheSettings.CACHE_TYPE_MAP.entrySet()) { + for (Map.Entry> entry1 : entry.getValue().entrySet()) { + settingList.add(entry1.getValue()); + } + } + for (Map.Entry>> entry : EhcacheTieredCacheSettings.CACHE_TYPE_MAP.entrySet()) { for (Map.Entry> entry1 : entry.getValue().entrySet()) { settingList.add(entry1.getValue()); } diff --git a/plugins/cache-ehcache/src/main/java/org/opensearch/cache/EhcacheDiskCacheSettings.java b/plugins/cache-ehcache/src/main/java/org/opensearch/cache/EhcacheDiskCacheSettings.java index cbc104f2d0b00..e7d7b52537be6 100644 --- a/plugins/cache-ehcache/src/main/java/org/opensearch/cache/EhcacheDiskCacheSettings.java +++ b/plugins/cache-ehcache/src/main/java/org/opensearch/cache/EhcacheDiskCacheSettings.java @@ -189,7 +189,7 @@ public class EhcacheDiskCacheSettings { * Used to form concrete setting for cache types and return desired map * @return map of cacheType and associated settings. */ - private static final Map>> getCacheTypeMap() { + protected static Map>> getCacheTypeMap() { Map>> cacheTypeMap = new HashMap<>(); for (CacheType cacheType : CacheType.values()) { Map> settingMap = new HashMap<>(); diff --git a/plugins/cache-ehcache/src/main/java/org/opensearch/cache/EhcacheTieredCacheSettings.java b/plugins/cache-ehcache/src/main/java/org/opensearch/cache/EhcacheTieredCacheSettings.java new file mode 100644 index 0000000000000..30fe0f46c0024 --- /dev/null +++ b/plugins/cache-ehcache/src/main/java/org/opensearch/cache/EhcacheTieredCacheSettings.java @@ -0,0 +1,98 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cache; + +import org.opensearch.cache.store.disk.EhcacheDiskCache; +import org.opensearch.cache.store.disk.EhcacheTieredCache; +import org.opensearch.common.cache.CacheType; +import org.opensearch.common.settings.Setting; + +import java.util.HashMap; +import java.util.Map; + +import static org.opensearch.common.settings.Setting.Property.NodeScope; + +/** + * Settings related to EhcacheTieredCache. + */ +public class EhcacheTieredCacheSettings { + // TODO: Since I can't override a static suffix for the settings, for now it's just gonna use ehcache_disk settings where appropriate. + // This can be fixed later on if needed. + + /** + * Default cache size in bytes ie 1gb. + */ + public static final long DEFAULT_HEAP_CACHE_SIZE_IN_BYTES = 10_485_760L; // 10 MB + + /** + * Heap cache max size setting. + */ + public static final Setting.AffixSetting HEAP_CACHE_MAX_SIZE_IN_BYTES_SETTING = Setting.suffixKeySetting( + EhcacheTieredCache.EhcacheTieredCacheFactory.EHCACHE_TIERED_CACHE_NAME + ".heap_max_size_in_bytes", + (key) -> Setting.longSetting(key, DEFAULT_HEAP_CACHE_SIZE_IN_BYTES, NodeScope) + ); + + /** + * Key for max size. + */ + public static final String HEAP_CACHE_MAX_SIZE_IN_BYTES_KEY = "heap_max_size_in_bytes"; + + private static final Map> KEY_SETTING_MAP = Map.of( + HEAP_CACHE_MAX_SIZE_IN_BYTES_KEY, + HEAP_CACHE_MAX_SIZE_IN_BYTES_SETTING + ); + + /** + * Map to store desired settings for a cache type. + */ + public static final Map>> CACHE_TYPE_MAP = getCacheTypeMap(); + + /** + * Used to form concrete setting for cache types and return desired map + * @return map of cacheType and associated settings. + * // TODO: Duplicated from other settings, but currently not extending those... + */ + protected static Map>> getCacheTypeMap() { + Map>> cacheTypeMap = new HashMap<>(); + for (CacheType cacheType : CacheType.values()) { + Map> settingMap = new HashMap<>(); + for (Map.Entry> entry : KEY_SETTING_MAP.entrySet()) { + settingMap.put(entry.getKey(), entry.getValue().getConcreteSettingForNamespace(cacheType.getSettingPrefix())); + } + cacheTypeMap.put(cacheType, settingMap); + } + return cacheTypeMap; + } + + /** + * Fetches setting list for a combination of cache type and store name. + * @param cacheType cache type + * @return settings + * // tODO: cant override this bc static. So not extending other settings for now. + */ + public static final Map> getSettingListForCacheType(CacheType cacheType) { + Map> cacheTypeSettings = CACHE_TYPE_MAP.get(cacheType); + if (cacheTypeSettings == null) { + throw new IllegalArgumentException( + "No settings exist for cache store name: " + + EhcacheDiskCache.EhcacheDiskCacheFactory.EHCACHE_DISK_CACHE_NAME + + "associated with " + + "cache type: " + + cacheType + ); + } + return cacheTypeSettings; + } + + /** + * Default constructor. Added to fix javadocs. + */ + public EhcacheTieredCacheSettings() {} + +} diff --git a/plugins/cache-ehcache/src/main/java/org/opensearch/cache/store/disk/EhcacheDiskCache.java b/plugins/cache-ehcache/src/main/java/org/opensearch/cache/store/disk/EhcacheDiskCache.java index 0fa0f8162bb98..238b591424cbc 100644 --- a/plugins/cache-ehcache/src/main/java/org/opensearch/cache/store/disk/EhcacheDiskCache.java +++ b/plugins/cache-ehcache/src/main/java/org/opensearch/cache/store/disk/EhcacheDiskCache.java @@ -101,8 +101,8 @@ public class EhcacheDiskCache implements ICache { private static final Logger logger = LogManager.getLogger(EhcacheDiskCache.class); // Unique id associated with this cache. - final static String UNIQUE_ID = UUID.randomUUID().toString(); - final static String THREAD_POOL_ALIAS_PREFIX = "ehcachePool"; + private final static String UNIQUE_ID = UUID.randomUUID().toString(); + private final static String THREAD_POOL_ALIAS_PREFIX = "ehcachePool"; final static int MINIMUM_MAX_SIZE_IN_BYTES = 1024 * 100; // 100KB final static String CACHE_DATA_CLEANUP_DURING_INITIALIZATION_EXCEPTION = "Failed to delete ehcache disk cache under " + "path: %s during initialization. Please clean this up manually and restart the process"; @@ -113,7 +113,10 @@ public class EhcacheDiskCache implements ICache { // Disk cache. Using ByteArrayWrapper to compare two byte[] by values rather than the default reference checks @SuppressWarnings({ "rawtypes" }) // We have to use the raw type as there's no way to pass the "generic class" to ehcache private final Cache cache; - private final long maxWeightInBytes; + /** + * Max weight for disk tier. + */ + protected final long maxWeightInBytes; private final String storagePath; private final Class keyType; private final Class valueType; @@ -194,8 +197,22 @@ PersistentCacheManager getCacheManager() { return this.cacheManager; } + /** + * Specify resource pools. + * @return + */ + protected ResourcePoolsBuilder getResourcePoolsBuilder() { + return ResourcePoolsBuilder.newResourcePoolsBuilder().disk(maxWeightInBytes, MemoryUnit.B); + } + + /** + * Build a cache. + * @param expireAfterAccess + * @param builder + * @return + */ @SuppressWarnings({ "rawtypes" }) - private Cache buildCache(Duration expireAfterAccess, Builder builder) { + protected Cache buildCache(Duration expireAfterAccess, Builder builder) { // Creating the cache requires permissions specified in plugin-security.policy return AccessController.doPrivileged((PrivilegedAction>) () -> { try { @@ -210,7 +227,7 @@ private Cache buildCache(Duration expireAfterAccess CacheConfigurationBuilder.newCacheConfigurationBuilder( ICacheKey.class, ByteArrayWrapper.class, - ResourcePoolsBuilder.newResourcePoolsBuilder().disk(maxWeightInBytes, MemoryUnit.B) + getResourcePoolsBuilder() ).withExpiry(new ExpiryPolicy<>() { @Override public Duration getExpiryForCreation(ICacheKey key, ByteArrayWrapper value) { @@ -664,7 +681,7 @@ public boolean equals(ByteArrayWrapper object, ByteBuffer binary) throws ClassNo * @param value the value * @return the serialized value */ - private ByteArrayWrapper serializeValue(V value) { + protected ByteArrayWrapper serializeValue(V value) { return new ByteArrayWrapper(valueSerializer.serialize(value)); } @@ -673,7 +690,7 @@ private ByteArrayWrapper serializeValue(V value) { * @param binary the serialized value * @return the deserialized value */ - private V deserializeValue(ByteArrayWrapper binary) { + protected V deserializeValue(ByteArrayWrapper binary) { if (binary == null) { return null; } @@ -695,9 +712,17 @@ public static class EhcacheDiskCacheFactory implements ICache.Factory { */ public EhcacheDiskCacheFactory() {} - @Override + /** + * Setup the builder. + * @param config + * @param cacheType + * @param cacheFactories + * @return + * @param + * @param + */ @SuppressWarnings({ "unchecked" }) // Required to ensure the serializers output byte[] - public ICache create(CacheConfig config, CacheType cacheType, Map cacheFactories) { + protected Builder setupBuilder(CacheConfig config, CacheType cacheType, Map cacheFactories) { Map> settingList = EhcacheDiskCacheSettings.getSettingListForCacheType(cacheType); Settings settings = config.getSettings(); @@ -752,7 +777,13 @@ public ICache create(CacheConfig config, CacheType cacheType, } else { builder.setNumberOfSegments(segmentCount); } - return builder.build(); + return builder; + } + + @Override + @SuppressWarnings({ "unchecked" }) // Required to ensure the serializers output byte[] + public ICache create(CacheConfig config, CacheType cacheType, Map cacheFactories) { + return setupBuilder(config, cacheType, cacheFactories).build(); } @Override @@ -768,22 +799,23 @@ public String getCacheName() { */ public static class Builder extends ICacheBuilder { - private CacheType cacheType; - private String storagePath; + // todo: should be protected, but dont want to do javadocs rn + CacheType cacheType; + String storagePath; - private String threadPoolAlias; + String threadPoolAlias; - private String diskCacheAlias; + String diskCacheAlias; // Provides capability to make ehCache event listener to run in sync mode. Used for testing too. - private boolean isEventListenerModeSync; + boolean isEventListenerModeSync; - private Class keyType; + Class keyType; - private Class valueType; - private List dimensionNames; - private Serializer keySerializer; - private Serializer valueSerializer; + Class valueType; + List dimensionNames; + Serializer keySerializer; + Serializer valueSerializer; /** * Default constructor. Added to fix javadocs. diff --git a/plugins/cache-ehcache/src/main/java/org/opensearch/cache/store/disk/EhcacheTieredCache.java b/plugins/cache-ehcache/src/main/java/org/opensearch/cache/store/disk/EhcacheTieredCache.java new file mode 100644 index 0000000000000..3b26be27ff0f8 --- /dev/null +++ b/plugins/cache-ehcache/src/main/java/org/opensearch/cache/store/disk/EhcacheTieredCache.java @@ -0,0 +1,126 @@ +/* + * SPDX-License-Identifier: Apache-2.0 + * + * The OpenSearch Contributors require contributions made to + * this file be licensed under the Apache-2.0 license or a + * compatible open source license. + */ + +package org.opensearch.cache.store.disk; + +import org.ehcache.config.builders.ResourcePoolsBuilder; +import org.ehcache.config.units.MemoryUnit; + +import org.opensearch.cache.EhcacheTieredCacheSettings; +import org.opensearch.common.cache.CacheType; +import org.opensearch.common.cache.store.config.CacheConfig; +import org.opensearch.common.settings.Setting; +import org.opensearch.common.settings.Settings; + +import java.util.Map; + +import static org.opensearch.cache.EhcacheTieredCacheSettings.HEAP_CACHE_MAX_SIZE_IN_BYTES_KEY; + +/** + * Tiered ehcache cache. + * @param + * @param + */ +public class EhcacheTieredCache extends EhcacheDiskCache { + + private final long maxHeapWeightInBytes; + EhcacheTieredCache(Builder builder) { + super(builder); + // TODO: Problem: cache is created during super's constructor. So it sees 0 for this value + this.maxHeapWeightInBytes = builder.maxHeapWeightInBytes; + } + + @Override + // TODO: Hardcode 40 MB for initial tests. Fix this later if this cache is any good relative to TSC. + protected ResourcePoolsBuilder getResourcePoolsBuilder() { + //return ResourcePoolsBuilder.newResourcePoolsBuilder().heap(maxHeapWeightInBytes, MemoryUnit.B).disk(maxWeightInBytes, MemoryUnit.B); + return ResourcePoolsBuilder.newResourcePoolsBuilder().heap(40, MemoryUnit.MB).disk(maxWeightInBytes, MemoryUnit.B); + } + + /** + * Factory to create an ehcache tiered cache. + */ + public static class EhcacheTieredCacheFactory extends EhcacheDiskCache.EhcacheDiskCacheFactory { + + /** + * Ehcache disk cache name. + */ + public static final String EHCACHE_TIERED_CACHE_NAME = "ehcache_tiered"; + + /** + * Default constructor. + */ + public EhcacheTieredCacheFactory() {} + + @Override + protected EhcacheDiskCache.Builder setupBuilder(CacheConfig config, CacheType cacheType, Map cacheFactories) { + Builder builder = new Builder<>(super.setupBuilder(config, cacheType, cacheFactories)); + Map> tieredSpecificSettingList = EhcacheTieredCacheSettings.getSettingListForCacheType(cacheType); + Settings settings = config.getSettings(); + long maxHeapWeight = (long) tieredSpecificSettingList.get(HEAP_CACHE_MAX_SIZE_IN_BYTES_KEY).get(settings); + builder.setMaxHeapWeightInBytes(maxHeapWeight); + return builder; + } + + @Override + public String getCacheName() { + return EHCACHE_TIERED_CACHE_NAME; + } + } + + /** + * Blorp + * @param + * @param + */ + public static class Builder extends EhcacheDiskCache.Builder { + private long maxHeapWeightInBytes; + + // Yuck! + + /** + * Make from ehcache disk cache builder + * @param baseBuilder + */ + public Builder(EhcacheDiskCache.Builder baseBuilder) { + this.setMaximumWeightInBytes(baseBuilder.getMaxWeightInBytes()); + this.setWeigher(baseBuilder.getWeigher()); + this.setExpireAfterAccess(baseBuilder.getExpireAfterAcess()); + this.setSettings(baseBuilder.getSettings()); + this.setRemovalListener(baseBuilder.getRemovalListener()); + this.setStatsTrackingEnabled(baseBuilder.getStatsTrackingEnabled()); + this.setNumberOfSegments(baseBuilder.getNumberOfSegments()); + + this.cacheType = baseBuilder.cacheType; + this.storagePath = baseBuilder.storagePath; + this.threadPoolAlias = baseBuilder.threadPoolAlias; + this.diskCacheAlias = baseBuilder.diskCacheAlias; + this.isEventListenerModeSync = baseBuilder.isEventListenerModeSync; + this.keyType = baseBuilder.keyType; + this.valueType = baseBuilder.valueType; + this.dimensionNames = baseBuilder.dimensionNames; + this.keySerializer = baseBuilder.keySerializer; + this.valueSerializer = baseBuilder.valueSerializer; + } + + /** + * Blorp + * @param maxHeapWeightInBytes + * @return + */ + public Builder setMaxHeapWeightInBytes(long maxHeapWeightInBytes) { + this.maxHeapWeightInBytes = maxHeapWeightInBytes; + return this; + } + + @Override + public EhcacheTieredCache build() { + return new EhcacheTieredCache<>(this); + } + } +}