Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8c4bec8

Browse files
authored
[prism] Support AnyOf in Prism. (#33705)
1 parent 7daccb5 commit 8c4bec8

File tree

4 files changed

+103
-7
lines changed

4 files changed

+103
-7
lines changed

CHANGES.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@
8282
* Initial support for AllowedLateness added. ([#33542](https://github.com/apache/beam/pull/33542))
8383
* The Go SDK's inprocess Prism runner (AKA the Go SDK default runner) now supports non-loopback mode environment types. ([#33572](https://github.com/apache/beam/pull/33572))
8484
* Support the Process Environment for execution in Prism ([#33651](https://github.com/apache/beam/pull/33651))
85+
* Support the AnyOf Environment for execution in Prism ([#33705](https://github.com/apache/beam/pull/33705))
86+
* This improves support for developing Xlang pipelines, when using a compatible cross language service.
8587

8688
## Breaking Changes
8789

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

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"log/slog"
2424
"os"
2525
"os/exec"
26+
"slices"
2627
"time"
2728

2829
fnpb "github.com/apache/beam/sdks/v2/go/pkg/beam/model/fnexecution_v1"
@@ -46,16 +47,26 @@ import (
4647

4748
func runEnvironment(ctx context.Context, j *jobservices.Job, env string, wk *worker.W) error {
4849
logger := j.Logger.With(slog.String("envID", wk.Env))
49-
// TODO fix broken abstraction.
50-
// We're starting a worker pool here, because that's the loopback environment.
51-
// It's sort of a mess, largely because of loopback, which has
52-
// a different flow from a provisioned docker container.
5350
e := j.Pipeline.GetComponents().GetEnvironments()[env]
51+
52+
if e.GetUrn() == urns.EnvAnyOf {
53+
// We've been given a choice!
54+
ap := &pipepb.AnyOfEnvironmentPayload{}
55+
if err := (proto.UnmarshalOptions{}).Unmarshal(e.GetPayload(), ap); err != nil {
56+
logger.Error("unmarshaling any environment payload", "error", err)
57+
return err
58+
}
59+
e = selectAnyOfEnv(ap)
60+
logger.Info("AnyEnv resolved", "selectedUrn", e.GetUrn(), "worker", wk.ID)
61+
// Process the environment as normal.
62+
}
63+
5464
switch e.GetUrn() {
5565
case urns.EnvExternal:
5666
ep := &pipepb.ExternalPayload{}
5767
if err := (proto.UnmarshalOptions{}).Unmarshal(e.GetPayload(), ep); err != nil {
58-
logger.Error("unmarshing external environment payload", "error", err)
68+
logger.Error("unmarshaling external environment payload", "error", err)
69+
return err
5970
}
6071
go func() {
6172
externalEnvironment(ctx, ep, wk)
@@ -65,13 +76,15 @@ func runEnvironment(ctx context.Context, j *jobservices.Job, env string, wk *wor
6576
case urns.EnvDocker:
6677
dp := &pipepb.DockerPayload{}
6778
if err := (proto.UnmarshalOptions{}).Unmarshal(e.GetPayload(), dp); err != nil {
68-
logger.Error("unmarshing docker environment payload", "error", err)
79+
logger.Error("unmarshaling docker environment payload", "error", err)
80+
return err
6981
}
7082
return dockerEnvironment(ctx, logger, dp, wk, j.ArtifactEndpoint())
7183
case urns.EnvProcess:
7284
pp := &pipepb.ProcessPayload{}
7385
if err := (proto.UnmarshalOptions{}).Unmarshal(e.GetPayload(), pp); err != nil {
74-
logger.Error("unmarshing docker environment payload", "error", err)
86+
logger.Error("unmarshaling process environment payload", "error", err)
87+
return err
7588
}
7689
go func() {
7790
processEnvironment(ctx, pp, wk)
@@ -83,6 +96,33 @@ func runEnvironment(ctx context.Context, j *jobservices.Job, env string, wk *wor
8396
}
8497
}
8598

99+
func selectAnyOfEnv(ap *pipepb.AnyOfEnvironmentPayload) *pipepb.Environment {
100+
// Prefer external, then process, then docker, unknown environments are 0.
101+
ranks := map[string]int{
102+
urns.EnvDocker: 1,
103+
urns.EnvProcess: 5,
104+
urns.EnvExternal: 10,
105+
}
106+
107+
envs := ap.GetEnvironments()
108+
109+
slices.SortStableFunc(envs, func(a, b *pipepb.Environment) int {
110+
rankA := ranks[a.GetUrn()]
111+
rankB := ranks[b.GetUrn()]
112+
113+
// Reverse the comparison so our favourite is at the front
114+
switch {
115+
case rankA > rankB:
116+
return -1 // Usually "greater than" would be 1
117+
case rankA < rankB:
118+
return 1
119+
}
120+
return 0
121+
})
122+
// Pick our favourite.
123+
return envs[0]
124+
}
125+
86126
func externalEnvironment(ctx context.Context, ep *pipepb.ExternalPayload, wk *worker.W) {
87127
conn, err := grpc.Dial(ep.GetEndpoint().GetUrl(), grpc.WithTransportCredentials(insecure.NewCredentials()))
88128
if err != nil {
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
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 internal
17+
18+
import (
19+
"testing"
20+
21+
pipepb "github.com/apache/beam/sdks/v2/go/pkg/beam/model/pipeline_v1"
22+
"github.com/apache/beam/sdks/v2/go/pkg/beam/runners/prism/internal/urns"
23+
)
24+
25+
func TestSelectAnyOf(t *testing.T) {
26+
tests := []struct {
27+
name, want string
28+
wantTag string
29+
envs []*pipepb.Environment
30+
}{
31+
{name: "singleDefault", want: urns.EnvDefault, envs: []*pipepb.Environment{{Urn: urns.EnvDefault}}},
32+
{name: "singleDocker", want: urns.EnvDocker, envs: []*pipepb.Environment{{Urn: urns.EnvDocker}}},
33+
{name: "singleProcess", want: urns.EnvProcess, envs: []*pipepb.Environment{{Urn: urns.EnvProcess}}},
34+
{name: "singleExternal", want: urns.EnvExternal, envs: []*pipepb.Environment{{Urn: urns.EnvExternal}}},
35+
{name: "multiplePickExternal_1", want: urns.EnvExternal, envs: []*pipepb.Environment{{Urn: urns.EnvExternal}, {Urn: urns.EnvDocker}, {Urn: urns.EnvProcess}}},
36+
{name: "multiplePickExternal_2", want: urns.EnvExternal, envs: []*pipepb.Environment{{Urn: urns.EnvDocker}, {Urn: urns.EnvProcess}, {Urn: urns.EnvExternal}}},
37+
{name: "multiplePickProcess", want: urns.EnvProcess, envs: []*pipepb.Environment{{Urn: urns.EnvDocker}, {Urn: urns.EnvProcess}}},
38+
{name: "multiplePickDocker", want: urns.EnvDocker, envs: []*pipepb.Environment{{Urn: urns.EnvDefault}, {Urn: urns.EnvDocker}}},
39+
{name: "multiplePickFirstExternal", want: urns.EnvExternal, wantTag: "first", envs: []*pipepb.Environment{{Urn: urns.EnvExternal, Payload: []byte("first")}, {Urn: urns.EnvExternal, Payload: []byte("second")}}},
40+
}
41+
for _, test := range tests {
42+
t.Run(test.name, func(t *testing.T) {
43+
selected := selectAnyOfEnv(&pipepb.AnyOfEnvironmentPayload{Environments: test.envs})
44+
if selected.GetUrn() != test.want {
45+
t.Errorf("expected %v, got %v", test.want, selected.GetUrn())
46+
}
47+
if got, want := string(selected.GetPayload()), test.wantTag; got != want {
48+
t.Errorf("expected payload with tag %v, got %v", want, got)
49+
}
50+
})
51+
}
52+
53+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,4 +147,5 @@ var (
147147
EnvProcess = envUrn(pipepb.StandardEnvironments_PROCESS)
148148
EnvExternal = envUrn(pipepb.StandardEnvironments_EXTERNAL)
149149
EnvDefault = envUrn(pipepb.StandardEnvironments_DEFAULT)
150+
EnvAnyOf = envUrn(pipepb.StandardEnvironments_ANYOF)
150151
)

0 commit comments

Comments
 (0)