Skip to content

Created new metadata lib and added AliasMetadataModel and MappingMetadataModel classes#20328

Open
gargmanik13 wants to merge 1 commit intoopensearch-project:mainfrom
gargmanik13:md_lib_stateless_cm
Open

Created new metadata lib and added AliasMetadataModel and MappingMetadataModel classes#20328
gargmanik13 wants to merge 1 commit intoopensearch-project:mainfrom
gargmanik13:md_lib_stateless_cm

Conversation

@gargmanik13
Copy link
Copy Markdown
Contributor

Description

  • Created a new library metadata under libs folder in the OpenSearch repo.
  • This will help to separate out the metadata related dependencies which are currently coupled with cluster management dependencies in the server package. It'll make the Cluster Manager more cloud native provider friendly and help to eventually run the CM in stateless mode.
  • As part of this change, added model classes for AliasMetadata and MappingMetadata which can be used independently and with existing OpenSearch dependencies to store relevant metadata.
  • This PR will be followed by IndexMetadataModel class

Related Issues

Resolves #13197

Check List

  • Functionality includes testing.
  • API changes companion pull request created, if applicable.
  • Public documentation issue/PR created, if applicable.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Dec 28, 2025

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR introduces a new metadata library (libs/metadata) containing core data model classes (CompressedData, AliasMetadataModel, MappingMetadataModel) and refactors existing cluster metadata classes (AliasMetadata, MappingMetadata, CompressedXContent) to delegate internal storage and serialization to these new models.

Changes

Cohort / File(s) Summary
New metadata library infrastructure
libs/metadata/build.gradle, CHANGELOG.md, server/build.gradle
Added Gradle build configuration for metadata library with opensearch.publish plugin and API dependencies on opensearch-common and opensearch-core. Server build now depends on new metadata library.
Core metadata model classes
libs/metadata/src/main/java/org/opensearch/metadata/compress/CompressedData.java, libs/metadata/src/main/java/org/opensearch/metadata/index/model/AliasMetadataModel.java, libs/metadata/src/main/java/org/opensearch/metadata/index/model/MappingMetadataModel.java
Introduced three new public final classes implementing Writeable: CompressedData (holds compressed bytes and checksum), AliasMetadataModel (encapsulates alias metadata with Builder pattern, copy constructor, and search routing parsing), MappingMetadataModel (stores type, source, and routing flag). All annotated @ExperimentalApi.
Package metadata
libs/metadata/src/main/java/org/opensearch/metadata/compress/package-info.java, libs/metadata/src/main/java/org/opensearch/metadata/index/model/package-info.java
Added package-level documentation and Apache-2.0 SPDX headers for new packages.
Tests for new models
libs/metadata/src/test/java/org/opensearch/metadata/compress/CompressedDataTests.java, libs/metadata/src/test/java/org/opensearch/metadata/index/model/AliasMetadataModelTests.java, libs/metadata/src/test/java/org/opensearch/metadata/index/model/MappingMetadataModelTests.java
Added comprehensive unit tests covering serialization/deserialization round-trips, equality semantics, hashCode behavior, and randomized stress tests for new model classes.
Refactored server metadata classes
server/src/main/java/org/opensearch/cluster/metadata/AliasMetadata.java
Replaced direct field storage (alias, filter, routing, etc.) with delegation to AliasMetadataModel. Updated constructors, accessors, equality, serialization, and Builder to route through model. Added static factory methods newAliasMetadataBuilder() and newAliasMetadata().
Refactored server metadata classes
server/src/main/java/org/opensearch/cluster/metadata/MappingMetadata.java
Replaced separate fields (type, source, routingRequired) with single MappingMetadataModel field. Updated all constructors, accessors, serialization, and equality/hashCode to delegate to model.
Refactored server compression class
server/src/main/java/org/opensearch/common/compress/CompressedXContent.java
Replaced dual field storage (bytes, crc32) with CompressedData field. Added new constructor accepting CompressedData, public accessor compressedData(), and updated all serialization, deserialization, and equality logic to use model.
Integration tests
server/src/test/java/org/opensearch/cluster/metadata/AliasMetadataTests.java
Added testModelDeserialization and testModelDeserializationWithNullValues to verify AliasMetadata and AliasMetadataModel interoperability and field matching.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 36.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately summarizes the main change: creating a new metadata library and introducing AliasMetadataModel and MappingMetadataModel classes, which aligns with the file changes.
Description check ✅ Passed The PR description follows the template with clear sections on what the change achieves, related issues, and a completed checklist. It explains the motivation and goals adequately.
Linked Issues check ✅ Passed The PR successfully implements the requirements from issue #13197: creating a separate metadata library to decouple metadata management from cluster state, with AliasMetadataModel and MappingMetadataModel as the initial model classes.
Out of Scope Changes check ✅ Passed All changes are directly aligned with the PR objectives: new metadata library creation, model classes for alias and mapping metadata, build configuration, tests, and integration with existing server components. No unrelated changes detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added Cluster Manager enhancement Enhancement or improvement to existing feature or request Roadmap:Modular Architecture Project-wide roadmap label labels Dec 28, 2025
@github-actions
Copy link
Copy Markdown
Contributor

