Skip to content

Commit 113c1c5

Browse files
david-streamlioTechnoboy-
authored andcommitted
[fix][fn] Honour negativeAckRedeliveryDelayMs in the Go function runtime (#26415)
(cherry picked from commit 12b86b1)
1 parent 94966a9 commit 113c1c5

2 files changed

Lines changed: 109 additions & 22 deletions

File tree

pulsar-function-go/pf/instance.go

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,21 @@ func (gi *goInstance) getProducer(topicName string) (pulsar.Producer, error) {
303303
return producer, err
304304
}
305305

306+
// resolveNackRedeliveryDelay returns the negative-ack redelivery delay to apply, or zero to leave
307+
// the client default in place.
308+
//
309+
// SourceSpec.NegativeAckRedeliveryDelayMs is a proto3 scalar with no presence, so an unset field
310+
// reads as 0. Only a positive value is applied, matching the guard the Java runtime uses in
311+
// JavaInstanceRunnable; a zero left in ConsumerOptions is treated by the client as unset, so the
312+
// default applies either way.
313+
func resolveNackRedeliveryDelay(delayMs uint64) time.Duration {
314+
if delayMs == 0 {
315+
return 0
316+
}
317+
318+
return time.Duration(delayMs) * time.Millisecond
319+
}
320+
306321
func (gi *goInstance) setupConsumer() (chan pulsar.ConsumerMessage, error) {
307322
subscriptionType := pulsar.Shared
308323
if int32(gi.context.instanceConf.funcDetails.Source.SubscriptionType) == pb.SubscriptionType_value["FAILOVER"] {
@@ -320,6 +335,8 @@ func (gi *goInstance) setupConsumer() (chan pulsar.ConsumerMessage, error) {
320335
funcDetails.Namespace,
321336
funcDetails.Name), gi.context.instanceConf.instanceID)
322337

338+
nackRedeliveryDelay := resolveNackRedeliveryDelay(funcDetails.Source.NegativeAckRedeliveryDelayMs)
339+
323340
channel := make(chan pulsar.ConsumerMessage)
324341

325342
var (
@@ -338,39 +355,43 @@ func (gi *goInstance) setupConsumer() (chan pulsar.ConsumerMessage, error) {
338355
if consumerConf.ReceiverQueueSize != nil {
339356
if consumerConf.IsRegexPattern {
340357
consumer, err = gi.client.Subscribe(pulsar.ConsumerOptions{
341-
TopicsPattern: topicName.Name,
342-
ReceiverQueueSize: int(consumerConf.ReceiverQueueSize.Value),
343-
SubscriptionName: subscriptionName,
344-
Properties: properties,
345-
Type: subscriptionType,
346-
MessageChannel: channel,
358+
TopicsPattern: topicName.Name,
359+
ReceiverQueueSize: int(consumerConf.ReceiverQueueSize.Value),
360+
SubscriptionName: subscriptionName,
361+
Properties: properties,
362+
Type: subscriptionType,
363+
MessageChannel: channel,
364+
NackRedeliveryDelay: nackRedeliveryDelay,
347365
})
348366
} else {
349367
consumer, err = gi.client.Subscribe(pulsar.ConsumerOptions{
350-
Topic: topicName.Name,
351-
SubscriptionName: subscriptionName,
352-
Properties: properties,
353-
Type: subscriptionType,
354-
ReceiverQueueSize: int(consumerConf.ReceiverQueueSize.Value),
355-
MessageChannel: channel,
368+
Topic: topicName.Name,
369+
SubscriptionName: subscriptionName,
370+
Properties: properties,
371+
Type: subscriptionType,
372+
ReceiverQueueSize: int(consumerConf.ReceiverQueueSize.Value),
373+
MessageChannel: channel,
374+
NackRedeliveryDelay: nackRedeliveryDelay,
356375
})
357376
}
358377
} else {
359378
if consumerConf.IsRegexPattern {
360379
consumer, err = gi.client.Subscribe(pulsar.ConsumerOptions{
361-
TopicsPattern: topicName.Name,
362-
SubscriptionName: subscriptionName,
363-
Properties: properties,
364-
Type: subscriptionType,
365-
MessageChannel: channel,
380+
TopicsPattern: topicName.Name,
381+
SubscriptionName: subscriptionName,
382+
Properties: properties,
383+
Type: subscriptionType,
384+
MessageChannel: channel,
385+
NackRedeliveryDelay: nackRedeliveryDelay,
366386
})
367387
} else {
368388
consumer, err = gi.client.Subscribe(pulsar.ConsumerOptions{
369-
Topic: topicName.Name,
370-
SubscriptionName: subscriptionName,
371-
Properties: properties,
372-
Type: subscriptionType,
373-
MessageChannel: channel,
389+
Topic: topicName.Name,
390+
SubscriptionName: subscriptionName,
391+
Properties: properties,
392+
Type: subscriptionType,
393+
MessageChannel: channel,
394+
NackRedeliveryDelay: nackRedeliveryDelay,
374395
})
375396

376397
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
//
2+
// Licensed to the Apache Software Foundation (ASF) under one
3+
// or more contributor license agreements. See the NOTICE file
4+
// distributed with this work for additional information
5+
// regarding copyright ownership. The ASF licenses this file
6+
// to you under the Apache License, Version 2.0 (the
7+
// "License"); you may not use this file except in compliance
8+
// with the License. You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing,
13+
// software distributed under the License is distributed on an
14+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
// KIND, either express or implied. See the License for the
16+
// specific language governing permissions and limitations
17+
// under the License.
18+
//
19+
20+
package pf
21+
22+
import (
23+
"testing"
24+
"time"
25+
26+
"github.com/stretchr/testify/assert"
27+
)
28+
29+
// The Go runtime nacks on failure but never configured the delay, so the client default of 60s
30+
// applied regardless of SourceSpec.NegativeAckRedeliveryDelayMs.
31+
func TestResolveNackRedeliveryDelay(t *testing.T) {
32+
tests := []struct {
33+
name string
34+
delayMs uint64
35+
expected time.Duration
36+
}{
37+
{
38+
// proto3 scalar with no presence: unset reads as 0. Returning zero leaves
39+
// ConsumerOptions at its zero value, which the client treats as unset.
40+
name: "unset leaves the client default",
41+
delayMs: 0,
42+
expected: 0,
43+
},
44+
{
45+
name: "milliseconds are converted to a duration",
46+
delayMs: 5000,
47+
expected: 5 * time.Second,
48+
},
49+
{
50+
name: "sub-second values are preserved",
51+
delayMs: 250,
52+
expected: 250 * time.Millisecond,
53+
},
54+
{
55+
name: "the client default expressed explicitly still round-trips",
56+
delayMs: 60000,
57+
expected: time.Minute,
58+
},
59+
}
60+
61+
for _, test := range tests {
62+
t.Run(test.name, func(t *testing.T) {
63+
assert.Equal(t, test.expected, resolveNackRedeliveryDelay(test.delayMs))
64+
})
65+
}
66+
}

0 commit comments

Comments
 (0)