Kafka connector

Delivery

Know when a record is safely handled: why the connector commits on acceptance rather than completion, what it does with each answer, and the three poison policies.

This is the durability contract of the Kafka connector: when an offset moves, and what happens to a record Duraton will not take. It is the page to read before deciding that a record was lost.

Commit on accept, not on completion

The connector commits a record's offset once Duraton has accepted the event, not when the workflow it started finishes.

  poll record --> POST /events --> Duraton records the run --> 202 accepted --> COMMIT
                                                                                  |
                        the workflow runs on from here, for a second or an hour ---+
                                     (the offset has already moved)

That is deliberate and it is not a weaker guarantee. Acceptance is the durability boundary: the run exists and is recorded before the response is returned, so committing there never advances past non-durable work. Waiting for completion instead would mean a workflow containing a one-hour sleep or a human approval holds its partition for as long as that takes, and a partition held is lag for every other record behind it.

Delivery is at-least-once, and the default dedupe id - the topic:partition:offset triple - is stable across any redelivery, which is what collapses at-least-once into effectively-once through Duraton's own deduplication.

A record the destination will not take

What Duraton answeredWhat the connector does
AcceptedCommit and continue.
Accepted, but the workspace is suspendedDo not commit. Stall with backoff, so the group's lag at the cluster becomes the visible signal instead of the events disappearing.
401 or 403Halt with a configuration error. A rejected key rejects every record, so treating it as poison would empty a topic in minutes.
429Back off and retry the same record. A rate limit is the one 4xx that waiting resolves.
Any other 4xxPoison: apply the poison policy, commit, continue.
5xx, or a transport faultBack off and retry the same record, without committing.
  any other 4xx --> poison --> onPoison?
                                  |
                                  +-- commit-and-log (default) --> log, commit, continue
                                  +-- commit-and-produce --------> write to poisonTopic, commit, continue
                                  +-- halt ----------------------> stop consuming, exit non-zero
onPoisonBehaviour
commit-and-log (default)Log the topic, partition, offset, status and response body; commit; continue.
haltStop consuming and exit non-zero, for a compliance posture that forbids skipping a record silently.
commit-and-produceWrite the record to duraton.poisonTopic, then commit and continue. Requires that key, and that key is valid only under this policy.

Under commit-and-produce the record is written with its original key, value and headers, plus duraton.source.topic, duraton.source.partition, duraton.source.offset, duraton.source.timestamp, duraton.error, and duraton.status when the destination answered at all. The timestamp is there because the written record is a new record carrying its own: without it a drain consumer cannot recover when the original was produced.

The write is acknowledged before the offset is committed, so a crash between the two redelivers the record rather than losing it. If the write cannot land the connector retries it on the same backoff as a refused delivery, indefinitely, and commits nothing: giving up would discard the record you explicitly asked to keep. Lag climbing on a poison topic that has stopped accepting writes is the intended signal.

Startup refuses a poison topic the connector is also consuming, whether it is named in duraton.topics, keyed in duraton.assign, or matched by duraton.topicPattern. Listing it in duraton.excludeTopics is the escape if a pattern is broad enough to catch it.

Backoff

A retried record is worked again on an exponential backoff. The window doubles from duraton.retry.min and is capped at duraton.retry.max, and each wait is picked at random inside that window, so a fleet that stalled on the same destination does not come back to it in step. A wait is never shorter than duraton.retry.min: a destination that has just refused is the last thing worth retrying immediately. That same window also governs the wait after a failed fetch from the cluster, so it is one knob for how hard this process hammers something that is not answering, whichever side is not answering.

BoundValue
First wait after a refusalduraton.retry.min, 250ms unless you set it
Longest wait between attemptsduraton.retry.max, 30s unless you set it
One delivery attempt30 s at the transport, 45 s outer ceiling
How often a continuing stall repeats itself in the log30 s
Attempts before the connector gives up on a recordnone: a stall ends when the destination changes its mind or the process is stopped

Setting neither key keeps those two defaults. A min longer than max is refused at start-up with a message naming both keys and both values, and so is a value of zero or less for either. That is a start-up failure rather than a silent reordering, because an operator who inverted them meant something by it.

There is no attempt limit, and deliberately no key for one. Giving up on a record would mean skipping work the destination never actually refused, which loses data nobody asked to lose - so a stall ends when the destination changes its mind, or when the connector is shut down, and never because a counter ran out. What tells you it is happening is the group's lag at the cluster, plus the stall warning in the log.

Next

On this page