Skip to content

fix(metadata): fix ArrayIndexOutOfBoundsException when 1.x reader merges old MDT records - #19560

Open
lokeshj1703 wants to merge 1 commit into
apache:masterfrom
lokeshj1703:fix/mdt-schema-aioobe-old-reader
Open

fix(metadata): fix ArrayIndexOutOfBoundsException when 1.x reader merges old MDT records#19560
lokeshj1703 wants to merge 1 commit into
apache:masterfrom
lokeshj1703:fix/mdt-schema-aioobe-old-reader

Conversation

@lokeshj1703

Copy link
Copy Markdown
Collaborator

Describe the issue this Pull Request addresses

Closes #19559

When a Hudi 1.x reader reads a Metadata Table (MDT) that was written by an older Hudi release, merging log-file delta records against base-file records throws an ArrayIndexOutOfBoundsException:

Caused by: java.lang.ArrayIndexOutOfBoundsException: 6
    at org.apache.avro.generic.GenericData$Record.put(GenericData.java:273)
    at org.apache.hudi.metadata.HoodieMetadataPayload.getInsertValue(HoodieMetadataPayload.java:422)
    at org.apache.hudi.metadata.HoodieMetadataPayload.combineAndGetUpdateValue(HoodieMetadataPayload.java:399)
    at org.apache.hudi.common.model.HoodieAvroRecordMerger.merge(HoodieAvroRecordMerger.java:67)
    at org.apache.hudi.common.table.read.BufferedRecordMergerFactory$CustomPayloadRecordMerger.deltaMergeRecords(BufferedRecordMergerFactory.java:404)
    at org.apache.hudi.common.table.read.BufferedRecordMergerFactory$BaseCustomMerger.deltaMerge(BufferedRecordMergerFactory.java:439)
    at org.apache.hudi.common.table.read.buffer.KeyBasedFileGroupRecordBuffer.processNextDataRecord(...)

Summary and Changelog

Root cause: SecondaryIndexMetadata was added to HoodieMetadataRecord in a later Hudi release, growing the schema from 6 to 7 fields. MDT log blocks written by older releases carry the 6-field writer schema in their block header. When merging, HoodieAvroRecordMerger retrieves the writer schema from the buffered record and passes it to HoodieMetadataPayload.getInsertValue(schema).

Inside getInsertValue, the fast path uses an object-identity check:

if (schema == null || schema == HOODIE_METADATA_AVRO_SCHEMA) {
    // safe path
} else {
    // assumes HOODIE_META_COLUMNS are prepended; uses TYPE_FIELD_OFFSET=6
    record.put(TYPE_FIELD_OFFSET, type);  // index 6 on a 6-field schema → ArrayIndexOutOfBoundsException
}

The old 6-field schema is a different Schema object from HOODIE_METADATA_AVRO_SCHEMA (7 fields), so it fails the identity check and falls into the else branch designed for schemas with HOODIE_META_COLUMNS prepended. Writing to index 6 on a 6-field schema throws ArrayIndexOutOfBoundsException.

Fix: Add isHoodieMetadataRecordSchema(Schema) that recognises any version of HoodieMetadataRecord by Avro record name and namespace. Route these older-version schemas to the same fast path as the current schema, returning a HoodieMetadataRecord with the current schema (SecondaryIndexMetadata defaults to null).

Changes:

  • HoodieMetadataPayload.java: add isHoodieMetadataRecordSchema() helper; extend the fast-path guard in getInsertValue()
  • TestHoodieMetadataPayload.java: add testGetInsertValueWithOldMetadataSchema regression test that constructs the old 6-field schema and verifies no exception is thrown

Impact

No user-facing API or config change. Tables written by older Hudi releases can be read safely by a 1.x reader without requiring a table upgrade.

Risk Level

Low. The change is confined to getInsertValue() in HoodieMetadataPayload. The added condition only triggers when the schema is a HoodieMetadataRecord schema that is not object-identical to HOODIE_METADATA_AVRO_SCHEMA (i.e. an older-version schema read from a log block); all other callers are unaffected.

Documentation Update

None.

Contributor's checklist

  • Read through contributor's guide
  • Enough context is provided in the sections above
  • Adequate tests were added if applicable

…ges old MDT records

When a 1.x reader reads MDT data written by an older Hudi release, log blocks
carry the old HoodieMetadataRecord writer schema (6 fields, no SecondaryIndexMetadata).
HoodieMetadataPayload.getInsertValue() uses an object-identity check against the
current HOODIE_METADATA_AVRO_SCHEMA (7 fields). The old schema fails this check and
falls into the else-branch, which assumes HOODIE_META_COLUMNS are prepended and uses
TYPE_FIELD_OFFSET=6 — causing ArrayIndexOutOfBoundsException on a 6-field schema.

Fix: add isHoodieMetadataRecordSchema() that recognises any version of
HoodieMetadataRecord by Avro name + namespace, routing it to the fast path.
Old schemas produce a current-schema record with SecondaryIndexMetadata=null.

Closes apache#19559

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@hudi-agent hudi-agent 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.

⚠️ 🤖 This review was generated by an AI agent and may contain mistakes. Please verify any suggestions before applying.

Thanks for working on this! The PR fixes an ArrayIndexOutOfBoundsException when a 1.x reader merges MDT records written by an older Hudi release with a narrower HoodieMetadataRecord schema, by broadening the fast-path check in getInsertValue to also accept any HoodieMetadataRecord-named schema. The core fix looks reasonable and the returned record's schema is self-consistent with what the merger propagates downstream. One edge case around the breadth of the schema match is worth double-checking in the inline comment. Please take a look, and this should be ready for a Hudi committer or PMC member to take it from here. Code looks clean overall — one minor naming nit below.

// Uses cached Avro schema reference for O(1) equality check.
if (schema == null || schema == HOODIE_METADATA_AVRO_SCHEMA) {
// If the schema is same or none is provided, we can return the record directly
if (schema == null || schema == HOODIE_METADATA_AVRO_SCHEMA || isHoodieMetadataRecordSchema(schema)) {

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.

🤖 isHoodieMetadataRecordSchema only checks the record name/namespace, so it matches any schema named HoodieMetadataRecord — including one with the 5 Hudi meta columns prepended (addMetadataFields preserves the name) or a projected subset. Those cases previously fell into the else-branch. Now they take the fast path and get back the full 7-field record regardless of the requested layout. Is that safe for every caller here (e.g. a populateMetaFields=true MDT or a projected read), or could it hand back a record that doesn't match the schema the caller asked for?

⚠️ AI-generated; verify before applying. React 👍/👎 to flag quality.

return MetadataPartitionType.get(type).combineMetadataPayloads(previousRecord, this);
}

private static boolean isHoodieMetadataRecordSchema(Schema schema) {

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.

🤖 nit: isHoodieMetadataRecordSchema describes the structural check but not the intent — could you rename it to something like isCompatibleMetadataRecordSchema or isOlderVersionMetadataRecordSchema to signal that this exists specifically to handle older schemas missing newer fields?

⚠️ AI-generated; verify before applying. React 👍/👎 to flag quality.

@github-actions github-actions Bot added the size:S PR with lines of changes in (10, 100] label Aug 7, 2026
@hudi-bot

hudi-bot commented Aug 7, 2026

Copy link
Copy Markdown
Collaborator

CI report:

Bot commands @hudi-bot supports the following commands:
  • @hudi-bot run azure re-run the last Azure build

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

Labels

size:S PR with lines of changes in (10, 100]

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(metadata): ArrayIndexOutOfBoundsException when 1.x reader merges MDT records written by older Hudi versions

3 participants