diff --git a/evcache-core/src/main/java/com/netflix/evcache/EVCacheImpl.java b/evcache-core/src/main/java/com/netflix/evcache/EVCacheImpl.java index dadc5a38..f9e9084a 100644 --- a/evcache-core/src/main/java/com/netflix/evcache/EVCacheImpl.java +++ b/evcache-core/src/main/java/com/netflix/evcache/EVCacheImpl.java @@ -6,6 +6,7 @@ import com.netflix.archaius.api.PropertyRepository; import com.netflix.evcache.EVCacheInMemoryCache.DataNotFoundException; import com.netflix.evcache.EVCacheLatch.Policy; +import com.netflix.evcache.config.EVCacheTranscoderProperties; import com.netflix.evcache.dto.KeyMapDto; import com.netflix.evcache.event.EVCacheEvent; import com.netflix.evcache.event.EVCacheEventListener; @@ -76,8 +77,7 @@ public class EVCacheImpl implements EVCache, EVCacheImplMBean { private static final Logger log = LoggerFactory.getLogger(EVCacheImpl.class); - - private static final int ENVELOPE_COMPRESSION_DISABLED = Integer.MAX_VALUE; + private static final int DEFAULT_COMPRESSION_THRESHOLD = Integer.MAX_VALUE; private final Clock clock; private final String _appName; @@ -166,12 +166,13 @@ public class EVCacheImpl implements EVCache, EVCacheImplMBean { this.maxHashLength = propertyRepository.get(appName + ".max.hash.length", Integer.class).orElse(-1); this.encoderBase = propertyRepository.get(appName + ".hash.encoder", String.class).orElse("base64"); this.autoHashKeys = propertyRepository.get(_appName + ".auto.hash.keys", Boolean.class).orElseGet("evcache.auto.hash.keys").orElse(false); - // Whether the EVCacheValue envelope (hashed keys) is written using the compact binary format - // instead of native Java serialization. - final boolean useBinarySerialization = propertyRepository.get(_appName + ".envelope.binary.serialization.enabled", Boolean.class) - .orElseGet("evcache.envelope.binary.serialization.enabled").orElse(false).get(); + // EVCacheValue envelope (hashed-key path) transcoder. The binary-vs-Java-OOS encoding + // switch is resolved through EVCacheTranscoderProperties; max size is read inline and + // compression is held at DEFAULT_COMPRESSION_THRESHOLD (Integer.MAX_VALUE) so the + // envelope's leading magic byte stays untouched on the wire. + final EVCacheTranscoderProperties evCacheTranscoderProperties = new EVCacheTranscoderProperties(_appName, propertyRepository); final int maxValueSize = propertyRepository.get("default.evcache.max.data.size", Integer.class).orElse(20 * 1024 * 1024).get(); - this.evcacheValueTranscoder = new EVCacheTranscoder(maxValueSize, ENVELOPE_COMPRESSION_DISABLED, useBinarySerialization); + this.evcacheValueTranscoder = new EVCacheTranscoder(maxValueSize, DEFAULT_COMPRESSION_THRESHOLD, evCacheTranscoderProperties); // default max key length is 200, instead of using what is defined in MemcachedClientIF.MAX_KEY_LENGTH (250). This is to accommodate // auto key prepend with appname for duet feature. diff --git a/evcache-core/src/main/java/com/netflix/evcache/EVCacheTranscoder.java b/evcache-core/src/main/java/com/netflix/evcache/EVCacheTranscoder.java index 789dd100..e5e13feb 100644 --- a/evcache-core/src/main/java/com/netflix/evcache/EVCacheTranscoder.java +++ b/evcache-core/src/main/java/com/netflix/evcache/EVCacheTranscoder.java @@ -1,5 +1,6 @@ package com.netflix.evcache; +import com.netflix.evcache.config.EVCacheTranscoderProperties; import com.netflix.evcache.pool.EVCacheValue; import com.netflix.evcache.pool.EVCacheValueSerde; import com.netflix.evcache.util.EVCacheConfig; @@ -8,7 +9,7 @@ public class EVCacheTranscoder extends EVCacheSerializingTranscoder { - private final boolean useBinarySerialization; + private final EVCacheTranscoderProperties properties; public EVCacheTranscoder() { this(EVCacheConfig.getInstance().getPropertyRepository().get("default.evcache.max.data.size", Integer.class).orElse(20 * 1024 * 1024).get()); @@ -19,13 +20,15 @@ public EVCacheTranscoder(int max) { } public EVCacheTranscoder(int max, int compressionThreshold) { - this(max, compressionThreshold, false); + this(max, compressionThreshold, new EVCacheTranscoderProperties(null, EVCacheConfig.getInstance().getPropertyRepository())); } - public EVCacheTranscoder(int max, int compressionThreshold, boolean useBinarySerialization) { + public EVCacheTranscoder(int max, int compressionThreshold, EVCacheTranscoderProperties properties) { super(max); - setCompressionThreshold(compressionThreshold); - this.useBinarySerialization = useBinarySerialization; + this.properties = properties; + this.setCompressionThreshold( + compressionThreshold + ); } @Override @@ -46,7 +49,7 @@ public CachedData encode(Object o) { @Override protected byte[] serialize(Object o) { - if (useBinarySerialization && o instanceof EVCacheValue) { + if (this.properties.isBinarySerializationEnabled() && o instanceof EVCacheValue) { return EVCacheValueSerde.serialize((EVCacheValue) o); } return super.serialize(o); diff --git a/evcache-core/src/main/java/com/netflix/evcache/config/EVCacheTranscoderProperties.java b/evcache-core/src/main/java/com/netflix/evcache/config/EVCacheTranscoderProperties.java new file mode 100644 index 00000000..b8fcfb28 --- /dev/null +++ b/evcache-core/src/main/java/com/netflix/evcache/config/EVCacheTranscoderProperties.java @@ -0,0 +1,87 @@ +package com.netflix.evcache.config; + +import com.netflix.archaius.api.Property; +import com.netflix.archaius.api.PropertyRepository; + +/** + * Typed access to the FastProperties that govern {@link com.netflix.evcache.EVCacheTranscoder} + * behavior. Today the bundle holds a single entry — {@link Key#USE_BINARY_SERIALIZATION} — but + * the {@link Key} enum is the extension point: additional transcoder properties land here and + * inherit the same resolution chain without touching call sites. + * + *
Every property resolves at construction through: + * + *
Properties are read once at construction and cached as primitives. A future field that
+ * needs runtime mutability can skip the cached primitive and call
+ * {@link #getProperty(Key, Class, Object)} on each access — the three-level resolution
+ * applies to dynamic reads too.
+ */
+public class EVCacheTranscoderProperties {
+
+
+ public enum Key {
+ USE_BINARY_SERIALIZATION("binary.serialization.enabled", "default.evcache.binary.serialization.enabled");
+
+ final String appKeySuffix;
+ final String globalKey;
+
+ Key(String appKeySuffix, String globalKey) {
+ this.appKeySuffix = appKeySuffix;
+ this.globalKey = globalKey;
+ }
+ }
+
+ private static final boolean DEFAULT_BINARY_SERIALIZATION_ENABLED = false;
+
+ private final String appName;
+ private final PropertyRepository propertyRepository;
+
+ private final boolean binarySerializationEnabled;
+
+ /**
+ * Construct the bundle and snapshot every property via the three-level resolution chain.
+ *
+ * @param appName the EVCache app name used as the per-app override prefix
+ * (e.g. {@code "EVCACHE_FOO"}). When {@code null} or empty the
+ * per-app step is skipped and resolution starts at the global
+ * key — useful for the no-app transcoder constructors and for
+ * callers that only want fleet-wide defaults.
+ * @param propertyRepository the Archaius2 PropertyRepository to resolve against. Never null;
+ * pass {@code EVCacheConfig.getInstance().getPropertyRepository()}
+ * for the production wiring.
+ */
+ public EVCacheTranscoderProperties(String appName, PropertyRepository propertyRepository) {
+ this.appName = appName;
+ this.propertyRepository = propertyRepository;
+ this.binarySerializationEnabled = getProperty(appName, propertyRepository,
+ Key.USE_BINARY_SERIALIZATION, Boolean.class, DEFAULT_BINARY_SERIALIZATION_ENABLED).get();
+ }
+
+ public boolean isBinarySerializationEnabled() {
+ return binarySerializationEnabled;
+ }
+
+ /**
+ * Read a property dynamically (re-evaluates on every call), with the same per-app -> global ->
+ * static-default chain used for the cached fields above.
+ */
+ public