Skip to content

Add @JsonIgnoreProperties(ignoreUnknown=true) to all shared-store DTOs for forward compatibility#3218

Merged
SophieGuo410 merged 3 commits intomasterfrom
Branch_resolve_all_incompatible_issue
Apr 9, 2026
Merged

Add @JsonIgnoreProperties(ignoreUnknown=true) to all shared-store DTOs for forward compatibility#3218
SophieGuo410 merged 3 commits intomasterfrom
Branch_resolve_all_incompatible_issue

Conversation

@SophieGuo410
Copy link
Copy Markdown
Contributor

Summary

Without this annotation, adding a new field to any of these classes causes UnrecognizedPropertyException on older nodes during rolling deploys. This affects Account/Container metadata (MySQL), storage stats (MySQL), Helix maintenance records (ZK), and compaction policy info (local store).

Classes fixed:

  • MigrationConfig (outer class; inner classes already had it)
  • RampControl
  • DatasetBuilder
  • HostAccountStorageStatsWrapper
  • HostPartitionClassStorageStatsWrapper
  • StatsHeader
  • HostAccountStorageStats
  • HostPartitionClassStorageStats
  • HelixParticipant.MaintenanceRecord
  • CompactionPolicySwitchInfo

Testing Done

…s for forward compatibility

Without this annotation, adding a new field to any of these classes causes
UnrecognizedPropertyException on older nodes during rolling deploys. This
affects Account/Container metadata (MySQL), storage stats (MySQL), Helix
maintenance records (ZK), and compaction policy info (local store).

Classes fixed:
- MigrationConfig (outer class; inner classes already had it)
- RampControl
- DatasetBuilder
- HostAccountStorageStatsWrapper
- HostPartitionClassStorageStatsWrapper
- StatsHeader
- HostAccountStorageStats
- HostPartitionClassStorageStats
- HelixParticipant.MaintenanceRecord
- CompactionPolicySwitchInfo

Each class includes forward compatibility tests (unknown fields ignored)
and backward compatibility tests (missing optional fields use defaults).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@SophieGuo410 SophieGuo410 requested a review from crliao March 28, 2026 21:34
Copy link
Copy Markdown
Contributor

@crliao crliao left a comment

Choose a reason for hiding this comment

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

Comment on lines +197 to +202
public void testHostPartitionClassStorageStatsWrapperForwardCompatibility() throws Exception {
com.fasterxml.jackson.annotation.JsonIgnoreProperties annotation =
HostPartitionClassStorageStatsWrapper.class.getAnnotation(
com.fasterxml.jackson.annotation.JsonIgnoreProperties.class);
Assert.assertNotNull("HostPartitionClassStorageStatsWrapper must have @JsonIgnoreProperties", annotation);
Assert.assertTrue("ignoreUnknown must be true", annotation.ignoreUnknown());
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.

this test doesn't actually deserialize any JSON

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Fixed — made HostPartitionClassStorageStatsWrapper Jackson-deserializable by adding a default constructor, @JsonAutoDetect, and making fields non-final (consistent with HostAccountStorageStatsWrapper). Test now
deserializes JSON with "someNewField":"futureValue" and verifies it's ignored.

*/
@Test
public void testHostAccountStorageStatsForwardCompatibility() throws Exception {
// Build a valid HostAccountStorageStats, serialize, inject unknown field, deserialize
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.

no unknown field injected , should inject a field with wrong type to prove @JsonIgnoreProperties protects @JsonAnySetter

String json = "{\"1\":{\"1\":{\"1\":{\"logicalStorageUsage\":100,\"physicalStorageUsage\":200,\"numberOfBlobs\":5}}},\"someNewField\":\"futureValue\"}";
HostAccountStorageStats deserialized = objectMapper.readValue(json, HostAccountStorageStats.class);     

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@JsonAnySetter takes precedence over @JsonIgnoreProperties in Jackson. When Jackson encounters "someNewField":"futureValue", it routes to @JsonAnySetter and tries to deserialize "futureValue" as Map<Short, Map<Short, ContainerStorageStats>>, which fails before the method body even executes.
HostAccountStorageStats by design is a flat map where all top-level keys are partition IDs. Forward compatibility for non-partition metadata fields is handled at the wrapper level —HostAccountStorageStatsWrapper has @JsonIgnoreProperties(ignoreUnknown=true) and its test already verifies unknown fields are ignored.

* Forward compatibility: HostPartitionClassStorageStats round-trip with valid data.
*/
@Test
public void testHostPartitionClassStorageStatsForwardCompatibility() throws Exception {
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.

same here , no unknown field injected

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.

This matters specifically because both classes use @JsonAnySetter — without @JsonIgnoreProperties, an unknown field like "someNewField": "futureValue" would reach @JsonAnySetter and fail trying to deserialize "futureValue" as a nested Map.

The annotation intercepts it before that happens, but the current tests don't exercise that path.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

HostPartitionClassStorageStats does not use @JsonAnySetter, so @JsonIgnoreProperties(ignoreUnknown=true) works here. Test now injects "someNewField":"futureValue" and verifies it's silently ignored during deserialization.

Sophie Guo and others added 2 commits March 30, 2026 13:01
Addresses review comment: CompactionPolicyCounter uses @JsonAutoDetect
and is vulnerable to the same forward compatibility issue as
CompactionPolicySwitchInfo. Added annotation and compatibility tests.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Remove unused ArrayList import
- Make HostPartitionClassStorageStatsWrapper Jackson-deserializable (add
  default constructor, @JsonAutoDetect, non-final fields) consistent with
  HostAccountStorageStatsWrapper
- Replace reflection-only test with actual JSON deserialization for
  HostPartitionClassStorageStatsWrapper
- Inject unknown fields in HostPartitionClassStorageStats test to exercise
  @JsonIgnoreProperties
- Inject extra partition entry in HostAccountStorageStats test to verify
  forward compatibility via @JsonAnySetter

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@SophieGuo410 SophieGuo410 requested a review from crliao April 6, 2026 06:35
@SophieGuo410 SophieGuo410 merged commit 5281507 into master Apr 9, 2026
5 checks passed
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.

2 participants