Skip to content

Add Zstd support for EVCache#197

Open
janewang1680 wants to merge 15 commits into
masterfrom
janewang1680/evcache_zstd
Open

Add Zstd support for EVCache#197
janewang1680 wants to merge 15 commits into
masterfrom
janewang1680/evcache_zstd

Conversation

@janewang1680

@janewang1680 janewang1680 commented Jun 10, 2026

Copy link
Copy Markdown

Summary
Add pluggable compression algorithm support (gzip, zstd) via a new CompressionAlgorithm enum on EVCacheSerializingTranscoder, with magic-byte auto-detection on decode so existing gzip-compressed cache entries
continue to decode correctly. Algorithm and zstd level are configurable via two new fast properties:
default.evcache.compression.algorithm (default GZIP)
default.evcache.compression.zstd.level (default 3)
Fix a pre-existing bug in the compression ratio metric where compressed.length / b.length always evaluated to 1.0 because b was reassigned to compressed before the ratio calculation. The metric now uses the
captured originalLength, producing accurate ratios.
Test plan
./gradlew :evcache-core:test --tests "com.netflix.evcache.EVCacheSerializingTranscoderTest" — 17 tests covering enum values, default constructor, gzip/zstd round-trip, cross-decode (gzip-written read by zstd
transcoder and vice versa), and FP wiring.
Verify backward compatibility: data written with gzip in production decodes correctly after deploy.
Verify compression ratio metric (COMPRESSION_RATIO timer) reports realistic ratios (not always ~100%) in a canary.
Verify it works in our internal services

Comment thread evcache-core/src/main/java/com/netflix/evcache/EVCacheSerializingTranscoder.java Outdated
Comment thread evcache-core/src/main/java/com/netflix/evcache/EVCacheSerializingTranscoder.java Outdated
Comment thread evcache-core/src/main/java/com/netflix/evcache/EVCacheSerializingTranscoder.java Outdated
Comment thread evcache-core/src/main/java/com/netflix/evcache/EVCacheSerializingTranscoder.java Outdated
Comment thread evcache-core/src/main/java/com/netflix/evcache/EVCacheSerializingTranscoder.java Outdated
Comment thread evcache-core/build.gradle Outdated
@srrangarajan srrangarajan requested a review from shy-1234 June 22, 2026 16:42
@srrangarajan

Copy link
Copy Markdown
Contributor

@shy-1234 can you also review the PR please

Comment thread evcache-core/src/main/java/com/netflix/evcache/EVCacheSerializingTranscoder.java Outdated
Comment thread evcache-core/src/main/java/com/netflix/evcache/EVCacheSerializingTranscoder.java Outdated
Comment thread evcache-core/src/main/java/com/netflix/evcache/EVCacheTranscoder.java Outdated
Comment thread evcache-core/src/main/java/com/netflix/evcache/EVCacheTranscoder.java Outdated
joegoogle123 pushed a commit that referenced this pull request Jun 30, 2026
…nscoderProperties

Today EVCacheImpl reads the binary-serialization FastProperty inline:

```java
final boolean useBinarySerialization = propertyRepository.get(_appName + ".envelope.binary.serialization.enabled", Boolean.class)
        .orElseGet("evcache.envelope.binary.serialization.enabled").orElse(false).get();
this.evcacheValueTranscoder = new EVCacheTranscoder(maxValueSize, ENVELOPE_COMPRESSION_DISABLED, useBinarySerialization);
```

This change introduces an EVCacheTranscoderProperties bundle that owns the
read and exposes the result behind a typed accessor. Each property follows a
uniform three-level resolution chain:

  1. <appName>.<appSuffix>   (per-app override)
  2. <globalKey>             (fleet-wide global)
  3. compiled-in static default

Today the bundle holds exactly one property — USE_BINARY_SERIALIZATION,
appSuffix `binary.serialization.enabled`, globalKey
`default.evcache.binary.serialization.enabled`. The Key enum is the
extension point: new transcoder properties land here and inherit the same
chain without touching the call sites.

EVCacheTranscoder gains a (int, int, EVCacheTranscoderProperties) ctor that
stores the bundle and consults `properties.isBinarySerializationEnabled()`
inside `serialize()`. The old (int, int, boolean) ctor is gone; the 2-arg
(int, int) ctor constructs a properties object with appName=null so the
no-app callers naturally fall through to the global key.

EVCacheImpl drops the inline read and the ENVELOPE_COMPRESSION_DISABLED
constant, building the bundle once per app.

