Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions evcache-core/src/main/java/com/netflix/evcache/EVCacheImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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());
Expand All @@ -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
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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.
*
* <p>Every property resolves at construction through:
*
* <ol>
* <li><b>Per-app override:</b> {@code <appName>.<appKeySuffix>}</li>
* <li><b>Global default:</b> {@code <globalKey>}</li>
* <li><b>Static default:</b> the value baked into this class</li>
* </ol>
*
* <p>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 ->

Check failure on line 71 in evcache-core/src/main/java/com/netflix/evcache/config/EVCacheTranscoderProperties.java

View workflow job for this annotation

GitHub Actions / CI with Java 8

bad use of '>'

Check failure on line 71 in evcache-core/src/main/java/com/netflix/evcache/config/EVCacheTranscoderProperties.java

View workflow job for this annotation

GitHub Actions / CI with Java 8

bad use of '>'

Check failure on line 71 in evcache-core/src/main/java/com/netflix/evcache/config/EVCacheTranscoderProperties.java

View workflow job for this annotation

GitHub Actions / CI with Java 8

bad use of '>'

Check failure on line 71 in evcache-core/src/main/java/com/netflix/evcache/config/EVCacheTranscoderProperties.java

View workflow job for this annotation

GitHub Actions / CI with Java 8

bad use of '>'
* static-default chain used for the cached fields above.
*/
public <T> T getProperty(Key key, Class<T> type, T defaultValue) {
return getProperty(appName, propertyRepository, key, type, defaultValue).get();
}

private static <T> Property<T> getProperty(String appName, PropertyRepository propertyRepository,
Key key, Class<T> type, T defaultValue) {
if (appName == null || appName.isEmpty()) {
return propertyRepository.get(key.globalKey, type).orElse(defaultValue);
}
return propertyRepository.get(appName + "." + key.appKeySuffix, type)
.orElseGet(key.globalKey)
.orElse(defaultValue);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.netflix.evcache.config;

import static org.assertj.core.api.Assertions.assertThat;

import com.netflix.archaius.DefaultPropertyFactory;
import com.netflix.archaius.api.PropertyRepository;
import com.netflix.archaius.config.DefaultSettableConfig;

import org.testng.annotations.Test;

/**
* Three-level resolution tests for {@link EVCacheTranscoderProperties#isBinarySerializationEnabled()}:
* per-app override → global default → static default.
*/
public class EVCacheTranscoderPropertiesTest {

private static final String APP = "MYAPP";
private static final String PER_APP_KEY = "MYAPP.binary.serialization.enabled";
private static final String GLOBAL_KEY = "default.evcache.binary.serialization.enabled";

private static PropertyRepository repo(DefaultSettableConfig cfg) {
return DefaultPropertyFactory.from(cfg);
}

@Test
public void binarySerialization_perAppOverrideWins() {
DefaultSettableConfig cfg = new DefaultSettableConfig();
cfg.setProperty(PER_APP_KEY, "true");

EVCacheTranscoderProperties props = new EVCacheTranscoderProperties(APP, repo(cfg));
assertThat(props.isBinarySerializationEnabled()).isTrue();
}

@Test
public void binarySerialization_globalFallbackWhenPerAppUnset() {
DefaultSettableConfig cfg = new DefaultSettableConfig();
cfg.setProperty(GLOBAL_KEY, "true");

EVCacheTranscoderProperties props = new EVCacheTranscoderProperties(APP, repo(cfg));
assertThat(props.isBinarySerializationEnabled()).isTrue();
}

@Test
public void binarySerialization_staticDefaultWhenBothUnset() {
EVCacheTranscoderProperties props = new EVCacheTranscoderProperties(APP, repo(new DefaultSettableConfig()));
assertThat(props.isBinarySerializationEnabled()).isFalse();
}

@Test
public void binarySerialization_perAppBeatsGlobal() {
DefaultSettableConfig cfg = new DefaultSettableConfig();
cfg.setProperty(PER_APP_KEY, "false");
cfg.setProperty(GLOBAL_KEY, "true");

EVCacheTranscoderProperties props = new EVCacheTranscoderProperties(APP, repo(cfg));
assertThat(props.isBinarySerializationEnabled()).isFalse();
}

@Test
public void binarySerialization_nullAppNameUsesGlobalKey() {
DefaultSettableConfig cfg = new DefaultSettableConfig();
cfg.setProperty(GLOBAL_KEY, "true");

EVCacheTranscoderProperties props = new EVCacheTranscoderProperties(null, repo(cfg));
assertThat(props.isBinarySerializationEnabled()).isTrue();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.util.ArrayList;
import java.util.Arrays;

import com.netflix.evcache.config.EVCacheTranscoderProperties;
import org.testng.annotations.Test;

import com.netflix.evcache.EVCacheTranscoder;
Expand All @@ -34,7 +35,11 @@ public class EVCacheValueSerdeTest {

/** Binary-enabled transcoder, compression disabled, so encoded bytes start with our magic. */
private static EVCacheTranscoder binaryTranscoder() {
return new EVCacheTranscoder(20 * 1024 * 1024, Integer.MAX_VALUE, true);
com.netflix.archaius.config.DefaultSettableConfig cfg = new com.netflix.archaius.config.DefaultSettableConfig();
cfg.setProperty("testApp.binary.serialization.enabled", "true");
return new EVCacheTranscoder(20 * 1024 * 1024, Integer.MAX_VALUE,
new EVCacheTranscoderProperties("testApp",
com.netflix.archaius.DefaultPropertyFactory.from(cfg)));
}

/** Default transcoder (binary OFF, falls through to native Java serialization). */
Expand Down
1 change: 1 addition & 0 deletions evcache-core/src/test/java/test-suite.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<classes>
<class name="com.netflix.evcache.pool.NodeLocatorLookupTest" />
<class name="com.netflix.evcache.pool.EVCacheValueSerdeTest" />
<class name="com.netflix.evcache.config.EVCacheTranscoderPropertiesTest" />
</classes>
</test>
<test name="MockTests">
Expand Down
Loading