❌ Gradle check result for aa3ee56: FAILURE

Please examine the workflow log, locate, and copy-paste the failure(s) below, then iterate to green. Is the failure a flaky test unrelated to your change?

@github-actions
Copy link
Copy Markdown
Contributor

❌ Gradle check result for 47c27ce: FAILURE

Please examine the workflow log, locate, and copy-paste the failure(s) below, then iterate to green. Is the failure a flaky test unrelated to your change?

@github-actions
Copy link
Copy Markdown
Contributor

❌ Gradle check result for 1bc5838: FAILURE

Please examine the workflow log, locate, and copy-paste the failure(s) below, then iterate to green. Is the failure a flaky test unrelated to your change?

@github-actions
Copy link
Copy Markdown
Contributor

❌ Gradle check result for 5318259: FAILURE

Please examine the workflow log, locate, and copy-paste the failure(s) below, then iterate to green. Is the failure a flaky test unrelated to your change?

@gargmanik13
Copy link
Copy Markdown
Contributor Author

@github-actions
Copy link
Copy Markdown
Contributor

❌ Gradle check result for 31df1c5: FAILURE

Please examine the workflow log, locate, and copy-paste the failure(s) below, then iterate to green. Is the failure a flaky test unrelated to your change?

@github-actions
Copy link
Copy Markdown
Contributor

✅ Gradle check result for 313a39a: SUCCESS

@codecov
Copy link
Copy Markdown

codecov bot commented Jan 19, 2026

Codecov Report

❌ Patch coverage is 92.28487% with 26 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.27%. Comparing base (3aed19d) to head (df986e0).

Files with missing lines Patch % Lines
...earch/metadata/index/model/AliasMetadataModel.java 90.27% 4 Missing and 10 partials ⚠️
...rch/metadata/index/model/MappingMetadataModel.java 89.39% 2 Missing and 5 partials ⚠️
...g/opensearch/metadata/compress/CompressedData.java 95.23% 2 Missing ⚠️
...opensearch/common/compress/CompressedXContent.java 90.00% 0 Missing and 2 partials ⚠️
...org/opensearch/cluster/metadata/AliasMetadata.java 97.36% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##               main   #20328      +/-   ##
============================================
- Coverage     73.30%   73.27%   -0.03%     
- Complexity    71965    72033      +68     
============================================
  Files          5781     5785       +4     
  Lines        329200   329363     +163     
  Branches      47491    47492       +1     
============================================
+ Hits         241314   241348      +34     
- Misses        68491    68683     +192     
+ Partials      19395    19332      -63     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@gargmanik13 gargmanik13 marked this pull request as ready for review January 19, 2026 12:58
@gargmanik13 gargmanik13 requested a review from a team as a code owner January 19, 2026 12:58
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Fix all issues with AI agents
In
`@libs/metadata/src/main/java/org/opensearch/metadata/compress/CompressedData.java`:
- Around line 37-75: The class exposes its internal byte[] allowing external
mutation; update the two constructors (CompressedData(byte[] compressedBytes,
int checksum) and CompressedData(StreamInput in)) to null-guard and store a
defensive copy (e.g., Arrays.copyOf) of the incoming byte[]; update
compressedBytes() to return a fresh copy instead of the internal array; ensure
writeTo uses the internal array (already safe) but if you choose to accept null
inputs, normalize to an empty byte[] on construction so writeTo and checksum
logic never see null. Use Arrays.copyOf to perform copies.

In
`@libs/metadata/src/main/java/org/opensearch/metadata/index/model/MappingMetadataModel.java`:
- Around line 40-44: The constructor MappingMetadataModel(String type,
CompressedData source, boolean routingRequired) allows a null source but
writeTo(OutputStream/StreamOutput) calls source.writeTo(out), causing an NPE;
either validate and reject null in the constructor (throw
NPE/IllegalArgumentException) or treat null as a valid state and update writeTo
to handle a null source (e.g., write a boolean flag then write source only if
present) and mirror the change in the deserialization constructor/readFrom so
reading and writing remain consistent; update the relevant symbols:
MappingMetadataModel constructor, writeTo(...), and the deserialization
constructor/readFrom to keep serialization symmetric.