NOTE on property key naming: the global key changed from
`evcache.envelope.binary.serialization.enabled` to
`default.evcache.binary.serialization.enabled`, matching the
`default.evcache.…` convention used by the other transcoder properties.
The per-app suffix is `binary.serialization.enabled` (no `envelope.`
prefix). Any caller who set the old global key will need to migrate.

Future fields that need runtime-dynamic reads can call the bundle's
`getProperty(Key, Class, default)` method instead of caching at
construction. PR #197's setCompressionAlgorithmProperty /
setCompressionLevelProperty setters on EVCacheSerializingTranscoder are
orthogonal and untouched here.

Tests: 5 new tests in EVCacheTranscoderPropertiesTest cover per-app
override, global fallback, static default, per-app-beats-global precedence,
and null-appName routing to the global key.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
joegoogle123 pushed a commit that referenced this pull request Jun 30, 2026
…nscoderProperties

Today EVCacheImpl reads the binary-serialization FastProperty inline:

```java
final boolean useBinarySerialization = propertyRepository.get(_appName + ".envelope.binary.serialization.enabled", Boolean.class)
        .orElseGet("evcache.envelope.binary.serialization.enabled").orElse(false).get();
this.evcacheValueTranscoder = new EVCacheTranscoder(maxValueSize, ENVELOPE_COMPRESSION_DISABLED, useBinarySerialization);
```

This change introduces an EVCacheTranscoderProperties bundle that owns the
read and exposes the result behind a typed accessor. Each property follows a
uniform three-level resolution chain:

  1. <appName>.<appSuffix>   (per-app override)
  2. <globalKey>             (fleet-wide global)
  3. compiled-in static default

Today the bundle holds exactly one property — USE_BINARY_SERIALIZATION,
appSuffix `binary.serialization.enabled`, globalKey
`default.evcache.binary.serialization.enabled`. The Key enum is the
extension point: new transcoder properties land here and inherit the same
chain without touching the call sites.

EVCacheTranscoder gains a (int, int, EVCacheTranscoderProperties) ctor that
stores the bundle and consults `properties.isBinarySerializationEnabled()`
inside `serialize()`. The old (int, int, boolean) ctor is gone; the 2-arg
(int, int) ctor constructs a properties object with appName=null so the
no-app callers naturally fall through to the global key.

EVCacheImpl drops the inline read and the ENVELOPE_COMPRESSION_DISABLED
constant, building the bundle once per app.

NOTE on property key naming: the global key changed from
`evcache.envelope.binary.serialization.enabled` to
`default.evcache.binary.serialization.enabled`, matching the
`default.evcache.…` convention used by the other transcoder properties.
The per-app suffix is `binary.serialization.enabled` (no `envelope.`
prefix). Any caller who set the old global key will need to migrate.

Future fields that need runtime-dynamic reads can call the bundle's
`getProperty(Key, Class, default)` method instead of caching at
construction. PR #197's setCompressionAlgorithmProperty /
setCompressionLevelProperty setters on EVCacheSerializingTranscoder are
orthogonal and untouched here.

Tests: 5 new tests in EVCacheTranscoderPropertiesTest cover per-app
override, global fallback, static default, per-app-beats-global precedence,
and null-appName routing to the global key.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
joegoogle123 pushed a commit that referenced this pull request Jun 30, 2026
…nscoderProperties

Today EVCacheImpl reads the binary-serialization FastProperty inline:

```java
final boolean useBinarySerialization = propertyRepository.get(_appName + ".envelope.binary.serialization.enabled", Boolean.class)
        .orElseGet("evcache.envelope.binary.serialization.enabled").orElse(false).get();
this.evcacheValueTranscoder = new EVCacheTranscoder(maxValueSize, ENVELOPE_COMPRESSION_DISABLED, useBinarySerialization);
```

This change introduces an EVCacheTranscoderProperties bundle that owns the
read and exposes the result behind a typed accessor. Each property follows a
uniform three-level resolution chain:

  1. <appName>.<appSuffix>   (per-app override)
  2. <globalKey>             (fleet-wide global)
  3. compiled-in static default

Today the bundle holds exactly one property — USE_BINARY_SERIALIZATION,
appSuffix `binary.serialization.enabled`, globalKey
`default.evcache.binary.serialization.enabled`. The Key enum is the
extension point: new transcoder properties land here and inherit the same
chain without touching the call sites.

EVCacheTranscoder gains a (int, int, EVCacheTranscoderProperties) ctor that
stores the bundle and consults `properties.isBinarySerializationEnabled()`
inside `serialize()`. The old (int, int, boolean) ctor is gone; the 2-arg
(int, int) ctor constructs a properties object with appName=null so the
no-app callers naturally fall through to the global key.

