Skip to content

Commit 580db93

Browse files
authored
[Prism] LP SOME coder and its data elements for TestStream (#36424)
* Always LP coder and its data elements for TestStream * Fix the failed tests in go sdk due to side input coders not setting correctly. * Reduce the change scope. Also avoid LP'ing an LP'ed coder.. * Mention breaking changes in CHANGES.md
1 parent 0d121ec commit 580db93

5 files changed

Lines changed: 53 additions & 11 deletions

File tree

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
* (Python) Fixed transform naming conflict when executing DataTransform on a dictionary of PColls ([#30445](https://github.com/apache/beam/issues/30445)).
8787
This may break update compatibility if you don't provide a `--transform_name_mapping`.
8888
* Removed deprecated Hadoop versions (2.10.2 and 3.2.4) that are no longer supported for [Iceberg](https://github.com/apache/iceberg/issues/10940) from IcebergIO ([#36282](https://github.com/apache/beam/issues/36282)).
89+
* (Go) Coder construction on SDK side is more faithful to the specs from runners without stripping length-prefix. This may break streaming pipeline update as the underlying coder could be changed ([#36424](https://github.com/apache/beam/pull/36424)).
8990

9091
## Deprecations
9192

runners/prism/java/build.gradle

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,11 @@ def sickbayTests = [
106106

107107
// Prism doesn't support multiple TestStreams.
108108
'org.apache.beam.sdk.testing.TestStreamTest.testMultipleStreams',
109-
// Sometimes fails missing a final 'AFTER'. Otherwise, Hangs in ElementManager.FailBundle due to a held stageState lock.
110-
'org.apache.beam.sdk.testing.TestStreamTest.testMultiStage',
111109

112110
// GroupIntoBatchesTest tests that fail:
113-
// Teststream has bad KV encodings due to using an outer context.
111+
// Wrong number of elements in windows after GroupIntoBatches.
114112
'org.apache.beam.sdk.transforms.GroupIntoBatchesTest.testInStreamingMode',
115113
'org.apache.beam.sdk.transforms.GroupIntoBatchesTest.testBufferingTimerInFixedWindow',
116-
// sdk worker disconnected
117114
'org.apache.beam.sdk.transforms.GroupIntoBatchesTest.testBufferingTimerInGlobalWindow',
118115
// ShardedKey not yet implemented.
119116
'org.apache.beam.sdk.transforms.GroupIntoBatchesTest.testWithShardedKeyInGlobalWindow',

sdks/go/pkg/beam/core/runtime/graphx/coder.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -266,9 +266,9 @@ func (b *CoderUnmarshaller) makeCoder(id string, c *pipepb.Coder) (*coder.Coder,
266266

267267
// No payload means this coder was length prefixed by the runner
268268
// but is likely self describing - AKA a beam coder.
269-
if len(sub.GetSpec().GetPayload()) == 0 {
270-
return b.makeCoder(components[0], sub)
271-
}
269+
// if len(sub.GetSpec().GetPayload()) == 0 {
270+
// return b.makeCoder(components[0], sub)
271+
// }
272272
// TODO(lostluck) 2018/10/17: Make this strict again, once dataflow can use
273273
// the portable pipeline model directly (BEAM-2885)
274274
switch u := sub.GetSpec().GetUrn(); u {
@@ -285,8 +285,8 @@ func (b *CoderUnmarshaller) makeCoder(id string, c *pipepb.Coder) (*coder.Coder,
285285
t := typex.New(custom.Type)
286286
cc := &coder.Coder{Kind: coder.Custom, T: t, Custom: custom}
287287
return cc, nil
288-
case urnBytesCoder, urnStringCoder: // implicitly length prefixed types.
289-
return b.makeCoder(components[0], sub)
288+
// case urnBytesCoder, urnStringCoder: // implicitly length prefixed types.
289+
// return b.makeCoder(components[0], sub)
290290
default:
291291
// Handle Length prefixing dictated by the runner.
292292
cc, err := b.makeCoder(components[0], sub)

sdks/go/pkg/beam/runners/prism/internal/coders.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,31 @@ func lpUnknownCoders(cID string, bundle, base map[string]*pipepb.Coder) (string,
198198
return cID, nil
199199
}
200200

201+
// forceLpCoder always add a new LP-coder for a given coder into the "base" map
202+
func forceLpCoder(cID string, base map[string]*pipepb.Coder) (string, error) {
203+
// First check if we've already added the LP version of this coder to coders already.
204+
lpcID := cID + "_flp"
205+
// Check if we've done this one before.
206+
if _, ok := base[lpcID]; ok {
207+
return lpcID, nil
208+
}
209+
// Look up the canonical location.
210+
_, ok := base[cID]
211+
if !ok {
212+
// We messed up somewhere.
213+
return "", fmt.Errorf("forceLpCoders: coder %q not present in base map", cID)
214+
}
215+
216+
lpc := &pipepb.Coder{
217+
Spec: &pipepb.FunctionSpec{
218+
Urn: urns.CoderLengthPrefix,
219+
},
220+
ComponentCoderIds: []string{cID},
221+
}
222+
base[lpcID] = lpc
223+
return lpcID, nil
224+
}
225+
201226
// retrieveCoders recursively ensures that the coder along with all its direct
202227
// and indirect component coders, are present in the `bundle` map.
203228
// If a coder is already in `bundle`, it's skipped. Returns an error if any

sdks/go/pkg/beam/runners/prism/internal/execute.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -285,13 +285,25 @@ func executePipeline(ctx context.Context, wks map[string]*worker.W, j *jobservic
285285
//slog.Warn("teststream bytes", "value", string(v), "bytes", v)
286286
return v
287287
}
288-
// Hack for Java Strings in test stream, since it doesn't encode them correctly.
289-
forceLP := cID == "StringUtf8Coder" || cID != pyld.GetCoderId()
288+
// If the TestStream coder needs to be LP'ed or if it is a coder that has different
289+
// behaviors between nested context and outer context (in Java SDK), then we must
290+
// LP this coder and the TestStream data elements.
291+
forceLP := cID != pyld.GetCoderId() ||
292+
coders[cID].GetSpec().GetUrn() == urns.CoderStringUTF8 ||
293+
coders[cID].GetSpec().GetUrn() == urns.CoderBytes ||
294+
coders[cID].GetSpec().GetUrn() == urns.CoderKV
290295
if forceLP {
291296
// slog.Warn("recoding TestStreamValue", "cID", cID, "newUrn", coders[cID].GetSpec().GetUrn(), "payloadCoder", pyld.GetCoderId(), "oldUrn", coders[pyld.GetCoderId()].GetSpec().GetUrn())
292297
// The coder needed length prefixing. For simplicity, add a length prefix to each
293298
// encoded element, since we will be sending a length prefixed coder to consume
294299
// this anyway. This is simpler than trying to find all the re-written coders after the fact.
300+
// This also adds a LP-coder for the original coder in comps.
301+
cID, err := forceLpCoder(pyld.GetCoderId(), comps.GetCoders())
302+
if err != nil {
303+
panic(err)
304+
}
305+
slog.Debug("teststream: add coder", "coderId", cID)
306+
295307
mayLP = func(v []byte) []byte {
296308
var buf bytes.Buffer
297309
if err := coder.EncodeVarInt((int64)(len(v)), &buf); err != nil {
@@ -303,6 +315,13 @@ func executePipeline(ctx context.Context, wks map[string]*worker.W, j *jobservic
303315
//slog.Warn("teststream bytes - after LP", "value", string(v), "bytes", buf.Bytes())
304316
return buf.Bytes()
305317
}
318+
319+
// we need to change Coder and Pcollection in comps directly before they are used to build descriptors
320+
for _, col := range t.GetOutputs() {
321+
oCID := comps.Pcollections[col].CoderId
322+
comps.Pcollections[col].CoderId = cID
323+
slog.Debug("teststream: rewrite coder for output pcoll", "colId", col, "oldId", oCID, "newId", cID)
324+
}
306325
}
307326

308327
tsb := em.AddTestStream(stage.ID, t.Outputs)

0 commit comments

Comments
 (0)