[vpj] Throw a clear error instead of NPE on a missing timestamp.field - #2908
[vpj] Throw a clear error instead of NPE on a missing timestamp.field#2908minhmo1620 wants to merge 2 commits into
Conversation
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>
There was a problem hiding this comment.
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 throwVeniceSchemaFieldNotFoundExceptionwith an actionable message when the configured field is absent. - Add a new test case asserting the exception type when
Schema.getField()returnsnull.
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>
|
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. |
Problem Statement
When a batch push sets
timestamp.field(theRMD_FIELD_PROPconfig) so that records carry a per-record replication-metadata (RMD) timestamp, the Spark data-writer validates the input's RMD schema inRmdPushUtils.getInputRmdSchema():This line had no null checks, so it threw an opaque
NullPointerExceptionin two distinct situations:inputDataSchemais null — the input never populated a top-level Avro record schema. For example, VSON inputs setrmdFieldfromRMD_FIELD_PROPinDefaultInputDataInfoProviderwithout ever settinginputDataSchema.Schema.getField()returnsnullwhentimestamp.fieldis 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.getInputRmdSchemawith no hint that the real problem is a misconfigured or missingtimestamp.field. The MR record-reader path (AbstractAvroRecordReader.getField) already throwsVeniceSchemaFieldNotFoundExceptionfor 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():inputDataSchemaisnull→ throwVeniceExceptionnaming the configuredtimestamp.fieldand noting that a top-level Avro input record schema is required (VSON inputs called out as the example).null→ throwVeniceSchemaFieldNotFoundExceptionnaming the configuredtimestamp.fieldand 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?
TestRmdPushUtils:testGetInputRmdSchemaWhenInputDataSchemaIsNull— assertsVeniceExceptionwheninputDataSchemais null.testGetInputRmdSchemaWhenRmdFieldNotInInputSchema— assertsVeniceSchemaFieldNotFoundException(notNullPointerException) when the configured field is absent from the input schema.→ 6/6 tests pass.
Does this PR introduce any user-facing or breaking changes?
timestamp.fieldeither without a populated input schema, or with a field that is not present at the top level of the input record, previously failed with aNullPointerException. It now fails with a clearVeniceException/VeniceSchemaFieldNotFoundExceptionwhose 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.