EVCacheImpl drops the inline read and the ENVELOPE_COMPRESSION_DISABLED
constant, building the bundle once per app.

NOTE on property key naming: the global key changed from
`evcache.envelope.binary.serialization.enabled` to
`default.evcache.binary.serialization.enabled`, matching the
`default.evcache.…` convention used by the other transcoder properties.
The per-app suffix is `binary.serialization.enabled` (no `envelope.`
prefix). Any caller who set the old global key will need to migrate.

Future fields that need runtime-dynamic reads can call the bundle's
`getProperty(Key, Class, default)` method instead of caching at
construction. PR #197's setCompressionAlgorithmProperty /
setCompressionLevelProperty setters on EVCacheSerializingTranscoder are
orthogonal and untouched here.

Tests: 5 new tests in EVCacheTranscoderPropertiesTest cover per-app
override, global fallback, static default, per-app-beats-global precedence,
and null-appName routing to the global key.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
joegoogle123 pushed a commit that referenced this pull request Jul 1, 2026
…nscoderProperties

Today EVCacheImpl reads the binary-serialization FastProperty inline:

```java
final boolean useBinarySerialization = propertyRepository.get(_appName + ".envelope.binary.serialization.enabled", Boolean.class)
        .orElseGet("evcache.envelope.binary.serialization.enabled").orElse(false).get();
this.evcacheValueTranscoder = new EVCacheTranscoder(maxValueSize, ENVELOPE_COMPRESSION_DISABLED, useBinarySerialization);
```

This change introduces an EVCacheTranscoderProperties bundle that owns the
read and exposes the result behind a typed accessor. Each property follows a
uniform three-level resolution chain:

  1. <appName>.<appSuffix>   (per-app override)
  2. <globalKey>             (fleet-wide global)
  3. compiled-in static default

Today the bundle holds exactly one property — USE_BINARY_SERIALIZATION,
appSuffix `binary.serialization.enabled`, globalKey
`default.evcache.binary.serialization.enabled`. The Key enum is the
extension point: new transcoder properties land here and inherit the same
chain without touching the call sites.

EVCacheTranscoder gains a (int, int, EVCacheTranscoderProperties) ctor that
stores the bundle and consults `properties.isBinarySerializationEnabled()`
inside `serialize()`. The old (int, int, boolean) ctor is gone; the 2-arg
(int, int) ctor constructs a properties object with appName=null so the
no-app callers naturally fall through to the global key.

EVCacheImpl drops the inline read and the ENVELOPE_COMPRESSION_DISABLED
constant, building the bundle once per app.

NOTE on property key naming: the global key changed from
`evcache.envelope.binary.serialization.enabled` to
`default.evcache.binary.serialization.enabled`, matching the
`default.evcache.…` convention used by the other transcoder properties.
The per-app suffix is `binary.serialization.enabled` (no `envelope.`
prefix). Any caller who set the old global key will need to migrate.

Future fields that need runtime-dynamic reads can call the bundle's
`getProperty(Key, Class, default)` method instead of caching at
construction. PR #197's setCompressionAlgorithmProperty /
setCompressionLevelProperty setters on EVCacheSerializingTranscoder are
orthogonal and untouched here.

Tests: 5 new tests in EVCacheTranscoderPropertiesTest cover per-app
override, global fallback, static default, per-app-beats-global precedence,
and null-appName routing to the global key.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Jane Wang and others added 2 commits July 2, 2026 15:55
Move compression algorithm/level resolution into EVCacheTranscoderProperties
so the transcoder holds live Property handles resolved through the standard
per-app -> global -> static-default chain (keys: compression.algorithm,
compression.zstd.level). Simplifies EVCacheSerializingTranscoder/EVCacheTranscoder
plumbing and removes the ad-hoc getProperty overloads and setters.

Harden algorithm parsing: an unrecognized FP value (e.g. a typo) previously
resolved to null and NPE'd the compression switch at encode time. It now
warn-logs and falls back to DEFAULT_COMPRESSION_ALGORITHM (GZIP), so a bad
fast-property degrades instead of taking down writes.

Tests: rewrite EVCacheSerializingTranscoderTest and EVCacheTranscoderPropertiesTest
against the new property-bundle API; add coverage for three-level resolution of
the two new keys, case-insensitivity, live FP updates, the compression-grew-data
branch, and the unrecognized-algorithm fallback. Remove redundant duplicate tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@srrangarajan

Copy link
Copy Markdown
Contributor

LGTM

@srrangarajan srrangarajan self-requested a review July 6, 2026 18:31

@shy-1234 shy-1234 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Please also get a +1 from @joegoogle123 and I think we are good to go.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants