Code review comment: None of those functions is serializable therefore we cannot pass them directly to the sink.

Code under review:
public SinkRuntimeProvider getSinkRuntimeProvider(Context context) {
        final SerializationSchema<RowData> keySerialization =
                createSerialization(context, keyEncodingFormat, keyProjection, keyPrefix);

        final SerializationSchema<RowData> valueSerialization =
                createSerialization(context, valueEncodingFormat, valueProjection, null);

        final KafkaSinkBuilder<RowData> sinkBuilder = KafkaSink.builder();
        final List<LogicalType> physicalChildren = physicalDataType.getLogicalType().getChildren();
        if (transactionalIdPrefix != null) {
            sinkBuilder.setTransactionalIdPrefix(transactionalIdPrefix);
        }
        final KafkaSink<RowData> kafkaSink =
                sinkBuilder
                        .setDeliverGuarantee(deliveryGuarantee)
                        .setBootstrapServers(
                                properties.get(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG).toString())
                        .setKafkaProducerConfig(properties)
                        .setRecordSerializer(
                                new DynamicKafkaRecordSerializationSchema(
                                        topic,
                                        partitioner,
                                        keySerialization,
                                        valueSerialization,
                                        getFieldGetters(physicalChildren, keyProjection),
                                        getFieldGetters(physicalChildren, valueProjection),
                                        hasMetadata(),
                                        getMetadataPositions(physicalChildren),
                                        upsertMode))
                        .build();
        if (flushMode.isEnabled() && upsertMode) {
            return (DataStreamSinkProvider)
                    dataStream -> {
                        final boolean objectReuse =
                                dataStream
                                        .getExecutionEnvironment()
                                        .getConfig()
                                        .isObjectReuseEnabled();
                        final ReducingUpsertSink<?> sink =
                                new ReducingUpsertSink<>(
                                        kafkaSink,
                                        physicalDataType,
                                        keyProjection,
                                        flushMode,
                                        objectReuse
                                                ? createRowDataTypeSerializer(
                                                        context, dataStream.getExecutionConfig())
                                                : null);
                        final DataStreamSink<RowData> end = dataStream.sinkTo(sink);
                        if (parallelism != null) {
                            end.setParallelism(parallelism);
                        }
                        return end;
                    };
        }
        return SinkProvider.of(kafkaSink, parallelism);
    }

Line commented on: end.setParallelism(parallelism);

Revised code:
public SinkRuntimeProvider getSinkRuntimeProvider(Context context) {
        final SerializationSchema<RowData> keySerialization =
                createSerialization(context, keyEncodingFormat, keyProjection, keyPrefix);

        final SerializationSchema<RowData> valueSerialization =
                createSerialization(context, valueEncodingFormat, valueProjection, null);

        final KafkaSinkBuilder<RowData> sinkBuilder = KafkaSink.builder();
        final List<LogicalType> physicalChildren = physicalDataType.getLogicalType().getChildren();
        if (transactionalIdPrefix != null) {
            sinkBuilder.setTransactionalIdPrefix(transactionalIdPrefix);
        }
        final KafkaSink<RowData> kafkaSink =
                sinkBuilder
                        .setDeliverGuarantee(deliveryGuarantee)
                        .setBootstrapServers(
                                properties.get(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG).toString())
                        .setKafkaProducerConfig(properties)
                        .setRecordSerializer(
                                new DynamicKafkaRecordSerializationSchema(
                                        topic,
                                        partitioner,
                                        keySerialization,
                                        valueSerialization,
                                        getFieldGetters(physicalChildren, keyProjection),
                                        getFieldGetters(physicalChildren, valueProjection),
                                        hasMetadata(),
                                        getMetadataPositions(physicalChildren),
                                        upsertMode))
                        .build();
        if (flushMode.isEnabled() && upsertMode) {
            return (DataStreamSinkProvider)
                    dataStream -> {
                        final boolean objectReuse =
                                dataStream
                                        .getExecutionEnvironment()
                                        .getConfig()
                                        .isObjectReuseEnabled();
                        final ReducingUpsertSink<?> sink =
                                new ReducingUpsertSink<>(
                                        kafkaSink,
                                        physicalDataType,
                                        keyProjection,
                                        flushMode,
                                        objectReuse
                                                ? createRowDataTypeSerializer(
                                                                context,
                                                                dataStream.getExecutionConfig())
                                                        ::copy
                                                : rowData -> rowData);
                        final DataStreamSink<RowData> end = dataStream.sinkTo(sink);
                        if (parallelism != null) {
                            end.setParallelism(parallelism);
                        }
                        return end;
                    };
        }
        return SinkProvider.of(kafkaSink, parallelism);
    }