In `@server/src/main/java/org/opensearch/cluster/metadata/AliasMetadata.java`:
- Around line 160-166: The new AliasMetadataModel.writeTo()/constructor changed
the wire format for indexRouting/searchRouting by switching to
writeOptionalString()/readOptionalString(), breaking compatibility with older
nodes that expect a boolean marker + conditional writeString/readString; restore
the original serialization pattern (in AliasMetadataModel.writeTo and
AliasMetadataModel(StreamInput)) by writing a boolean marker before each routing
string and conditionally writing/reading the string, or implement explicit
version-gated handling in AliasMetadata.writeTo and AliasMetadata(StreamInput)
that emits/consumes the old boolean+string format for older stream versions and
the new optional-string format for newer versions, ensuring indexRouting and
searchRouting are encoded exactly as the old nodes expect when the
StreamInput/StreamOutput version indicates older clusters.

In `@server/src/main/java/org/opensearch/cluster/metadata/MappingMetadata.java`:
- Around line 162-165: Add explicit cross-version serialization guards and/or
rolling-upgrade tests: update MappingMetadata.writeTo (and the corresponding
deserialization constructor that reads type, source checksum+bytes, and
routingRequired) to include version checks using the StreamOutput/StreamInput
version APIs and branch serialization accordingly, and add unit/integration
tests that simulate older cluster node versions to validate compatibility during
rolling upgrades; ensure model.writeTo remains unchanged except behind the same
version guard so the read/write order stays compatible when communicating with
older versions.
🧹 Nitpick comments (6)
libs/metadata/build.gradle (1)

20-24: Avoid blanket disabling of audit tasks if possible.

Consider re-enabling these checks and adding targeted exclusions instead, or document the rationale to keep compliance posture clear.

libs/metadata/src/test/java/org/opensearch/metadata/compress/CompressedDataTests.java (1)

27-32: Consider renaming the helper method to avoid shadowing.

The private assertEquals method shadows the inherited assertEquals from the test framework. While it works correctly here, this could cause confusion when reading the code or if someone adds assertions expecting JUnit's behavior.

Suggested rename
-    private void assertEquals(CompressedData d1, CompressedData d2) {
+    private void assertCompressedDataEquals(CompressedData d1, CompressedData d2) {
         assertThat(d1, equalTo(d2));
         assertArrayEquals(d1.compressedBytes(), d2.compressedBytes());
         assertThat(d1.checksum(), equalTo(d2.checksum()));
         assertThat(d1.hashCode(), equalTo(d2.hashCode()));
     }

Then update call sites at lines 46, 58, 86, and 105 accordingly.

libs/metadata/src/main/java/org/opensearch/metadata/index/model/AliasMetadataModel.java (2)

54-69: Consider adding null validation for the alias field.

The alias field is required (non-nullable per the Javadoc at line 118 stating "never null"), but the constructor doesn't validate this. A null alias could cause issues downstream.

Suggested fix
     public AliasMetadataModel(
         String alias,
         CompressedData filter,
         String indexRouting,
         String searchRouting,
         `@Nullable` Boolean writeIndex,
         `@Nullable` Boolean isHidden
     ) {
+        if (alias == null) {
+            throw new IllegalArgumentException("alias must not be null");
+        }
         this.alias = alias;
         this.filter = filter;

265-267: Add null validation in Builder constructor.

Consistent with the main constructor, the Builder should validate that alias is not null.

Suggested fix
         public Builder(String alias) {
+            if (alias == null) {
+                throw new IllegalArgumentException("alias must not be null");
+            }
             this.alias = alias;
         }
libs/metadata/src/test/java/org/opensearch/metadata/index/model/MappingMetadataModelTests.java (1)

24-61: LGTM, but consider adding inequality tests.

The serialization and equality tests are good. For completeness with CompressedDataTests, consider adding tests for:

  • testNotEquals with different type, source, or routingRequired
  • testHashCode consistency
server/src/main/java/org/opensearch/cluster/metadata/MappingMetadata.java (1)

135-137: Consider caching the CompressedXContent instance.

The source() method creates a new CompressedXContent wrapper on each call. If this method is called frequently, consider caching the result lazily.

Suggested optimization
+    private volatile CompressedXContent sourceCache;
+
     public CompressedXContent source() {
-        return new CompressedXContent(model.source());
+        CompressedXContent result = sourceCache;
+        if (result == null) {
+            result = new CompressedXContent(model.source());
+            sourceCache = result;
+        }
+        return result;
     }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4ac5bab and 313a39a.

📒 Files selected for processing (15)
  • CHANGELOG.md
  • libs/metadata/build.gradle
  • libs/metadata/src/main/java/org/opensearch/metadata/compress/CompressedData.java
  • libs/metadata/src/main/java/org/opensearch/metadata/compress/package-info.java
  • libs/metadata/src/main/java/org/opensearch/metadata/index/model/AliasMetadataModel.java
  • libs/metadata/src/main/java/org/opensearch/metadata/index/model/MappingMetadataModel.java
  • libs/metadata/src/main/java/org/opensearch/metadata/index/model/package-info.java
  • libs/metadata/src/test/java/org/opensearch/metadata/compress/CompressedDataTests.java
  • libs/metadata/src/test/java/org/opensearch/metadata/index/model/AliasMetadataModelTests.java
  • libs/metadata/src/test/java/org/opensearch/metadata/index/model/MappingMetadataModelTests.java
  • server/build.gradle
  • server/src/main/java/org/opensearch/cluster/metadata/AliasMetadata.java
  • server/src/main/java/org/opensearch/cluster/metadata/MappingMetadata.java
  • server/src/main/java/org/opensearch/common/compress/CompressedXContent.java
  • server/src/test/java/org/opensearch/cluster/metadata/AliasMetadataTests.java
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2026-01-02T19:23:29.698Z
Learnt from: karenyrx
Repo: opensearch-project/OpenSearch PR: 20335
File: modules/transport-grpc/src/main/java/org/opensearch/transport/grpc/proto/request/search/SearchSourceBuilderProtoUtils.java:155-172
Timestamp: 2026-01-02T19:23:29.698Z
Learning: The gRPC search API in OpenSearch is marked as "experimental" in official documentation, so changes to proto schemas that remove previously unsupported fields (those throwing UnsupportedOperationException) are not considered breaking changes.

Applied to files:

  • CHANGELOG.md
📚 Learning: 2026-01-13T17:40:27.167Z
Learnt from: reta
Repo: opensearch-project/OpenSearch PR: 20411
File: server/src/main/java/org/opensearch/index/codec/CodecService.java:112-133
Timestamp: 2026-01-13T17:40:27.167Z
Learning: Avoid capturing or evaluating a supplier (e.g., this::defaultCodec) upfront when passing it to a registry during object construction. If registries may replace defaults during iteration (as in EnginePlugin.getAdditionalCodecs), pass the supplier itself and only resolve it at use time. This ensures dynamic behavior is preserved during initialization and prevents premature binding of defaults in codecs/registry setup. This pattern should apply to similar initialization paths in Java server code where registries may mutate defaults during construction.

Applied to files:

  • server/src/main/java/org/opensearch/cluster/metadata/MappingMetadata.java
  • server/src/main/java/org/opensearch/common/compress/CompressedXContent.java
  • server/src/main/java/org/opensearch/cluster/metadata/AliasMetadata.java
🧬 Code graph analysis (2)
libs/metadata/src/test/java/org/opensearch/metadata/index/model/AliasMetadataModelTests.java (1)
libs/common/src/main/java/org/opensearch/common/util/set/Sets.java (1)
  • Sets (57-185)
libs/metadata/src/main/java/org/opensearch/metadata/index/model/AliasMetadataModel.java (3)
libs/metadata/src/main/java/org/opensearch/metadata/compress/CompressedData.java (1)
  • ExperimentalApi (25-90)
libs/metadata/src/main/java/org/opensearch/metadata/index/model/MappingMetadataModel.java (1)
  • ExperimentalApi (26-126)
libs/common/src/main/java/org/opensearch/common/util/set/Sets.java (1)
  • Sets (57-185)
🔇 Additional comments (16)
libs/metadata/src/main/java/org/opensearch/metadata/index/model/package-info.java (1)

1-10: LGTM — clear package-level documentation.

CHANGELOG.md (1)

16-16: Changelog entry looks good.

Clear and aligned with the PR scope.

libs/metadata/src/main/java/org/opensearch/metadata/compress/package-info.java (1)

1-10: LGTM.

Standard license header and package declaration.

server/build.gradle (1)

75-75: Dependency addition looks good.

Integrates the new metadata library as intended.

libs/metadata/src/test/java/org/opensearch/metadata/index/model/AliasMetadataModelTests.java (1)

27-36: The test expectations are correct and consistent with legacy behavior. Both AliasMetadata and AliasMetadataModel use Strings.splitStringByCommaToArray(), which internally calls Java's String.split(","). This method preserves whitespace—it splits only on commas. The test in AliasMetadataModelTests.java line 36 correctly expects {"trim", "tw ", " ltw ", " lw"}, matching the identical assertion in AliasMetadataTests.java line 60. Additionally, AliasMetadataTests.java lines 91–92 explicitly validate that model.searchRoutingValues() equals aliasMetadata.searchRoutingValues(), confirming the model aligns with legacy behavior. No divergence exists.

Likely an incorrect or invalid review comment.

libs/metadata/src/test/java/org/opensearch/metadata/compress/CompressedDataTests.java (1)

34-128: LGTM!

The test coverage is comprehensive, including serialization round-trips, equality/hashCode contracts, and randomized stress testing. The tests follow OpenSearch testing patterns well.

server/src/main/java/org/opensearch/common/compress/CompressedXContent.java (3)

79-88: LGTM on the refactoring to use CompressedData.

The field change from separate bytes/crc32 to a single CompressedData instance is clean. The new constructor properly calls assertConsistent() to validate state.


170-177: Verify the mutability implications of the new accessor.

The new compressedData() method exposes the internal CompressedData, and per CompressedData.compressedBytes() implementation, it returns the raw byte array without a defensive copy. This could allow callers to mutate the internal state.

Since CompressedXContent is @PublicApi(since = "1.0.0") and the class is final, consider whether the CompressedData should make a defensive copy in compressedBytes(), or document the expectation that callers must not mutate the returned array.


179-186: LGTM on serialization delegation.

The read/write paths properly delegate to CompressedData. The serialization format (checksum then bytes) is preserved through CompressedData's implementation.

libs/metadata/src/main/java/org/opensearch/metadata/index/model/AliasMetadataModel.java (2)

87-99: LGTM on serialization format.

The serialization order (alias, filter presence/data, indexRouting, searchRouting, writeIndex, isHidden) is consistent between read and write operations. The use of optional methods for nullable fields follows OpenSearch conventions.

Also applies to: 198-211


220-244: LGTM on equals/hashCode implementation.

Correctly excludes the derived searchRoutingValues field since it's computed from searchRouting. The implementation properly uses Objects.equals and Objects.hash for null-safe comparisons.

libs/metadata/src/main/java/org/opensearch/metadata/index/model/MappingMetadataModel.java (1)

52-56: LGTM on deserialization constructor.

The deserialization order matches writeTo(): type, source, routingRequired.

server/src/main/java/org/opensearch/cluster/metadata/MappingMetadata.java (2)

167-172: LGTM on writeVerifiableTo implementation.

This method intentionally doesn't delegate to the model because it needs to write through BufferedChecksumStreamOutput for verification purposes, including calling source().writeVerifiableTo(out) for the compressed content's checksum.


69-106: LGTM on constructor refactoring.

All constructors properly initialize the MappingMetadataModel with the same data that was previously stored directly. The routing extraction logic in isRoutingRequired remains unchanged.

server/src/test/java/org/opensearch/cluster/metadata/AliasMetadataTests.java (1)

40-40: Good coverage for model deserialization.

The new tests validate both populated and minimal AliasMetadata cases and should guard compatibility well.

Also applies to: 71-126

server/src/main/java/org/opensearch/cluster/metadata/AliasMetadata.java (1)

52-127: Refactor keeps accessor/builder behavior intact.

The delegation preserves the public accessors, builder flow, and XContent output semantics, including null handling for filter/routing/writeIndex/isHidden.

Also applies to: 151-157, 191-259, 268-285

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@gargmanik13 gargmanik13 force-pushed the md_lib_stateless_cm branch 2 times, most recently from 76c92c6 to faa4ee1 Compare January 20, 2026 12:47
@github-actions
Copy link
Copy Markdown
Contributor

✅ Gradle check result for faa4ee1: SUCCESS

…Model

Signed-off-by: Manik Garg <gargmanik1317@gmail.com>
@github-actions
Copy link
Copy Markdown
Contributor

✅ Gradle check result for df986e0: SUCCESS

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

Labels

Cluster Manager enhancement Enhancement or improvement to existing feature or request Roadmap:Modular Architecture Project-wide roadmap label

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

[RFC] Refactor Metadata management in Cluster Manager code as separate lib

2 participants