Skip to content

kafka: reject max_in_flight_requests != 1 with idempotent_write at config time - #4645

Merged
prakhargarg105 merged 1 commit into
redpanda-data:mainfrom
prakhargarg105:fix/max-in-flight-requests-idempotency
Aug 5, 2026
Merged

kafka: reject max_in_flight_requests != 1 with idempotent_write at config time#4645
prakhargarg105 merged 1 commit into
redpanda-data:mainfrom
prakhargarg105:fix/max-in-flight-requests-idempotency

Conversation

@prakhargarg105

Copy link
Copy Markdown
Contributor

Problem

Setting max_in_flight_requests to any value other than 1 while idempotent_write is enabled (the default) produced a pipeline that passed lint, started, and then produced zero records forever:

level=error msg="Failed to connect to redpanda: invalid usage of
MaxProduceRequestsInflightPerBroker with idempotency enabled" path=root.output

No fatal error, no non-zero exit — just an endlessly retried connection error at zero throughput.

franz-go is behaving correctly here: it rejects anything but 1 while idempotency is on (kgo/config.go:232) because it relies on a single in-flight request per broker to keep producer sequence numbers gapless. The defect is on our side — we applied the option unconditionally and validated only < 1, so a permanent config error surfaced from inside Connect() where the framework treats it as retryable.

Made more likely by our own field documentation, which described the invalid configuration as valid ("capped at 5 by the Kafka protocol").

Changes

1. Validate at config time, mirroring how the sibling acks constraint is already handled:

  • A rule in FranzWriterConfigLints(), so lint catches it before the pipeline runs.
  • A check in FranzProducerOptsFromConfig, which all affected components funnel through, for paths that bypass lint (--chilled, programmatic config construction).

2. Correct the field documentation. It said the value is "capped at 5 by the Kafka protocol" when idempotent_write is enabled. It is not capped — it must be exactly 1, so the documented configuration could never start. While there, added two clarifications that caused real confusion:

  • This field is not max_in_flight. That one counts message batches written in parallel (default 256 on the redpanda output); this one counts unacknowledged produce requests per broker connection.
  • 1 is not a throughput ceiling — records from concurrent writes are coalesced into fewer, larger produce requests.

3. Fixed a latent gap in the existing acks rule. It matched on this.idempotent_write == true, which does not fire when the field is left at its default of true, so acks: leader with idempotent_write omitted was never caught at lint. Both rules now use .or(true). This only surfaces an existing failure earlier — FranzProducerOptsFromConfig already rejected those configs at construction time — but it is strictly an addition to the reported bug and easy to split out if preferred.

Affected components

All six inheriting FranzProducerFields(): redpanda and kafka_franz outputs, redpanda_migrator, the global redpanda logs/status writer, the redpanda tracer, and the ockam_kafka output. The older sarama-based kafka output is unaffected (different client, no such field).

Verification

  • go build ./... clean.
  • go test ./internal/impl/kafka/... ./internal/impl/redpanda/... ./internal/impl/ockam/... — all pass.
  • task lint — 0 issues. task fmt applied.
  • task docs — regenerated exactly the 6 expected pages, one line each, no unrelated drift.
  • End-to-end against a built binary: the previously-silent config now fails lint with exit 1 and a clear message, while idempotent_write: false + max_in_flight_requests: 5 and plain default configs still lint clean.

New tests: 5 cases added to TestKafkaFranzOutputBadParams (covering both the explicit and the defaulted idempotent_write shapes) plus TestFranzProducerOptsIdempotencyLimits, which asserts the opt-construction path rejects the combination at config parse time rather than at connect time.

Notes

Found while tuning a customer ingest pipeline (Zendesk 7161), where raising this value was recommended as a tuning step based on the field documentation and had to be retracted after testing.

Fixes CON-522
Fixes DOC-2404

🤖 Generated with Claude Code

…nfig time

franz-go rejects any value other than 1 for
MaxProduceRequestsInflightPerBroker while idempotency is enabled, because it
relies on a single in-flight request per broker to keep producer sequence
numbers gapless. Connect applied the option unconditionally and validated only
`< 1`, so the rejection surfaced from within Connect() as a retryable
connection error: the config passed lint, the pipeline started, and it then
retried "Failed to connect" forever while producing zero records. No fatal
error, no non-zero exit.

Add the missing cross-field validation in two places, mirroring how the
sibling `acks` constraint is already handled:

- A linter rule, so `lint` catches it before the pipeline runs.
- A check in FranzProducerOptsFromConfig, which every affected component
  funnels through, for paths that bypass lint (--chilled, programmatic config).

Also correct the field documentation, which claimed the value was "capped at 5
by the Kafka protocol" when idempotent_write is enabled. It is not capped, it
must be exactly 1, so the documented configuration could never start. While
there, note that this field is distinct from the output's `max_in_flight` and
that 1 is not a throughput ceiling, since records from concurrent writes are
coalesced into fewer, larger produce requests. Both points caused real
confusion.

The existing `acks` linter rule matched on `this.idempotent_write == true`,
which does not fire when the field is left at its default of true. Use
`.or(true)` on both rules so the common shape (setting only the offending
field) is caught. This surfaces an existing error earlier rather than
introducing a new one, as FranzProducerOptsFromConfig already rejected those
configs at construction time.

Affects all six components inheriting FranzProducerFields(): the redpanda and
kafka_franz outputs, redpanda_migrator, the global redpanda logs/status
writer, the redpanda tracer and the ockam_kafka output.

Fixes CON-522
Fixes DOC-2404

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@prakhargarg105
prakhargarg105 force-pushed the fix/max-in-flight-requests-idempotency branch from 94a34af to 0332404 Compare August 5, 2026 17:20
@prakhargarg105
prakhargarg105 merged commit 8d41df6 into redpanda-data:main Aug 5, 2026
9 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants