Flink CDC UDFs

更新时间: 2026-08-17 15:14:07

Learn how to create, register, and use Java user-defined functions (UDFs) in Flink CDC jobs. When built-in functions don't meet your needs, custom UDFs provide flexible ways to extend data processing capabilities.

User-defined functions (UDFs)

When built-in Flink CDC functions don't cover your data transformation logic—such as encrypting or decrypting fields, parsing custom data formats, or calling a third-party library—write a Java UDF and call it just like any built-in function.

The overall workflow has three steps:

  1. Define — Write a Java class that implements the UDF interface.

  2. Register — Declare the UDF in the pipeline section of your Flink CDC job configuration.

  3. Use — Call the UDF in projection and filter expressions inside a transform block.

Define a Java UDF

To write a UDF in Java, first create a Maven project and add the following base dependency to your pom.xml:

<dependency>
    <groupId>org.apache.flink</groupId>
    <artifactId>flink-cdc-common</artifactId>
    <version>${Apache Flink CDC version}</version>
    <scope>provided</scope>
</dependency>

Select the Apache Flink CDC version that matches your Ververica Runtime (VVR) engine version:

VVR engine version Apache Flink CDC version
11.7 and later 3.6.0
11.3 to 11.6 3.5.0
11.0 to 11.2 3.4.0
8.0.11 3.3.0
8.0.10 and earlier 3.2.1

A Java class must satisfy the following requirements to work as a Flink CDC UDF:

  • Implements the org.apache.flink.cdc.common.udf.UserDefinedFunction interface

  • Has a public, parameterless constructor

  • Has at least one public method named eval

Optionally, override the following methods for more control:

  • getReturnType — Explicitly specify the return type when eval has an ambiguous return type.

  • open and close — Add initialization and cleanup logic for lifecycle management.

The following example defines a UDF that increments an integer by 1:

public class AddOneFunctionClass implements UserDefinedFunction {

    public Object eval(Integer num) {
        return num + 1;
    }

    @Override
    public DataType getReturnType() {
        // The return type of eval is ambiguous (Object).
        // Use getReturnType to declare the exact CDC type.
        return DataTypes.INT();
    }

    @Override
    public void open() throws Exception {
        // Initialize resources here, for example, open a database connection.
    }

    @Override
    public void close() throws Exception {
        // Release resources here, for example, close a database connection.
    }
}

Type mappings

The following table maps CDC column types to the corresponding Java types used in eval method parameters, return values, and getReturnType:

CDC column type Java class
BOOLEAN java.lang.Boolean
TINYINT java.lang.Byte
SMALLINT java.lang.Short
INTEGER java.lang.Integer
BIGINT java.lang.Long
FLOAT java.lang.Float
DOUBLE java.lang.Double
DECIMAL java.math.BigDecimal
DATE java.time.LocalDate
TIME java.time.LocalTime
TIMESTAMP java.time.LocalDateTime
TIMESTAMP_TZ java.time.ZonedDateTime
TIMESTAMP_LTZ java.time.Instant
CHAR / VARCHAR / STRING java.lang.String
BINARY / VARBINARY / BYTES byte[]
ARRAY java.util.List (element types map to the List generic parameter)
MAP java.util.Map (key and value types map to the Map generic parameters)
ROW java.util.List
VARIANT org.apache.flink.cdc.common.types.variant.Variant
The VARIANT class path differs from the Variant class path in Flink SQL.

Register a Java UDF

Add a user-defined-function entry to the pipeline section of your Flink CDC job configuration:

pipeline:
  user-defined-function:
    - name: inc
      classpath: org.apache.flink.cdc.udf.examples.java.AddOneFunctionClass
    - name: format
      classpath: org.apache.flink.cdc.udf.examples.java.FormatFunctionClass
On the Configurations tab of your job draft, upload the JAR file for the specified classpath as an additional dependency in the More Configurations section. The name field sets the alias used in expressions—it doesn't need to match the class name.

Python UDF (public preview)

You can write inline UDFs in Python directly in Flink CDC YAML ingestion jobs, without creating a Maven project or compiling additional JAR packages.

