[fix][fn] Honour producerSpec batching configuration in the Go function runtime - #26393
Merged
david-streamlio merged 1 commit intoAug 26, 2026
Merged
Conversation
…on runtime ### Motivation PIP-401 (apache#23860) made producer batching configurable for Pulsar Functions. The setting travels as `ProducerConfig.batchingConfig` -> `ProducerSpec.batchingSpec` in `FunctionDetails`, and the Java runtime applies it in `ProducerBuilderFactory`. The Go runtime never reads it. `getProducer` hardcodes `BatchingMaxPublishDelay: time.Millisecond * 10` and never sets `DisableBatching`, so `batchingSpec` arrives in the instance -- the generated bindings in `pb/Function.pb.go` already carry it -- and is silently dropped. Every Go function is pinned to a 10ms publish-latency floor that no configuration can change, and a function configured with `batchingConfig.enabled: false` still batches. `maxPendingMessages` was ignored as well. Fixes apache#26391 ### Modifications - Add `pf/producerConfig.go` with `producerOptionsFromSpec()`, translating a `ProducerSpec` into `pulsar.ProducerOptions`. - Use it in `getProducer()`, which serves both the sink producer and the producers behind `context.NewOutputMessage()`, so one call site covers both. The translation follows the same rules as the Java runtime: - Unset or non-positive spec fields are left at their zero value so the client default applies. - A nil spec, or a spec with no `BatchingSpec`, keeps batching enabled with a 10ms delay, matching `BatchingUtils.convertFromSpec(nil)`. Existing deployments are unaffected. - `BatchingSpec.BatchBuilder` overrides `ProducerSpec.BatchBuilder`, matching the order in which `ProducerBuilderFactory` applies them. The existing compression and batchBuilder handling moves into the same helper, so `getProducer` no longer mixes configuration translation with producer creation. `RoundRobinRouterBatchingPartitionSwitchFrequency` and `MaxPendingMessagesAcrossPartitions` have no equivalent in the Go client and are ignored. `DisableBlockIfQueueFull` stays false (the producer blocks); the Java runtime hardcodes `blockIfQueueFull(true)` as well and exposes no configuration for it, so making it configurable would need a new proto field and belongs in a separate change. No proto regeneration is needed; `pb/Function.pb.go` already contains `BatchingSpec` and `ProducerSpec.GetBatchingSpec()`. ### Verifying this change 14 unit tests added in `pf/producerConfig_test.go`: 10 covering the spec-to-options translation directly, and 4 driving `getProducer` through a fake `pulsar.Client` that captures the options, including a case for a `context.NewOutputMessage()` topic and explicit backwards-compatibility assertions for a function with no producerSpec. 12 of them fail against the unfixed runtime. `go build ./...`, `go test ./pf/...` and `golangci-lint run -c ./golangci.yml ./pf` (v2.12.2, as CI runs it) all pass.
This was referenced Aug 20, 2026
Contributor
Author
|
Same note as #26392, which this mirrors for the Go runtime: the apache/pulsar-site#1215 ("Document This one is green and has had no review activity since 20 Aug. |
9 tasks
freeznet
approved these changes
Aug 26, 2026
freeznet
left a comment
Contributor
There was a problem hiding this comment.
Verified the ProducerSpec/BatchingSpec mapping against FunctionConfigUtils, BatchingUtils, ProducerBuilderFactory, and pulsar-client-go v0.20.0. getProducer covers both output paths, preserves the existing defaults, and applies the supported batching and pending-message settings. The full Go build and test suite pass locally.
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 #26391
Motivation
PIP-401 (#23860) made producer batching configurable for Pulsar Functions. The setting travels as
ProducerConfig.batchingConfig→ProducerSpec.batchingSpecinFunctionDetails, and the Java runtime applies it inProducerBuilderFactory.The Go runtime never reads it.
getProducerhardcodesBatchingMaxPublishDelay: time.Millisecond * 10and never setsDisableBatching, sobatchingSpecarrives in the instance — the generated bindings inpb/Function.pb.goalready carry it — and is silently dropped.Two user-visible consequences:
batchingConfig.enabled: falsestill batches, becauseDisableBatchingis never set.maxPendingMessageswas ignored as well.This is the Go counterpart of #26390, fixed for Python in #26392.
Modifications
pf/producerConfig.gowithproducerOptionsFromSpec(), translating aProducerSpecintopulsar.ProducerOptions.getProducer(), which serves both the sink producer and the producers behindcontext.NewOutputMessage(), so one call site covers both paths.The translation follows the same rules as the Java runtime:
BatchingSpec, keeps batching enabled with a 10ms delay, matchingBatchingUtils.convertFromSpec(nil).BatchingSpec.BatchBuilderoverridesProducerSpec.BatchBuilder, matching the orderProducerBuilderFactoryapplies them in.The existing compression and batchBuilder handling moves into the same helper, so
getProducerno longer mixes configuration translation with producer creation. That accounts for the deletions ininstance.go.No proto regeneration is needed:
pb/Function.pb.goalready containsBatchingSpecandProducerSpec.GetBatchingSpec().Out of scope, deliberately:
DisableBlockIfQueueFullstays false (the producer blocks). The Java runtime hardcodesblockIfQueueFull(true)too and exposes no configuration for it, so making it configurable means adding a newProducerSpecfield — a config-surface change that belongs in its own PR.RoundRobinRouterBatchingPartitionSwitchFrequencyandMaxPendingMessagesAcrossPartitionshave no equivalent in the Go client and are ignored.Documentation
There is no
pulsar-function-goREADME to extend, and adding one solely for this would be out of proportion. The documentation here is thego doccomment onproducerOptionsFromSpec, which carries the fullProducerSpec→pulsar.ProducerOptionsmapping table and the three parity rules above.The user-facing gap is in apache/pulsar-site and I have not addressed it there:
batchingConfigwithout saying which runtimes honour it. That silence is what made both [Functions] Python function runtime hardcodes producer batching config, ignoring producerSpec.batchingSpec #26390 and [Functions] Go function runtime hardcodes producer batching config, ignoring producerSpec.batchingSpec #26391 hard to spot — a user configures the setting and gets no signal that their function is ignoring it.batchingConfigis honoured by all three runtimes, which makes a single accurate docs update possible for the first time. I raised the same point on [fix][fn] Honour producerSpec batching configuration in the Python function runtime #26392 and suggested waiting for this PR rather than writing a note that would go stale; if reviewers agree, one pulsar-site PR can cover Java, Python and Go together.Marking
doc-requiredfor that reason. Happy to open the pulsar-site PR once both of these land.Verifying this change
This change added tests and can be verified as follows:
pf/producerConfig_test.go: 10 covering the spec-to-options translation directly, and 4 drivinggetProducerthrough a fakepulsar.Clientthat captures the options.context.NewOutputMessage()topic rather than the sink topic, covering the second producer path.producerSpecstill getsDisableBatching: false,BatchingMaxPublishDelay: 10ms,CompressionType: LZ4and the default batcher.getProducerwiring fails 12 of the 14, confirming the tests pin the new behaviour rather than passing vacuously.go build ./...andgo test ./pf/...pass (full package, not only the new tests).golangci-lint run -c ./golangci.yml ./pfreports 0 issues, using v2.12.2 — the versionci-go-functions.yamlinstalls../gradlew quickCheckpasses; both new files carry the ASF header.go vetreports three pre-existing lock-copy warnings (instance.go:312,instance.go:650,instanceConf.go:133). They are untouched by this change and CI does not rungo vet.Does this pull request potentially affect one of the following parts:
If the box was checked, please highlight the changes
No box checked: no configuration surface is added or changed, and defaults are unchanged. This makes the Go runtime honour configuration that already exists and is already accepted from users today.