Skip to content

[FLINK-38647][CDC] Improve error message for null before/after struct…#4439

Open
BreadS00 wants to merge 4 commits into
apache:masterfrom
BreadS00:master
Open

[FLINK-38647][CDC] Improve error message for null before/after struct…#4439
BreadS00 wants to merge 4 commits into
apache:masterfrom
BreadS00:master

Conversation

@BreadS00

@BreadS00 BreadS00 commented Jun 8, 2026

Copy link
Copy Markdown

When PostgreSQL table uses default REPLICA IDENTITY DEFAULT or REPLICA IDENTITY PRIMARY KEY, the logical decoding plugin (pgoutput) may not provide complete before-data for UPDATE events. This causes the before struct to be null in Debezium events, leading to an unhelpful NullPointerException.

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

Improves diagnostics when Debezium before/after payload structs are unexpectedly null (common with PostgreSQL REPLICA IDENTITY DEFAULT/PRIMARY KEY), replacing an unhelpful downstream NullPointerException with a targeted message and an optional connector-specific hint.

Changes:

  • Add explicit null checks for before/after structs in DebeziumEventDeserializationSchema, throwing with a clearer message.
  • Introduce an overridable getNullBeforeDataHint() hook for connector-specific guidance.
  • Provide a PostgreSQL-specific hint suggesting REPLICA IDENTITY FULL.

Reviewed changes

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

File Description
flink-cdc-connect/flink-cdc-source-connectors/flink-connector-debezium/src/main/java/org/apache/flink/cdc/debezium/event/DebeziumEventDeserializationSchema.java Adds null checks for before/after and a hook for connector-specific hint text.
flink-cdc-connect/flink-cdc-pipeline-connectors/flink-cdc-pipeline-connector-postgres/src/main/java/org/apache/flink/cdc/connectors/postgres/source/PostgresEventDeserializer.java Overrides the hint hook to provide a PostgreSQL-specific remediation suggestion.

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

@yuxiqian yuxiqian left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Thanks for @BreadS00's nice work, just left some minor comments.

Comment on lines +147 to +153
if (beforeValue == null) {
throw new NullPointerException(
"Before data is null for UPDATE/DELETE event. "
+ formatHint(getNullBeforeDataHint())
+ "Schema name: "
+ valueSchema.name());
}

@yuxiqian yuxiqian Jul 10, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I wonder if we can throw a generic NPE in extractBeforeDataRecord, and wrap it in PostgresEventDeserializer like this?

// In PG event deserializer

@Override
private RecordData extractBeforeDataRecord(Struct value, Schema valueSchema) throws Exception {
    try {
        return super.extractBeforeDataRecord(value, valueSchema);
    } catch (NullPointerException ape) {
        // throw a wrapper exception here, indicating that one may forget to specify REPLICA IDENTITY PRIMARY mode
    }
}

or add extra checking like this:

private RecordData extractBeforeDataRecord(Struct value, Schema valueSchema) throws Exception {
    Struct beforeValue = fieldStruct(value, Envelope.FieldName.BEFORE);
    // Ensure we got proper UPDATE_BEFORE here
    Preconditions.checkNotNull(beforeValue);

    return super.extractBeforeDataRecord(value, valueSchema);
}

WDYT?

Comment on lines +157 to +172
/** Connector-specific hint appended to the error message when before data is null. */
protected String getNullBeforeDataHint() {
return "";
}

/**
* Normalizes the hint string to a single trailing space so that overrides do not need to manage
* whitespace themselves. Returns an empty string when the hint is null or blank.
*/
private static String formatHint(String hint) {
if (hint == null) {
return "";
}
String trimmed = hint.trim();
return trimmed.isEmpty() ? "" : trimmed + " ";
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I wonder if it's necessary to introduce new methods here just for error messages.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants