Skip to content

Commit 656895a

Browse files
committed
spanner_cdc: defer waiting for acks till the end of partition
- Rename waitForAck() to emit and do not wait for ack - Add spannerPartitionBatcher.acks and helper methods to work with it
1 parent dc56f28 commit 656895a

File tree

2 files changed

+71
-10
lines changed

2 files changed

+71
-10
lines changed

internal/impl/gcp/enterprise/input_spanner_cdc.go

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -217,25 +217,29 @@ func newSpannerCDCReader(conf spannerCDCInputConfig, batching service.BatchPolic
217217
}
218218
}
219219

220-
func (r *spannerCDCReader) waitForAck(
220+
func (r *spannerCDCReader) emit(
221221
ctx context.Context,
222222
partitionToken string,
223223
dcr *changestreams.DataChangeRecord,
224224
msg service.MessageBatch,
225-
) error {
225+
) (*ack.Once, error) {
226226
if len(msg) == 0 {
227-
return nil
227+
return nil, nil
228228
}
229229
ackOnce := ack.NewOnce(func(ctx context.Context) error {
230-
return r.subscriber.UpdatePartitionWatermark(ctx, partitionToken, dcr)
230+
// If we processed the message and failed to update the watermark, we
231+
// would try to update it on the next message, no need to return an error here.
232+
if err := r.subscriber.UpdatePartitionWatermark(ctx, partitionToken, dcr); err != nil {
233+
r.log.Errorf("%s: failed to update watermark: %v", partitionToken, err)
234+
}
235+
return nil
231236
})
232237
select {
233238
case <-ctx.Done():
234-
return ctx.Err()
239+
return nil, ctx.Err()
235240
case r.resCh <- asyncMessage{msg: msg, ackFn: ackOnce.Ack}:
236-
// ok
241+
return ackOnce, nil
237242
}
238-
return ackOnce.Wait(ctx)
239243
}
240244

241245
var forcePeriodicFlush = &changestreams.DataChangeRecord{
@@ -248,30 +252,58 @@ func (r *spannerCDCReader) onDataChangeRecord(ctx context.Context, partitionToke
248252
return err
249253
}
250254

255+
if err := batcher.AckError(); err != nil {
256+
return fmt.Errorf("ack error: %v", err)
257+
}
258+
259+
// On partition end, flush the remaining messages and wait for all messages
260+
// to be acked before returning and marking the partition as finished.
251261
if dcr == nil {
252262
msg, last, err := batcher.Flush(ctx)
253263
if err != nil {
254264
return err
255265
}
266+
ack, err := r.emit(ctx, partitionToken, last, msg)
267+
if err != nil {
268+
return err
269+
}
270+
batcher.AddAck(ack)
271+
272+
if err := batcher.WaitAcks(ctx); err != nil {
273+
return fmt.Errorf("ack error: %v", err)
274+
}
256275
if err := batcher.Close(ctx); err != nil {
257276
return err
258277
}
259-
return r.waitForAck(ctx, partitionToken, last, msg)
278+
279+
return nil
260280
}
261281

262282
if dcr == forcePeriodicFlush {
263283
msg, last, err := batcher.Flush(ctx)
264284
if err != nil {
265285
return err
266286
}
267-
return r.waitForAck(ctx, partitionToken, last, msg)
287+
ack, err := r.emit(ctx, partitionToken, last, msg)
288+
if err != nil {
289+
return err
290+
}
291+
batcher.AddAck(ack)
292+
293+
return nil
268294
}
269295

270296
msg, err := batcher.MaybeFlushWith(ctx, dcr)
271297
if err != nil {
272298
return err
273299
}
274-
return r.waitForAck(ctx, partitionToken, dcr, msg)
300+
ack, err := r.emit(ctx, partitionToken, dcr, msg)
301+
if err != nil {
302+
return err
303+
}
304+
batcher.AddAck(ack)
305+
306+
return nil
275307
}
276308

277309
func (r *spannerCDCReader) Connect(ctx context.Context) error {

internal/impl/gcp/enterprise/input_spanner_partition_batcher.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,21 @@ package enterprise
1111
import (
1212
"context"
1313
"encoding/json"
14+
"errors"
1415
"fmt"
1516
"sync"
1617
"time"
1718

1819
"github.com/redpanda-data/benthos/v4/public/service"
20+
"github.com/redpanda-data/connect/v4/internal/ack"
1921
"github.com/redpanda-data/connect/v4/internal/impl/gcp/enterprise/changestreams"
2022
)
2123

2224
type spannerPartitionBatcher struct {
2325
batcher *service.Batcher
2426
last *changestreams.DataChangeRecord
2527
period *time.Timer
28+
acks []*ack.Once
2629
rm func()
2730
}
2831

@@ -60,6 +63,32 @@ func (s *spannerPartitionBatcher) flush(ctx context.Context) (service.MessageBat
6063
return msg, err
6164
}
6265

66+
func (s *spannerPartitionBatcher) AddAck(ack *ack.Once) {
67+
if ack == nil {
68+
return
69+
}
70+
s.acks = append(s.acks, ack)
71+
}
72+
73+
func (s *spannerPartitionBatcher) WaitAcks(ctx context.Context) error {
74+
var merr []error
75+
for _, ack := range s.acks {
76+
if err := ack.Wait(ctx); err != nil {
77+
merr = append(merr, err)
78+
}
79+
}
80+
return errors.Join(merr...)
81+
}
82+
83+
func (s *spannerPartitionBatcher) AckError() error {
84+
for _, ack := range s.acks {
85+
if _, err := ack.TryWait(); err != nil {
86+
return err
87+
}
88+
}
89+
return nil
90+
}
91+
6392
func (s *spannerPartitionBatcher) Close(ctx context.Context) error {
6493
defer s.rm()
6594
if s.period != nil {

0 commit comments

Comments
 (0)