Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ jobs:

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
uses: github/codeql-action/init@v4.37.3
uses: github/codeql-action/init@v4.37.4
with:
languages: ${{ matrix.language }}
build-mode: ${{ matrix.build-mode }}
Expand Down Expand Up @@ -192,6 +192,6 @@ jobs:
fi

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4.37.3
uses: github/codeql-action/analyze@v4.37.4
with:
category: "/language:${{matrix.language}}"
2 changes: 1 addition & 1 deletion CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@
* (Python) Added `equal_to_approx`, an `assert_that` matcher that compares numeric pipeline outputs with a configurable tolerance ([#18028](https://github.com/apache/beam/issues/18028)).
* (Python) `Timestamp` now supports variable subsecond precision, up to nanoseconds. The portable
`beam:logical_type:timestamp:v1` logical type now maps to Python's `Timestamp` ([#39344](https://github.com/apache/beam/issues/39344)).
* X feature added (Java/Python) ([#X](https://github.com/apache/beam/issues/X)).
* (Python) Added support to analyze core dumps created after python worker segmentation faults with `pystack` (or `gdb` if installed) using the `--profiler_agent=coredump` pipeline option. ([#39484](https://github.com/apache/beam/issues/39484)).

## Breaking Changes

Expand Down
14 changes: 7 additions & 7 deletions sdks/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ require (
github.com/johannesboyne/gofakes3 v1.2.0
github.com/lib/pq v1.12.3
github.com/linkedin/goavro/v2 v2.15.0
github.com/nats-io/nats-server/v2 v2.14.3
github.com/nats-io/nats-server/v2 v2.14.4
github.com/nats-io/nats.go v1.52.0
github.com/proullon/ramsql v0.1.4
github.com/spf13/cobra v1.10.2
Expand All @@ -62,7 +62,7 @@ require (
golang.org/x/text v0.40.0
google.golang.org/api v0.291.0
google.golang.org/genproto v0.0.0-20260523011958-0a33c5d7ca68
google.golang.org/grpc v1.82.1
google.golang.org/grpc v1.83.0
google.golang.org/protobuf v1.36.11
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
Expand All @@ -86,10 +86,10 @@ require (
dario.cat/mergo v1.0.2 // indirect
filippo.io/edwards25519 v1.2.0 // indirect
github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.6.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.32.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.33.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.57.0 // indirect
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.57.0 // indirect
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op // indirect
github.com/antithesishq/antithesis-sdk-go v0.7.2-default-no-op // indirect
github.com/apache/arrow/go/v15 v15.0.2 // indirect
github.com/aws/aws-sdk-go-v2/service/signin v1.5.2 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
Expand Down Expand Up @@ -118,14 +118,14 @@ require (
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/shirou/gopsutil/v4 v4.26.5 // indirect
github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect
github.com/spiffe/go-spiffe/v2 v2.7.0 // indirect
github.com/stretchr/testify v1.11.1 // indirect
github.com/tklauser/go-sysconf v0.4.0 // indirect
github.com/tklauser/numcpus v0.12.0 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
go.einride.tech/aip v0.83.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/contrib/detectors/gcp v1.43.0 // indirect
go.opentelemetry.io/contrib/detectors/gcp v1.44.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect
go.opentelemetry.io/otel v1.44.0 // indirect
Expand Down Expand Up @@ -178,7 +178,7 @@ require (
github.com/gorilla/handlers v1.5.2 // indirect
github.com/gorilla/mux v1.8.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/klauspost/compress v1.18.6 // indirect
github.com/klauspost/compress v1.19.0 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/magiconair/properties v1.8.10 // indirect
github.com/moby/patternmatcher v0.6.1 // indirect
Expand Down
28 changes: 14 additions & 14 deletions sdks/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,8 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
github.com/GoogleCloudPlatform/cloudsql-proxy v1.29.0/go.mod h1:spvB9eLJH9dutlbPSRmHvSXXHOwGRyeXh1jVdquA2G8=
github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.6.0 h1:BzsL0qE7LvtTEtXG7Dt5NS1EP0CQwI21HZfj9aGghhw=
github.com/GoogleCloudPlatform/grpc-gcp-go/grpcgcp v1.6.0/go.mod h1:I7kE2kM3qCr9QPT4cU4cCFYkEpVyVr16YOGUHzy+nR0=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.32.0 h1:rIkQfkCOVKc1OiRCNcSDD8ml5RJlZbH/Xsq7lbpynwc=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.32.0/go.mod h1:RD2SsorTmYhF6HkTmDw7KmPYQk8OBYwTkuasChwv7R4=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.33.0 h1:l7+6kwRMJNwdCvYdDl7Eax+wzEYHSnNY7zrrfbhDdTA=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.33.0/go.mod h1:pJTkW8hEUIIi3Pf65lPZOnn4Y81yCllX6IWk2jNXdkM=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.57.0 h1:jLdiS1vO+XJFyDSWRHBx56r4s/NNtcl5J6KyCcWUX/w=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.57.0/go.mod h1:8lmpHY+1VRoteiOwyrQMDt1YGXOrFKCz+1wJW7n3ODY=
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.57.0 h1:cSjUzZ7KU8hicTgzaSv9NmSyM9fTVK3y5lsBUl3wOis=
Expand All @@ -177,8 +177,8 @@ github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0=
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI=
github.com/antithesishq/antithesis-sdk-go v0.7.2-default-no-op h1:p2zFsAzvhIpFya8AIOHIbWf7NGvO34QpLGclyf7nXj8=
github.com/antithesishq/antithesis-sdk-go v0.7.2-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI=
github.com/apache/arrow/go/arrow v0.0.0-20200730104253-651201b0f516/go.mod h1:QNYViu/X0HXDHw7m3KXzWSVXIbfUvJqBFe6Gj8/pYA0=
github.com/apache/arrow/go/arrow v0.0.0-20211112161151-bc219186db40 h1:q4dksr6ICHXqG5hm0ZW5IHyeEJXoIJSOZeBLmWPNeIQ=
github.com/apache/arrow/go/arrow v0.0.0-20211112161151-bc219186db40/go.mod h1:Q7yQnSMnLvcXlZ8RV+jwz/6y1rQTqbX6C82SndT52Zs=
Expand Down Expand Up @@ -624,8 +624,8 @@ github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYs
github.com/klauspost/compress v1.13.1/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.15.1/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
github.com/klauspost/compress v1.18.6 h1:2jupLlAwFm95+YDR+NwD2MEfFO9d4z4Prjl1XXDjuao=
github.com/klauspost/compress v1.18.6/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/klauspost/compress v1.19.0 h1:sXLILfc9jV2QYWkzFOPWStmcUVH2RHEB1JCdY2oVvCQ=
github.com/klauspost/compress v1.19.0/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.1.0/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
Expand Down Expand Up @@ -708,8 +708,8 @@ github.com/montanaflynn/stats v0.9.0 h1:tsBJ0RXwph9BmAuFoCmqGv6e8xa0MENQ8m0ptKq2
github.com/montanaflynn/stats v0.9.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/nats-io/jwt/v2 v2.8.2 h1:XXRgB60MSTnqsRwejQurVDs/hcv2dkt+86GjI+I/bMc=
github.com/nats-io/jwt/v2 v2.8.2/go.mod h1:Ag/56sq9OblL4JgdYufDd16Egb17Kr/8WwwuO/forVc=
github.com/nats-io/nats-server/v2 v2.14.3 h1:+xjydPt7rkit67G+04TN0mcO2n+8nveZE7tK/PPV53A=
github.com/nats-io/nats-server/v2 v2.14.3/go.mod h1:5IlCtBzfwyzQzPMjmoJ9W2/LKmnJRtNyuOs/OT+NHDY=
github.com/nats-io/nats-server/v2 v2.14.4 h1:efgjZ8cdExAKRuqSg8UPJFprb+l7NlBtSDPhDlw3rO4=
github.com/nats-io/nats-server/v2 v2.14.4/go.mod h1:BltdpOYestjbtQSnVO2zGHdg5SGBZjt+GYTgB9LZq/I=
github.com/nats-io/nats.go v1.52.0 h1:n3avV4VBsCgsdwh71TppsTwtv+QdPs7ntSKM8qJLGsc=
github.com/nats-io/nats.go v1.52.0/go.mod h1:26HypzazeOkyO3/mqd1zZd53STJN0EjCYF9Uy2ZOBno=
github.com/nats-io/nkeys v0.4.16 h1:rd5oAuLOb8mnAycB0xleuEBNS1pVVnN0fv/FF34Eypg=
Expand Down Expand Up @@ -779,8 +779,8 @@ github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiT
github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=
github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo=
github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs=
github.com/spiffe/go-spiffe/v2 v2.7.0 h1:uXe1MflJoHw58wAUvxVlcM7WpKtijWG7I1UidcGh6g4=
github.com/spiffe/go-spiffe/v2 v2.7.0/go.mod h1:47Q0Q9/AqGha8QLHp+kxpH4Wca7X7EnOtlIJy3mxZ3U=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
Expand Down Expand Up @@ -859,8 +859,8 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/detectors/gcp v1.43.0 h1:62yY3dT7/ShwOxzA0RsKRgshBmfElKI4d/Myu2OxDFU=
go.opentelemetry.io/contrib/detectors/gcp v1.43.0/go.mod h1:RyaZMFY7yi1kAs45S6mbFGz8O8rqB0dTY14uzvG4LCs=
go.opentelemetry.io/contrib/detectors/gcp v1.44.0 h1:NmLfL734pJhM0JKaYd2Y28+nY9dPRWYAAbxhRCrKXPw=
go.opentelemetry.io/contrib/detectors/gcp v1.44.0/go.mod h1:tNAsgd8avTGke1+MndXlU5Cru4PQ9Ai/cCNWQv/ZJ/s=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 h1:0Qx7VGBacMm9ZENQ7TnNObTYI4ShC+lHI16seduaxZo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0/go.mod h1:Sje3i3MjSPKTSPvVWCaL8ugBzJwik3u4smCjUeuupqg=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY=
Expand Down Expand Up @@ -1446,8 +1446,8 @@ google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9K
google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
google.golang.org/grpc v1.82.1 h1:NnAxzGRA0677vCa4BUkOAnO5+FfQqVl9iUXeD0IqcGE=
google.golang.org/grpc v1.82.1/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA=
google.golang.org/grpc v1.83.0 h1:JeNZEKJFbQxArAMl+hiytHauacDNqJUllNfmIMmpqnQ=
google.golang.org/grpc v1.83.0/go.mod h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
Expand Down
2 changes: 1 addition & 1 deletion sdks/python/apache_beam/dataframe/io.py
Original file line number Diff line number Diff line change
Expand Up @@ -736,7 +736,7 @@ def open(self, file_handle):
self.empty = self.header = self.footer = None
if not self.binary:
file_handle = TextIOWrapper(
file_handle, encoding=self.kwargs.get("encoding", None))
file_handle, encoding=self.kwargs.get("encoding", None), newline='')
self.file_handle = file_handle

def write_to(self, df, file_handle=None):
Expand Down
15 changes: 8 additions & 7 deletions sdks/python/apache_beam/dataframe/io_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import importlib
import math
import os
import platform
import shutil
import tempfile
import typing
Expand Down Expand Up @@ -65,9 +64,6 @@ class MyRow(typing.NamedTuple):
value: int


@unittest.skipIf(
platform.system() == 'Windows',
'https://github.com/apache/beam/issues/20642')
class IOTest(unittest.TestCase):
def setUp(self):
self._temp_roots = []
Expand Down Expand Up @@ -431,6 +427,11 @@ def test_file_not_found(self):

def test_windowed_write(self):
output = self.temp_dir()

def no_colon_file_naming(*args):
file_name = fileio.default_file_naming('out.csv')(*args)
return file_name.replace(':', '_')

with beam.Pipeline() as p:
pc = (
p | beam.Create([MyRow(timestamp=i, value=i % 3) for i in range(20)])
Expand All @@ -440,18 +441,18 @@ def test_windowed_write(self):
beam.window.FixedWindows(10)).with_output_types(MyRow))

deferred_df = convert.to_dataframe(pc)
deferred_df.to_csv(output + 'out.csv', index=False)
deferred_df.to_csv(output, file_naming=no_colon_file_naming, index=False)

first_window_files = (
f'{output}out.csv-'
f'{datetime.utcfromtimestamp(0).isoformat()}*')
f'{datetime.utcfromtimestamp(0).isoformat().replace(":", "_")}*')
self.assertCountEqual(
['timestamp,value'] + [f'{i},{i % 3}' for i in range(10)],
set(self.read_all_lines(first_window_files, delete=True)))

second_window_files = (
f'{output}out.csv-'
f'{datetime.utcfromtimestamp(10).isoformat()}*')
f'{datetime.utcfromtimestamp(10).isoformat().replace(":", "_")}*')
self.assertCountEqual(
['timestamp,value'] + [f'{i},{i%3}' for i in range(10, 20)],
set(self.read_all_lines(second_window_files, delete=True)))
Expand Down
4 changes: 4 additions & 0 deletions sdks/python/apache_beam/options/pipeline_options.py
Original file line number Diff line number Diff line change
Expand Up @@ -1743,6 +1743,10 @@ def validate(self, validator):
_LOGGER.info(
'Setting --profile_location to %s since profiling is enabled.',
self.profile_location)

if self.profiler_agent == 'coredump':
debug_options = self.view_as(DebugOptions)
debug_options.add_experiment('core_pattern=/tmp/beam_coredump.%e.%p')
return errors


Expand Down
9 changes: 9 additions & 0 deletions sdks/python/apache_beam/options/pipeline_options_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -702,6 +702,15 @@ def test_profiling_agent_is_exclusive_with_legacy_profiling_options(self):
self.assertTrue(
any('--profiler_agent is mutually exclusive' in err for err in errors))

def test_profiling_agent_coredump_adds_core_pattern(self):
options = PipelineOptions(['--profiler_agent=coredump'])
validator = PipelineOptionsValidator(options, None)
self.assertEqual(validator.validate(), [])
debug_options = options.view_as(DebugOptions)
self.assertEqual(
debug_options.lookup_experiment('core_pattern'),
'/tmp/beam_coredump.%e.%p')

def test_profile_location_defaulting_and_opt_out(self):
options = PipelineOptions(
['--profiler_agent=memray', '--temp_location=gs://bucket/temp'])
Expand Down
1 change: 1 addition & 0 deletions sdks/python/container/base_image_requirements_manual.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ guppy3
memray==1.19.3
mmh3 # Optimizes execution of some Beam codepaths. TODO: Make it Beam's dependency.
nltk # Commonly used for natural language processing.
pystack
google-crc32c
scipy
scikit-learn
Expand Down
55 changes: 37 additions & 18 deletions sdks/python/container/boot.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,9 @@ var (
provisionEndpoint = flag.String("provision_endpoint", "", "Provision endpoint (required).")
controlEndpoint = flag.String("control_endpoint", "", "Control endpoint (required).")
semiPersistDir = flag.String("semi_persist_dir", "/tmp", "Local semi-persistent directory (optional).")

workerMu sync.Mutex
shuttingDown bool
)

const (
Expand Down Expand Up @@ -307,19 +310,12 @@ func launchSDKProcess() error {

workerIds := append([]string{*id}, info.GetSiblingWorkerIds()...)

// Keep track of child PIDs for clean shutdown without zombies
childPids := struct {
v []int
canceled bool
mu sync.Mutex
}{v: make([]int, 0, len(workerIds))}

// Forward trapped signals to child process groups in order to terminate them gracefully and avoid zombies
go func() {
logger.Printf(ctx, "Received signal: %v", <-signalChannel)
childPids.mu.Lock()
childPids.canceled = true
for _, pid := range childPids.v {
workerMu.Lock()
shuttingDown = true
for _, pid := range activePids {
go func(pid int) {
// This goroutine will be canceled if the main process exits before the 5 seconds
// have elapsed, i.e., as soon as all subprocesses have returned from Wait().
Expand All @@ -330,7 +326,7 @@ func launchSDKProcess() error {
}(pid)
syscall.Kill(-pid, syscall.SIGTERM)
}
childPids.mu.Unlock()
workerMu.Unlock()
}()

var wg sync.WaitGroup
Expand All @@ -342,9 +338,9 @@ func launchSDKProcess() error {
bufLogger := tools.NewBufferedLogger(logger)
errorCount := 0
for {
childPids.mu.Lock()
if childPids.canceled {
childPids.mu.Unlock()
workerMu.Lock()
if shuttingDown {
workerMu.Unlock()
return
}

Expand All @@ -369,8 +365,9 @@ func launchSDKProcess() error {

logger.Printf(ctx, "Executing Python (%v): %v %v", envStr, currentProg, strings.Join(currentArgs, " "))
cmd := StartCommandEnv(currentEnv, os.Stdin, bufLogger, bufLogger, currentProg, currentArgs...)
childPids.v = append(childPids.v, cmd.Process.Pid)
childPids.mu.Unlock()
logger.Printf(ctx, "Started worker %s with PID %d", workerId, cmd.Process.Pid)
activePids = append(activePids, cmd.Process.Pid)
workerMu.Unlock()

var timer *time.Timer
var profilingTimedOut atomic.Bool
Expand All @@ -379,8 +376,8 @@ func launchSDKProcess() error {
if profilingActive && pcfg.StopAfterSec > 0 {
duration := time.Duration(pcfg.StopAfterSec) * time.Second
timer = time.AfterFunc(duration, func() {
childPids.mu.Lock()
defer childPids.mu.Unlock()
workerMu.Lock()
defer workerMu.Unlock()
if cmd.Process != nil {
logger.Printf(ctx, "Profiling timeout of %d seconds reached. Sending SIGINT to worker %s",
pcfg.StopAfterSec, workerId)
Expand All @@ -391,6 +388,7 @@ func launchSDKProcess() error {
}

err := cmd.Wait()
unregisterPid(cmd.Process.Pid)
if timer != nil {
timer.Stop()
}
Expand All @@ -417,6 +415,7 @@ func launchSDKProcess() error {
logger.Warnf(ctx, "Python (worker %v) exited %v times: %v\nrestarting SDK process",
workerId, errorCount, err)
} else {
cleanUpProfiler(ctx, logger)
logger.Fatalf(ctx, "Python (worker %v) exited %v times: %v\nout of retries, failing container",
workerId, errorCount, err)
}
Expand Down Expand Up @@ -595,3 +594,23 @@ func logSubmissionEnvDependencies(ctx context.Context, bufLogger *tools.Buffered
bufLogger.Printf(ctx, "Dependencies in submission environment:\n%s", string(content))
return nil
}

var (
activePids []int
)

func unregisterPid(pid int) {
workerMu.Lock()
defer workerMu.Unlock()
activePids = slices.DeleteFunc(activePids, func(p int) bool {
return p == pid
})
}

func getActivePids() []int {
workerMu.Lock()
defer workerMu.Unlock()
pids := make([]int, len(activePids))
copy(pids, activePids)
return pids
}
1 change: 1 addition & 0 deletions sdks/python/container/ml/py310/base_image_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ PyMySQL==1.2.0
pyOpenSSL==26.2.0
pyparsing==3.3.2
pyproject_hooks==1.2.0
pystack==1.7.0
pytest==9.1.1
pytest-timeout==2.4.0
pytest-xdist==3.8.0
Expand Down
1 change: 1 addition & 0 deletions sdks/python/container/ml/py310/gpu_image_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,7 @@ PyMySQL==1.2.0
pyOpenSSL==26.2.0
pyparsing==3.3.2
pyproject_hooks==1.2.0
pystack==1.7.0
pytest==9.1.1
pytest-timeout==2.4.0
pytest-xdist==3.8.0
Expand Down
1 change: 1 addition & 0 deletions sdks/python/container/ml/py311/base_image_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,7 @@ PyMySQL==1.2.0
pyOpenSSL==26.2.0
pyparsing==3.3.2
pyproject_hooks==1.2.0
pystack==1.7.0
pytest==9.1.1
pytest-timeout==2.4.0
pytest-xdist==3.8.0
Expand Down
Loading
Loading