Skip to content

Commit 04f1e0b

Browse files
Exactly one of Topic or Subscription must be set in pubsubio.go (#35369)
* Exactly one of Topic or Subscription must be set in pubsubio.go * Modifying current exaqmple Pubsub read calls as per new changes * Updated changes.md and *ReadOptions * Corrected test cases * Update sdks/go/pkg/beam/io/pubsubio/pubsubio.go
1 parent ec44161 commit 04f1e0b

9 files changed

Lines changed: 100 additions & 20 deletions

File tree

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
## Breaking Changes
8484

8585
* X behavior was changed ([#X](https://github.com/apache/beam/issues/X)).
86+
* Go: The pubsubio.Read transform now accepts ReadOptions as a value type instead of a pointer, and requires exactly one of Topic or Subscription to be set (they are mutually exclusive). Additionally, the ReadOptions struct now includes a Topic field for specifying the topic directly, replacing the previous topic parameter in the Read function signature ([#35369])(https://github.com/apache/beam/pull/35369).
8687

8788
## Deprecations
8889

learning/tour-of-beam/learning-content/io/kafka-io/kafka-write/description.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ When writing data to Kafka using Apache Beam, it is important to ensure that the
2828
For detailed [information](https://beam.apache.org/releases/javadoc/2.0.0/org/apache/beam/sdk/io/kafka/KafkaIO.html)
2929
{{if (eq .Sdk "go")}}
3030
```
31-
data := pubsubio.Read(s, "pubsub-public-data", "taxirides-realtime", nil)
31+
data := pubsubio.Read(s, "pubsub-public-data", pubsubio.ReadOptions{Topic: "taxirides-realtime"})
3232
kvData := beam.ParDo(s, func(elm []byte) ([]byte, []byte) { return []byte(""), elm }, data)
3333
windowed := beam.WindowInto(s, window.NewFixedWindows(15*time.Second), kvData)
3434
kafkaio.Write(s, *expansionAddr, *bootstrapServers, *topic, windowed)

learning/tour-of-beam/learning-content/io/kafka-io/kafka-write/go-example/main.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ func main() {
7676
7777
// In the main function, the code creates a Beam pipeline, reads from the Pub/Sub source, transforms the data into a key-value pair, applies a windowing function to the data, and writes the windowed data to a Kafka topic.
7878
79-
data := pubsubio.Read(s, "pubsub-public-data", "taxirides-realtime", nil)
79+
data := pubsubio.Read(s, "pubsub-public-data", pubsubio.ReadOptions{Topic: "taxirides-realtime"})
8080
kvData := beam.ParDo(s, func(elm []byte) ([]byte, []byte) { return []byte(""), elm }, data)
8181
windowed := beam.WindowInto(s, window.NewFixedWindows(15*time.Second), kvData)
8282
kafkaio.Write(s, *expansionAddr, *bootstrapServers, *topic, windowed)

sdks/go/examples/fhirio/read_write_pubsub/read_write_pubsub.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,9 @@ func main() {
143143
// PubSub notifications will be emitted containing the path of the resource once
144144
// it is written to the store. Simultaneously read notifications and resources
145145
// from PubSub and store, respectively.
146-
resourceNotifications := pubsubio.Read(s, *gcpopts.Project, *pubsubTopic, nil)
146+
resourceNotifications := pubsubio.Read(s, *gcpopts.Project, pubsubio.ReadOptions{
147+
Topic: *pubsubTopic,
148+
})
147149
resourcesInFhirStore, deadLetters := fhirio.Read(s, resourceNotifications)
148150

149151
// Log the read resources or read errors to the server.

sdks/go/examples/kafka/taxi.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,10 @@ func main() {
155155
s := p.Root()
156156

157157
// Read from Pubsub and write to Kafka.
158-
data := pubsubio.Read(s, "pubsub-public-data", "taxirides-realtime", nil)
158+
opts := pubsubio.ReadOptions{
159+
Topic: "taxirides-realtime",
160+
}
161+
data := pubsubio.Read(s, "pubsub-public-data", opts)
159162
kvData := beam.ParDo(s, func(elm []byte) ([]byte, []byte) { return []byte(""), elm }, data)
160163
windowed := beam.WindowInto(s, window.NewFixedWindows(15*time.Second), kvData)
161164
kafkaio.Write(s, *expansionAddr, *bootstrapServers, *topic, windowed)

sdks/go/examples/slowly_updating_side_input/slowly_updating_side_input.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,9 @@ func main() {
103103
_, err = pubsubx.EnsureTopic(ctx, client, inputTopic)
104104
fatalf(err, "Failed to ensure topic: %v", err)
105105

106-
source := pubsubio.Read(s, project, inputTopic, nil)
106+
source := pubsubio.Read(s, project, pubsubio.ReadOptions{
107+
Topic: inputTopic,
108+
})
107109
keyedSource := beam.AddFixedKey(s, source) // simulate keyed data by adding a fixed key
108110
mainInput := beam.WindowInto(
109111
s,

sdks/go/examples/streaming_wordcap/wordcap.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ func main() {
7070
p := beam.NewPipeline()
7171
s := p.Root()
7272

73-
col := pubsubio.Read(s, project, *input, &pubsubio.ReadOptions{Subscription: sub.ID()})
73+
col := pubsubio.Read(s, project, pubsubio.ReadOptions{Subscription: sub.ID()})
7474
str := beam.ParDo(s, func(b []byte) string {
7575
return (string)(b)
7676
}, col)

sdks/go/pkg/beam/io/pubsubio/pubsubio.go

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -52,32 +52,48 @@ func init() {
5252

5353
// ReadOptions represents options for reading from PubSub.
5454
type ReadOptions struct {
55-
Subscription string
55+
Topic string // Topic sets the topic to be read from. A new subscription will be generated for the job. Mutually exclusive with setting a Subscription.
56+
Subscription string // Subscription sets the name of an existing subscription to read from. Mutually exclusive with setting a Topic.
5657
IDAttribute string
5758
TimestampAttribute string
5859
WithAttributes bool
5960
}
6061

6162
// Read reads an unbounded number of PubSubMessages from the given
62-
// pubsub topic. It produces an unbounded PCollecton<*PubSubMessage>,
63+
// pubsub topic or subscription. It produces an unbounded PCollecton<*PubSubMessage>,
6364
// if WithAttributes is set, or an unbounded PCollection<[]byte>.
64-
func Read(s beam.Scope, project, topic string, opts *ReadOptions) beam.PCollection {
65+
//
66+
// The topic or subscription is required and must be set with ReadOptions.
67+
func Read(s beam.Scope, project string, opts ReadOptions) beam.PCollection {
6568
s = s.Scope("pubsubio.Read")
6669

67-
payload := &pipepb.PubSubReadPayload{
68-
Topic: pubsubx.MakeQualifiedTopicName(project, topic),
70+
// Validate: only one of Topic or Subscription should be set
71+
if (opts.Topic == "" && opts.Subscription == "") || (opts.Topic != "" && opts.Subscription != "") {
72+
panic("Exactly one of Topic or Subscription must be set in ReadOptions")
6973
}
70-
if opts != nil {
71-
payload.IdAttribute = opts.IDAttribute
72-
payload.TimestampAttribute = opts.TimestampAttribute
73-
if opts.Subscription != "" {
74-
payload.Subscription = pubsubx.MakeQualifiedSubscriptionName(project, opts.Subscription)
75-
}
76-
payload.WithAttributes = opts.WithAttributes
74+
75+
payload := &pipepb.PubSubReadPayload{}
76+
77+
if opts.Topic != "" {
78+
payload.Topic = pubsubx.MakeQualifiedTopicName(project, opts.Topic)
79+
} else {
80+
payload.Subscription = pubsubx.MakeQualifiedSubscriptionName(project, opts.Subscription)
7781
}
7882

79-
out := beam.External(s, readURN, protox.MustEncode(payload), nil, []beam.FullType{typex.New(reflectx.ByteSlice)}, false)
80-
if opts != nil && opts.WithAttributes {
83+
payload.IdAttribute = opts.IDAttribute
84+
payload.TimestampAttribute = opts.TimestampAttribute
85+
payload.WithAttributes = opts.WithAttributes
86+
87+
out := beam.External(
88+
s,
89+
readURN,
90+
protox.MustEncode(payload),
91+
nil,
92+
[]beam.FullType{typex.New(reflectx.ByteSlice)},
93+
false,
94+
)
95+
96+
if opts.WithAttributes {
8197
return beam.ParDo(s, unmarshalMessageFn, out[0])
8298
}
8399
return out[0]
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one or more
2+
// contributor license agreements. See the NOTICE file distributed with
3+
// this work for additional information regarding copyright ownership.
4+
// The ASF licenses this file to You under the Apache License, Version 2.0
5+
// (the "License"); you may not use this file except in compliance with
6+
// the License. You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
package pubsubio
17+
18+
import (
19+
"github.com/apache/beam/sdks/v2/go/pkg/beam"
20+
"testing"
21+
)
22+
23+
func TestRead_BothTopicAndSubscriptionPanics(t *testing.T) {
24+
defer func() {
25+
if r := recover(); r == nil {
26+
t.Fatal("expected panic when both topic and subscription are set")
27+
}
28+
}()
29+
30+
beam.Init()
31+
32+
p := beam.NewPipeline()
33+
s := p.Root()
34+
35+
opts := ReadOptions{
36+
Topic: "topic",
37+
Subscription: "sub",
38+
}
39+
Read(s, "test-project", opts)
40+
}
41+
42+
func TestRead_NeitherTopicNorSubscriptionPanics(t *testing.T) {
43+
defer func() {
44+
if r := recover(); r == nil {
45+
t.Fatal("expected panic when neither topic nor subscription is set")
46+
}
47+
}()
48+
49+
beam.Init()
50+
51+
p := beam.NewPipeline()
52+
s := p.Root()
53+
54+
opts := ReadOptions{}
55+
Read(s, "test-project", opts)
56+
}

0 commit comments

Comments
 (0)