Skip to content

[Go Functions] deadLetterTopic and maxMessageRetries are carried into the instance but never applied #26406

Description

@david-streamlio

Master Issue: #26404

Search before reporting

Motivation

The Go runtime builds RetryDetails into its own FunctionDetails from the instance configuration:

// pulsar-function-go/pf/instanceConf.go:115-118
RetryDetails: &pb.RetryDetails{
	MaxMessageRetries: cfg.MaxMessageRetries,
	DeadLetterTopic:   cfg.DeadLetterTopic,
},

and then never reads it. git grep -in "DLQ\|DeadLetter\|MaxRedeliver" -- 'pulsar-function-go/pf/*.go' returns only that one construction site. No pulsar.DLQPolicy is ever built, and setupConsumer passes no DLQ options to Subscribe.

The configuration is therefore accepted end to end and has no effect: a Go function created with --dead-letter-topic and --max-message-retries reports both back from functions get, and messages that fail repeatedly are redelivered forever instead of being routed to the dead letter topic. The Java runtime applies them in PulsarSource (guarded by hasRetryDetails() in JavaInstanceRunnable).

Populating the field and never consuming it is worse than not carrying it at all — it makes the gap invisible to anyone reading the instance configuration.

Solution

Build a pulsar.DLQPolicy from funcDetails.RetryDetails in setupConsumer and pass it in the ConsumerOptions. The Go client supports this:

type DLQPolicy struct {
	MaxDeliveries    uint32
	DeadLetterTopic  string
	RetryLetterTopic string
	InitialSubscriptionName string
}

Points worth settling, mirroring the ones raised on the Python fix in #26400:

  1. Presence check. RetryDetails is a message field, so funcDetails.GetRetryDetails() != nil is the equivalent of Java's hasRetryDetails(). A scalar check on MaxMessageRetries cannot distinguish unset from zero.
  2. maxMessageRetries of 0. Java accepts >= 0. Whether the Go client's MaxDeliveries accepts 0 meaningfully should be confirmed rather than assumed — the Python client rejects a redelivery count below 1, which is why [fix][fn] Honour deadLetterTopic and maxMessageRetries in the Python function runtime #26400 attaches no policy in that case and warns.
  3. Empty deadLetterTopic. Java sets the topic only when non-empty, leaving the client to derive <topic>-<subscription>-DLQ. Passing an empty string through would override that with an invalid name.
  4. Subscription type. A dead letter policy only takes effect on Shared or KeyShared. That interacts with the sibling issue [Go Functions] retainOrdering and retainKeyOrdering are ignored, so key ordering is silently not preserved #26405, where retainOrdering currently selects nothing and retainKeyOrdering is ignored; whichever lands second should handle the combination rather than silently producing an ineffective policy.

Alternatives

Removing the RetryDetails construction so the configuration is visibly absent would be honest but strictly less useful — the fields are already plumbed, and the Go client supports the feature.

Anything else?

Verified against origin/master.

Are you willing to submit a PR?

  • I'm willing to submit a PR!

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/functiontype/bugThe PR fixed a bug or issue reported a bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions