[INLONG-12191][Sort] Support InLong Transform SDK on the Pulsar sink pipeline - #12192
Merged
Conversation
luchunliang
requested review from
aloyszhang,
baomingyu,
fuweng11 and
vernedeng
August 26, 2026 03:35
doleyzi
approved these changes
Aug 26, 2026
fuweng11
approved these changes
Aug 26, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #12191
Motivation
STR_TO_MAP(URL_DECODE(event_value), '&', '=')['HY50']), then emit CSV / KV / JSON to Pulsar.$childIndex+ child-array unpacking can turn one event into multiple downstream messages; the sink pipeline must support that.WHEREclauses can filter events entirely; the sink must ack the event without emitting anything, without leaking transactions.transformSql, same encoding switch, same filter/explode semantics regardless of the underlying MQ.Modifications
Proposed change
Four files changed. All changes are additive; existing Pulsar deployments without
transformSqlare unaffected.1)
inlong-common:PulsarSinkConfigAdd encoding hints and defaults (mirroring
KafkaSinkConfig):The pre-existing fields (
pulsarTenant / namespace / topic / partitionNum) are kept intact.2)
PulsarIdConfigAdd
dataFlowId(aligned withKafkaIdConfig) so the handler can look up the correctTransformProcessor:Map<String,String>constructor initializesdataFlowId = uid(back-compat).create(DataFlowConfig)builder setsdataFlowId = dataFlowConfig.getDataflowId().3)
PulsarFederationSinkContextIntroduce transform caching and encoder wiring:
Semantics:
reload()usestaskConfigJson/sortTaskConfigJsonfor change detection (viareplaceConfig(...)in the baseSinkContext).unifiedConfigurationis on and config changes,transformMap.clear()so stale processors on worker threads are dropped.createTransformbuilds aTransformProcessorusing the base class helperscreateTransformConfig+createSourceDecoder+ our newcreateSinkEncoder.createSinkEncoderdispatches onPulsarSinkConfig.messageType:csv→CsvSinkInfo(encodingType, delimiter [default '|'], escapeChar, fieldInfos)kv→KvSinkInfo(encodingType, fieldInfos)+entrySplitter [default '&']+kvSplitter [default '=']json→MapSinkInfo(encodingType, fieldInfos)'|'.Flows without
transformSqlskipTransformProcessorconstruction entirely.4)
IEvent2PulsarRecordHandler(breaking — see notes)Signature updated to align with the Kafka handler and to allow 0/1/N outputs:
5)
DefaultEvent2PulsarRecordHandlerTwo branches, matching the Kafka side 1:1:
parseByTransform— buildsextParamsfromcontext.getSinkContext().getParameters()+event.getHeaders(), callsprocessor.transformForBytes(event.getBody(), extParams), converts each result:String→getBytes()byte[]→ as-isgson.toJson(...).getBytes()parseByBytes— the original behavior is preserved: forTEXTprependftime + separator + extinfo + separator, then appendevent.getBody(); forPB / JCE / UNKNOWNjust emitevent.getBody().6)
PulsarProducerCluster#sendAdapted to the new
List<byte[]>contract while keeping the "one transaction per event" model:PulsarIdConfigfromevent.getUid().handler.parse(sinkContext, event, idConfig).tx.commit(); event.ack(); tx.close();(filter case).AtomicInteger remaining+AtomicBoolean failed:sendAsynccallback records a per-message metric.tx.rollback(), elsetx.commit()+event.ack(); finallytx.close().This preserves atomicity — an event either fully lands or fully rolls back — even when it fans out to multiple Pulsar messages.
Behavior matrix
transformSqlpresent?messageTypeparseByBytes→ 1 message per eventcsvkvjsonBackward compatibility
PulsarSinkConfigonly adds fields/constants; old JSON that doesn't setmessageType / delimiter / escapeChar / entrySplitter / kvSplitterdeserializes fine.transformSqlis empty,TransformProcessoris not built,parseByTransformis not taken,parseByBytesreturns exactly one payload — semantically identical to today.IEvent2PulsarRecordHandler#parsereturn type changes frombyte[]toList<byte[]>and gainsPulsarIdConfig. This interface is a sort-standalone internal SPI (not published toinlong-common), and the only known implementationDefaultEvent2PulsarRecordHandleris updated in the same change. Users who plugged in a custom handler via theeventHandlercommon property will need a small adaptation:SinkContext/PulsarFederationSinkContextis removed or renamed; only additions.Risks / Notes
eventHandlerimplementations must adapt to the new interface signature. Because a wrong signature will surface as aNoSuchMethodErrorat boot, this failure is loud (not silent).addSendResultMetricis now invoked once per outbound message, which slightly changes metric cardinality (more accurate, but different from previous behavior). Existing dashboards that count "sink send success = event count" may need to switch to "sink send success = message count".MapSinkInfo(SinkEncoderFactory.createMapEncoder) to keep parity with Kafka. If you need strict record-JSON in the future (schema-aware), that is a separate follow-up.Files changed
inlong-common/src/main/java/org/apache/inlong/common/pojo/sort/dataflow/sink/PulsarSinkConfig.javainlong-sort-standalone/sort-standalone-source/src/main/java/org/apache/inlong/sort/standalone/sink/pulsar/PulsarIdConfig.javainlong-sort-standalone/sort-standalone-source/src/main/java/org/apache/inlong/sort/standalone/sink/pulsar/PulsarFederationSinkContext.javainlong-sort-standalone/sort-standalone-source/src/main/java/org/apache/inlong/sort/standalone/sink/pulsar/IEvent2PulsarRecordHandler.javainlong-sort-standalone/sort-standalone-source/src/main/java/org/apache/inlong/sort/standalone/sink/pulsar/DefaultEvent2PulsarRecordHandler.javainlong-sort-standalone/sort-standalone-source/src/main/java/org/apache/inlong/sort/standalone/sink/pulsar/PulsarProducerCluster.javaChecklist
transformSql) is preservedPulsarSinkConfigis JSON back-compat (only new fields/constants added)PulsarFederationSinkContextcachesTransformProcessorper worker thread and clears it on config changeDefaultEvent2PulsarRecordHandlersupports 0 / 1 / N output rowsPulsarProducerClustersends N messages under one transaction and commits/rolls back atomicallyKafkaFederationSinkContext+DefaultEvent2KafkaRecordHandleron the transform code pathVerifying this change
(Please pick either of the following options)
This change is a trivial rework/code cleanup without any test coverage.
This change is already covered by existing tests, such as:
(please describe tests)
This change added tests and can be verified as follows:
(example:)
Documentation