Note

To define UDFs with Python, you must upgrade to Ververica Runtime (VVR) 11.9 (preview editions included) or later.

This is an experimental feature.

Syntax rules

A Python UDF must meet the following requirements:

  • A top-level function named eval must be defined in python-code.

  • The eval function must declare a return type annotation.

  • The number of parameters of the eval function must match the number of arguments passed when the UDF is called.

  • Parameter type annotations are not required, but keeping them is recommended for code readability.

  • Python UDFs currently support only scalar functions. Each invocation returns a single value.

  • SQL NULL values in the input data are converted to Python None.

  • A Python function can return None. In that case, the output is SQL NULL.

For example, the following Python UDF trims whitespace from the input string and converts it to lowercase:

def eval(value: str) -> str:
    return value.strip().lower()

You can define helper functions, import modules, or declare constants in python-code, but the eval function that performs the data transformation must be at the top level of the code. For example:

import re

EMAIL_PATTERN = re.compile(r"\s+")

def normalize(value):
    return EMAIL_PATTERN.sub("", value).lower()

def eval(value: str) -> str:
    if value is None: return None
    return normalize(value)
Note

You can import built-in Python packages at the top level of the code block. For the complete list, see Pre-installed packages.

Type mappings

Flink CDC determines the output column type of a UDF based on the return type annotation of the eval function. Currently, only the following parameter types and return value types are supported:

Python return type annotation

CDC column type

bool

BOOLEAN

int

BIGINT

float

DOUBLE

str

STRING

bytes

BYTES

Register a Python UDF

To register a Python UDF, configure the python-code parameter under pipeline.user-defined-function of a Flink CDC ingestion job. The YAML | multi-line string syntax lets you write indented Python code blocks naturally:

pipeline:
  user-defined-function:
    - name: normalize_email
      python-code: |
        def eval(value: str) -> str:
            if value is None:
                return None
            return value.strip().lower()

    - name: double_age
      python-code: |
        def eval(value: int) -> int:
            if value is None:
                return None
            return value * 2

Here, name is the function name used in transform expressions and does not need to match the Python function name. The function that Flink CDC calls in the inline code is always eval.

Null and exception handling of Python UDFs

NULL values in an ingestion pipeline correspond to Python None. If a UDF is called with a NULL argument, the Python eval function receives None. If the Python eval function returns None, NULL is used as the UDF return value.

If the Python eval function throws a runtime exception, the transform operator triggers a failover. If your UDF may fail, catch exceptions with Python try/except statements to keep the job stable.

Python UDF parameters

Python UDFs support the following parameters.

Parameter

Description

Required

Data type

Default value

Remarks

name

The name of the UDF registered in the transform module.

Yes

STRING

None

UDF names must be unique within a job.

python-code

The inline source code of the Python UDF.

Yes

STRING

None

It must contain a top-level function named eval with a supported return type annotation declared.

python-files

Additional dependencies used by the Python UDF.

No

LIST<STRING>

None

If a Python UDF requires additional dependencies, package them into a .zip file and upload the file as an additional dependency.

Note

Additional dependency files are placed in the /flink/usrlib/ directory. Enter the full path including the prefix.

For example, if the additional dependency file is PIL.zip, configure the full path /flink/usrlib/PIL.zip.

Use a UDF

After registering a UDF, you can call it directly in the transform block, just like a built-in function:

transform:
  - source-table: db\.*
    projection: "*, inc(inc(inc(id))) as inc_id, format(id, 'id -> %d') as formatted_id"
    filter: inc(id) < 100

Compatibility with Flink SQL UDFs

Flink SQL UDFs that extend ScalarFunction can be registered and called directly as Flink CDC UDFs. The following limitations apply:

  • Parameterization: Parameterized ScalarFunction classes are not supported.

  • Type annotations: Flink-style TypeInformation annotations are ignored.

  • Lifecycle hooks: open and close lifecycle hooks are not called.

上一篇: Flink CDC functions 下一篇: Dirty data collection
阿里云首页 实时计算 Flink版 相关技术圈