Skip to content

fix: use record schema for partition key selector in HashKeyGenerator - #17685

Open
waterWang wants to merge 3 commits into
apache:mainfrom
waterWang:fix/hash-key-generator-schema-cache-key-17651
Open

fix: use record schema for partition key selector in HashKeyGenerator#17685
waterWang wants to merge 3 commits into
apache:mainfrom
waterWang:fix/hash-key-generator-schema-cache-key-17651

Conversation

@waterWang

Copy link
Copy Markdown

Problem

When using Dynamic Sink with DistributionMode.HASH and records alternating between two writer schema variants (different field IDs), HashKeyGenerator's SelectorKey cache key uses only the table schema ID and nulls the record's schema. This causes the cache to return the same PartitionKeySelector for both variants, but the selector reads the wrong field position from the row data, producing ClassCastException.

Root Cause

Two issues in HashKeyGenerator.java:

  1. SelectorKey cache key: When tableSchemaId is provided, the record's actual schema field is nulled out (this.schema = tableSchemaId == null ? schema : null). Records with different dynamic schemas but the same table schema ID share the same cache key, returning a stale PartitionKeySelector.

  2. Key selector construction: getKeySelector() is called with effectiveSchema (the table schema) instead of dynamicRecord.schema(). The row data is organized per the record's schema, so the key selector must be built with the record's schema.

Fix

  • Always include the record's schema and spec in the SelectorKey (remove the nulling condition)
  • Use dynamicRecord.schema() and dynamicRecord.spec() when building the PartitionKeySelector

Stack trace

ClassCastException: Cannot cast java.lang.String to java.lang.Integer
  at StructTransform.get(StructTransform.java:89)
  at PartitionKey.get(PartitionKey.java:30)
  at PartitionSpec.partitionToPath(PartitionSpec.java:225)
  at PartitionKeySelector.getKey(PartitionKeySelector.java:64)
  at HashKeyGenerator.generateKey(HashKeyGenerator.java:104)

Fixes #17651

When the table schema is provided to generateKey(), the SelectorKey
cache key was built using only the table schemaId, nulling the
record's own schema. This caused the cache to return the same
PartitionKeySelector for records with different writer schema
variants, leading to ClassCastException when the accessor resolved
a field ID from one variant against row data laid out per the other.

Fix: always include the record's schema and spec in the SelectorKey,
and use the record's own schema/spec when building the
PartitionKeySelector, since the row data is organized per the
record's schema, not the table schema.

Fixes apache#17651
When the table schema is provided to generateKey(), the SelectorKey
cache key was built using only the table schemaId, nulling the
record's own schema. This caused the cache to return the same
PartitionKeySelector for records with different writer schema
variants, leading to ClassCastException when the accessor resolved
a field ID from one variant against row data laid out per the other.

Fix: always include the record's schema and spec in the SelectorKey,
and use the record's own schema/spec when building the
PartitionKeySelector, since the row data is organized per the
record's schema, not the table schema.

Fixes apache#17651
When the table schema is provided to generateKey(), the SelectorKey
cache key was built using only the table schemaId, nulling the
record's own schema. This caused the cache to return the same
PartitionKeySelector for records with different writer schema
variants, leading to ClassCastException when the accessor resolved
a field ID from one variant against row data laid out per the other.

Fix: always include the record's schema and spec in the SelectorKey,
and use the record's own schema/spec when building the
PartitionKeySelector, since the row data is organized per the
record's schema, not the table schema.

Fixes apache#17651
@github-actions github-actions Bot added the flink label Aug 16, 2026
@pvary

pvary commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Could you please provide unit tests?

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

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Flink: Dynamic Sink — ClassCastException in HashKeyGenerator partition keying when records for one table alternate between writer schema variants

2 participants