Skip to content

[vpj] Throw a clear error instead of NPE on a missing timestamp.field - #2908

Open
minhmo1620 wants to merge 2 commits into
linkedin:mainfrom
minhmo1620:minnguye/vpj-clear-error-missing-timestamp-field
Open

[vpj] Throw a clear error instead of NPE on a missing timestamp.field#2908
minhmo1620 wants to merge 2 commits into
linkedin:mainfrom
minhmo1620:minnguye/vpj-clear-error-missing-timestamp-field

Conversation

@minhmo1620

@minhmo1620 minhmo1620 commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Problem Statement

When a batch push sets timestamp.field (the RMD_FIELD_PROP config) so that records carry a per-record replication-metadata (RMD) timestamp, the Spark data-writer validates the input's RMD schema in RmdPushUtils.getInputRmdSchema():

return pushJobSetting.inputDataSchema.getField(pushJobSetting.rmdField).schema();

This line had no null checks, so it threw an opaque NullPointerException in two distinct situations:

  1. inputDataSchema is null — the input never populated a top-level Avro record schema. For example, VSON inputs set rmdField from RMD_FIELD_PROP in DefaultInputDataInfoProvider without ever setting inputDataSchema.
  2. The configured field is absentSchema.getField() returns null when timestamp.field is not a top-level field of the input record (nested inside the value, misnamed, or simply absent).

In both cases the user got a bare NPE at RmdPushUtils.getInputRmdSchema with no hint that the real problem is a misconfigured or missing timestamp.field. The MR record-reader path (AbstractAvroRecordReader.getField) already throws VeniceSchemaFieldNotFoundException for the missing-field case, so the same misconfiguration was legible on the MR engine but surfaced as a bare NPE on the Spark engine.

Solution

Add explicit guards in RmdPushUtils.getInputRmdSchema():

  • If inputDataSchema is null → throw VeniceException naming the configured timestamp.field and noting that a top-level Avro input record schema is required (VSON inputs called out as the example).
  • If the looked-up field is null → throw VeniceSchemaFieldNotFoundException naming the configured timestamp.field and stating it must be a top-level field of the input record (a sibling of the key and value fields, not nested inside the value). This mirrors the existing MR-path behavior.

The success path is unchanged.

How was this PR tested?

  • Modified or extended existing tests. Added two cases to TestRmdPushUtils:
    • testGetInputRmdSchemaWhenInputDataSchemaIsNull — asserts VeniceException when inputDataSchema is null.
    • testGetInputRmdSchemaWhenRmdFieldNotInInputSchema — asserts VeniceSchemaFieldNotFoundException (not NullPointerException) when the configured field is absent from the input schema.
./gradlew :clients:venice-push-job:test --tests "com.linkedin.venice.hadoop.utils.TestRmdPushUtils"

→ 6/6 tests pass.

Does this PR introduce any user-facing or breaking changes?

  • Yes (error-message improvement only). A batch push that configures timestamp.field either without a populated input schema, or with a field that is not present at the top level of the input record, previously failed with a NullPointerException. It now fails with a clear VeniceException / VeniceSchemaFieldNotFoundException whose message explains the requirement. The job still fails in the same situations — only the error is now actionable. There is no change to the success path.

RmdPushUtils.getInputRmdSchema looked up the configured timestamp.field
(RMD_FIELD_PROP) via inputDataSchema.getField(rmdField).schema() with no null
check. Avro's Schema.getField returns null when the field is not a top-level
field of the input record (for example when it is nested inside the value,
misnamed, or absent), so a batch push that sets timestamp.field but does not
expose it at the input record root failed with an opaque NullPointerException
that gave no hint at the real cause.

Null-check the looked-up field and throw VeniceSchemaFieldNotFoundException with
an actionable message that names the configured timestamp.field and states it
must be a top-level field of the input record. This mirrors the existing
behavior of AbstractAvroRecordReader.getField on the MR path, which already
fails with VeniceSchemaFieldNotFoundException for the same case.

Add a unit test asserting the clear exception (not an NPE) is thrown when the
configured field is absent from the input schema.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 30, 2026 17:40

Copilot AI 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.

Pull request overview

This PR improves the Spark push-job validation path for per-record RMD timestamps by replacing an opaque NullPointerException with a targeted VeniceSchemaFieldNotFoundException when the configured timestamp.field is missing from the top-level input schema, and adds a unit test to lock in the behavior.

Changes:

  • Add a null-check in RmdPushUtils.getInputRmdSchema() and throw VeniceSchemaFieldNotFoundException with an actionable message when the configured field is absent.
  • Add a new test case asserting the exception type when Schema.getField() returns null.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
clients/venice-push-job/src/main/java/com/linkedin/venice/spark/utils/RmdPushUtils.java Converts the prior NPE into a clear, user-actionable exception when timestamp.field isn’t found in the input schema.
clients/venice-push-job/src/test/java/com/linkedin/venice/hadoop/utils/TestRmdPushUtils.java Adds coverage to ensure missing timestamp.field yields VeniceSchemaFieldNotFoundException (not NPE).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Address review feedback: getInputRmdSchema dereferenced
pushJobSetting.inputDataSchema before looking up the field, so a push that
configures timestamp.field on an input that never populates inputDataSchema
(e.g. VSON inputs, whose DefaultInputDataInfoProvider branch sets rmdField
without setting inputDataSchema) would still fail with an opaque
NullPointerException. Add an explicit null-schema guard that throws
VeniceException naming the configured timestamp.field, plus a unit test.

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

Copy link
Copy Markdown

Hi there. This pull request has been inactive for 30 days. To keep our review queue healthy, we plan to close it in 7 days unless there is new activity. If you are still working on this, please push a commit, leave a comment, or convert it to draft to signal intent. Thank you for your time and contributions.

@github-actions github-actions Bot added the stale label Jul 31, 2026
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.

2 participants