Search before reporting
Motivation
The Python function runtime silently ignores ConsumerSpec.cryptoSpec.consumerCryptoFailureAction: the Java runtime applies it, the Python runtime drops it, and the pinned Python client already supports the corresponding subscribe() parameter — so this is purely an instance-runtime gap.
A user hits it by configuring a crypto failure action on an encrypted input topic:
inputSpecs:
"persistent://public/default/encrypted-input":
cryptoConfig:
cryptoKeyReaderClassName: "myapp.MyKeyReader"
consumerCryptoFailureAction: CONSUME
- Expected (what the Java runtime does): undecryptable messages are delivered to the function in cleartext; with
DISCARD they are dropped.
- Observed (Python runtime): the setting has no effect. The client falls back to its default
FAIL — the message is logged as an error and redelivered indefinitely instead of being passed through or dropped.
The failure is silent: pulsar-admin accepts the config, functions get reports it back faithfully, and nothing at runtime indicates it was dropped. Unset config is unaffected (proto default FAIL = client default, so runtimes behave identically until the field is set).
Reproducing the issue
On master @ 8ae58a1:
- The field reaches the instance:
FunctionConfig.inputSpecs.<topic>.cryptoConfig.consumerCryptoFailureAction (CryptoConfig.java L45) is serialized by CryptoUtils.convert (CryptoUtils.java L57-62) into ConsumerSpec.cryptoSpec (Function.proto L148-149).
- The Java runtime applies it:
PulsarSource.java L87-88 — cb.cryptoFailureAction(conf.getConsumerCryptoFailureAction()).
- The Python runtime drops it:
python_instance.py L202-216 builds consumer_args with only crypto_key_reader; get_crypto_reader (L605-616) reads only the reader class/config. A grep for CryptoFailureAction in pulsar-functions/instance/src/main/python/ matches only the generated Function_pb2.py.
Additional information
Solution
In python_instance.py's input-consumer setup (L202-216), map consumer_conf.cryptoSpec.consumerCryptoFailureAction onto pulsar.ConsumerCryptoFailureAction — mirroring the existing CompressionType mapping at L389-398 — and pass it as crypto_failure_action in consumer_args. Unset config keeps the client default, so behavior is unchanged for functions that don't configure the field.
Test: extend pulsar-functions/instance/src/test/python/test_python_instance.py, which already mocks the client and asserts producer kwargs (_create_producer_kwargs); assert subscribe receives the mapped crypto_failure_action for each enum value.
Search before reporting
Motivation
The Python function runtime silently ignores
ConsumerSpec.cryptoSpec.consumerCryptoFailureAction: the Java runtime applies it, the Python runtime drops it, and the pinned Python client already supports the correspondingsubscribe()parameter — so this is purely an instance-runtime gap.A user hits it by configuring a crypto failure action on an encrypted input topic:
DISCARDthey are dropped.FAIL— the message is logged as an error and redelivered indefinitely instead of being passed through or dropped.The failure is silent:
pulsar-adminaccepts the config,functions getreports it back faithfully, and nothing at runtime indicates it was dropped. Unset config is unaffected (proto defaultFAIL= client default, so runtimes behave identically until the field is set).Reproducing the issue
On master @ 8ae58a1:
FunctionConfig.inputSpecs.<topic>.cryptoConfig.consumerCryptoFailureAction(CryptoConfig.javaL45) is serialized byCryptoUtils.convert(CryptoUtils.javaL57-62) intoConsumerSpec.cryptoSpec(Function.protoL148-149).PulsarSource.javaL87-88 —cb.cryptoFailureAction(conf.getConsumerCryptoFailureAction()).python_instance.pyL202-216 buildsconsumer_argswith onlycrypto_key_reader;get_crypto_reader(L605-616) reads only the reader class/config. A grep forCryptoFailureActioninpulsar-functions/instance/src/main/python/matches only the generatedFunction_pb2.py.Additional information
pulsar-client-python = "3.13.0"(gradle/libs.versions.tomlL22) exposescrypto_failure_actiononsubscribe()(v3.13.0pulsar/__init__.pyL1240, applied at L1419).producerCryptoFailureActionis likewise unread, but v3.13.0create_producer()has no such parameter, so honoring it needs client support first.cryptoSpec(consumer) as applied — true for the key reader ([improve][fn] Support e2e cryption in python instance #18738), not for the failure action; this issue corrects that row.Solution
In
python_instance.py's input-consumer setup (L202-216), mapconsumer_conf.cryptoSpec.consumerCryptoFailureActionontopulsar.ConsumerCryptoFailureAction— mirroring the existingCompressionTypemapping at L389-398 — and pass it ascrypto_failure_actioninconsumer_args. Unset config keeps the client default, so behavior is unchanged for functions that don't configure the field.Test: extend
pulsar-functions/instance/src/test/python/test_python_instance.py, which already mocks the client and asserts producer kwargs (_create_producer_kwargs); assertsubscribereceives the mappedcrypto_failure_actionfor each enum value.