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
1 change: 1 addition & 0 deletions .asf.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ github:

protected_branches:
master: {}
release-2.75: {}
release-2.74.0-postrelease: {}
release-2.74: {}
release-2.73.0-postrelease: {}
Expand Down
2 changes: 1 addition & 1 deletion .github/trigger_files/beam_PostCommit_Go_VR_Flink.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"comment": "Modify this file in a trivial way to cause this test suite to run",
"modification": 2,
"modification": 3,
"https://github.com/apache/beam/pull/32440": "testing datastream optimizations",
"pr": "37640"
}
2 changes: 1 addition & 1 deletion .github/trigger_files/beam_PostCommit_XVR_Flink.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"modification": 2,
"modification": 3,
"trigger-2026-04-04": "portable_runner expand_sdf opt-in"
}
1 change: 0 additions & 1 deletion .github/trigger_files/beam_PostCommit_XVR_Samza.json

This file was deleted.

5 changes: 4 additions & 1 deletion .github/workflows/cut_release_branch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,10 @@ jobs:
git config user.name $GITHUB_ACTOR
git config user.email actions@"$RUNNER_NAME".local
- name: Install xmllint
run: sudo apt-get install -y libxml2-utils
run: |
sudo apt-get clean
sudo apt-get update
sudo apt-get install -y --no-install-recommends libxml2-utils
- name: Update .asf.yaml to protect new release branch from force push
run: |
sed -i -e "s/master: {}/master: {}\n release-${RELEASE}: {}/g" .asf.yaml
Expand Down
4 changes: 2 additions & 2 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,8 @@
* (Java) Enabled state tag encoding v2 by default for new Dataflow Streaming Engine jobs. It can be disabled by passing `--experiments=disable_streaming_engine_state_tag_encoding_v2` or `--updateCompatibilityVersion=2.74.0` pipeline option. Note that the tag encoding version cannot change during a job update. Jobs using tag encoding v2 (enabled by default for new jobs on 2.75.0+) cannot be downgraded to Beam versions prior to 2.73.0, as only versions 2.73.0 and later support tag encoding v2. ([#38705](https://github.com/apache/beam/issues/38705)).
* (Python) Added instrumentation to support off-the-shelf profiling agents when launching Python SDK Harness ([#38853](https://github.com/apache/beam/issues/38853)).
* (Java) Added support to the FnApi Data stream protocol allowing runners to isolate bundles slowly processing input from other bundles. ([#39001](https://github.com/apache/beam/issues/39001)).
* (Java) Flink 2.1 support added ([#38947](https://github.com/apache/beam/issues/38947)).
* (Java) Flink 2.2 support added ([#38978](https://github.com/apache/beam/issues/38978)).
* (Java) Flink 2.1 and 2.2 support is added ([#38947](https://github.com/apache/beam/issues/38947)) ([#38978](https://github.com/apache/beam/issues/38978)); Flink 1.17 and 1.18 support is dropped.
* (Python) MqttIO is now supported in Python via cross-language ([#21060](https://github.com/apache/beam/issues/21060)).

## Breaking Changes

Expand Down
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ signing.gnupg.useLegacyGpg=true
# buildSrc/src/main/groovy/org/apache/beam/gradle/BeamModulePlugin.groovy.
# To build a custom Beam version make sure you change it in both places, see
# https://github.com/apache/beam/issues/21302.
version=2.75.0-SNAPSHOT
sdk_version=2.75.0.dev
version=2.76.0-SNAPSHOT
sdk_version=2.76.0.dev

javaVersion=11

Expand Down
20 changes: 18 additions & 2 deletions runners/flink/job-server/flink_job_server.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -248,10 +248,26 @@ def setupTask = project.tasks.register("flinkJobServerSetup", Exec) {
def flinkJobServerJar = shadowJar.archivePath
def flinkDir = project.project(":runners:flink").projectDir
def additionalArgs = ""
if (project.hasProperty('flinkConfDir'))

if (project.hasProperty('flinkConfDir')) {
additionalArgs += " --flink-conf-dir=${project.property('flinkConfDir')}"
else
}
else if (isFlink2) {
def flinkConfDir = "$flinkDir/2.0/src/test/resources"
additionalArgs += "--flink-conf-dir=${project.buildDir}/flink-conf"

doFirst {
copy {
from "$flinkDir/2.0/src/test/resources/flink-test-config.yaml"
into "${project.buildDir}/flink-conf"

// Rename the file during the copy process
rename 'flink-test-config.yaml', 'config.yaml'
}
}
} else {
additionalArgs += "--flink-conf-dir=$flinkDir/src/test/resources"
}

executable 'sh'
args '-c', "$pythonDir/scripts/run_job_server.sh stop --group_id ${project.name} && $pythonDir/scripts/run_job_server.sh start --group_id ${project.name} --job_port ${jobPort} --artifact_port ${artifactPort} --job_server_jar ${flinkJobServerJar} --additional_args \"${additionalArgs}\""
Expand Down
7 changes: 5 additions & 2 deletions runners/google-cloud-dataflow-java/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ evaluationDependsOn(":sdks:java:container:java11")

ext.dataflowLegacyEnvironmentMajorVersion = '8'
ext.dataflowFnapiEnvironmentMajorVersion = '8'
ext.dataflowLegacyContainerVersion = 'beam-master-20260601'
ext.dataflowFnapiContainerVersion = 'beam-master-20260601'
ext.dataflowLegacyContainerVersion = 'beam-master-20260624'
ext.dataflowFnapiContainerVersion = 'beam-master-20260624'
ext.dataflowContainerBaseRepository = 'gcr.io/cloud-dataflow/v1beta3'

processResources {
Expand Down Expand Up @@ -486,6 +486,9 @@ def validatesRunnerStreamingConfig = [
'org.apache.beam.sdk.transforms.ParDoLifecycleTest.testTeardownCalledAfterExceptionInSetupStateful',
'org.apache.beam.sdk.transforms.ParDoLifecycleTest.testTeardownCalledAfterExceptionInStartBundle',
'org.apache.beam.sdk.transforms.ParDoLifecycleTest.testTeardownCalledAfterExceptionInStartBundleStateful',
// Bundle finalizer tests are flaky https://github.com/apache/beam/issues/38710
'org.apache.beam.sdk.transforms.SplittableDoFnTest.testBundleFinalizationOccursOnBoundedSplittableDoFn',
'org.apache.beam.sdk.transforms.SplittableDoFnTest.testBundleFinalizationOccursOnUnboundedSplittableDoFn',
]
]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1061,6 +1061,12 @@ public static void main(String[] args) throws Exception {
}
});
LOG.info("Enabled Open Telemetry with properties: {}", openTelemetryProperties);
} else {
// turn off auth extension so it doesn't interfere if user is configuring otel e.g. via
// JvmInitializer.
if (System.getProperty("google.otel.auth.target.signals") == null) {
System.setProperty("google.otel.auth.target.signals", "none");
}
}

LOG.debug("Creating StreamingDataflowWorker from options: {}", options);
Expand Down
2 changes: 1 addition & 1 deletion scripts/beam-sql.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
set -e # Exit immediately if a command exits with a non-zero status.

# --- Configuration ---
DEFAULT_BEAM_VERSION="2.75.0"
DEFAULT_BEAM_VERSION="2.76.0"
MAIN_CLASS="org.apache.beam.sdk.extensions.sql.jdbc.BeamSqlLine"
# Directory to store cached executable JAR files
CACHE_DIR="${HOME}/.beam/cache"
Expand Down
2 changes: 1 addition & 1 deletion sdks/go/pkg/beam/core/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const (
// SdkName is the human readable name of the SDK for UserAgents.
SdkName = "Apache Beam SDK for Go"
// SdkVersion is the current version of the SDK.
SdkVersion = "2.75.0.dev"
SdkVersion = "2.76.0.dev"

// DefaultDockerImage represents the associated image for this release.
DefaultDockerImage = "apache/beam_go_sdk:" + SdkVersion
Expand Down
2 changes: 1 addition & 1 deletion sdks/go/test/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ task flinkValidatesRunner {
doFirst {
// Copy Flink conf file
copy {
from "${project.rootDir}/runners/flink/${flinkVersion}/src/test/resources/flink-test-config.yaml"
from "${project.rootDir}/runners/flink/2.0/src/test/resources/flink-test-config.yaml"
into "${project.buildDir}/flink-conf"

// Rename the file during the copy process
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -315,6 +315,12 @@ public static void main(
}
});
LOG.info("Enabled Open Telemetry with properties: {}", openTelemetryProperties);
} else {
// turn off auth extension so it doesn't interfere if user is configuring otel e.g. via
// JvmInitializer.
if (System.getProperty("google.otel.auth.target.signals") == null) {
System.setProperty("google.otel.auth.target.signals", "none");
}
}
EnumMap<
BeamFnApi.InstructionRequest.RequestCase,
Expand Down
162 changes: 80 additions & 82 deletions sdks/python/apache_beam/io/gcp/bigquery_file_loads.py
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,8 @@ class TriggerCopyJobs(beam.DoFn):
"""

TRIGGER_DELETE_TEMP_TABLES = 'TriggerDeleteTempTables'
# https://docs.cloud.google.com/bigquery/quotas#copy_jobs
MAX_SOURCES_PER_COPY_JOB = 1200

def __init__(
self,
Expand Down Expand Up @@ -528,96 +530,90 @@ def process(
self, element_list, job_name_prefix=None, unused_schema_mod_jobs=None):
if isinstance(element_list, tuple):
# Allow this for streaming update compatibility while fixing BEAM-24535.
self.process_one(element_list, job_name_prefix)
else:
for element in element_list:
self.process_one(element, job_name_prefix)
element_list = [element_list]

def process_one(self, element, job_name_prefix):
destination, job_reference = element
if not element_list:
return

copy_to_reference = bigquery_tools.parse_table_reference(destination)
first_destination = element_list[0][0]
copy_to_reference = bigquery_tools.parse_table_reference(first_destination)
if copy_to_reference.projectId is None:
copy_to_reference.projectId = vp.RuntimeValueProvider.get_value(
'project', str, '') or self.project

copy_from_reference = bigquery_tools.parse_table_reference(destination)
copy_from_reference.tableId = job_reference.jobId
if copy_from_reference.projectId is None:
copy_from_reference.projectId = vp.RuntimeValueProvider.get_value(
'project', str, '') or self.project

_LOGGER.info(
"Triggering copy job from %s to %s",
copy_from_reference,
copy_to_reference)
copy_from_references = []
for destination, job_reference in element_list:
copy_from_reference = bigquery_tools.parse_table_reference(destination)
copy_from_reference.tableId = job_reference.jobId
if copy_from_reference.projectId is None:
copy_from_reference.projectId = vp.RuntimeValueProvider.get_value(
'project', str, '') or self.project
copy_from_references.append(copy_from_reference)

wait_for_job, write_disposition = (
self._determine_write_disposition(copy_to_reference))
full_table_ref = bigquery_tools.get_hashable_destination(copy_to_reference)

if not self.bq_io_metadata:
self.bq_io_metadata = create_bigquery_io_metadata(self._step_name)
is_first_time = full_table_ref not in self._observed_tables
if is_first_time:
self._observed_tables.add(full_table_ref)
if self.bq_io_metadata:
Lineage.sinks().add(
'bigquery',
copy_to_reference.projectId,
copy_to_reference.datasetId,
copy_to_reference.tableId)

# Split into chunks of MAX_SOURCES_PER_COPY_JOB
chunks = [
copy_from_references[i:i + self.MAX_SOURCES_PER_COPY_JOB]
for i in range(
0, len(copy_from_references), self.MAX_SOURCES_PER_COPY_JOB)
]

copy_job_name_base = '%s_%s' % (
job_name_prefix,
_bq_uuid(bigquery_tools.get_hashable_destination(copy_to_reference)))

project_id = (
copy_to_reference.projectId
if self.load_job_project_id is None else self.load_job_project_id)
copy_job_name = '%s_%s' % (
job_name_prefix,
_bq_uuid(
'%s:%s.%s' % (
copy_from_reference.projectId,
copy_from_reference.datasetId,
copy_from_reference.tableId)))
job_reference = self.bq_wrapper._insert_copy_job(
project_id,
copy_job_name,
copy_from_reference,
copy_to_reference,
create_disposition=self.create_disposition,
write_disposition=write_disposition,
job_labels=self.bq_io_metadata.add_additional_bq_job_labels())

if wait_for_job:
self.bq_wrapper.wait_for_bq_job(job_reference, sleep_duration_sec=10)
self.pending_jobs.append(
GlobalWindows.windowed_value((destination, job_reference)))

def _determine_write_disposition(self, copy_to_reference) -> tuple[bool, str]:
"""
Determines the write disposition for a BigQuery copy job,
based on destination.

When the write_disposition for a job is WRITE_TRUNCATE, multiple copy jobs
to the same destination can interfere with each other, truncate data, and
write to the BigQuery table repeatedly. To prevent this, the first copy job
runs with the user's specified write_disposition, but subsequent jobs must
always use WRITE_APPEND. This ensures that subsequent copy jobs do not
clear out data appended by previous jobs.

Args:
copy_to_reference: The reference to the destination table.

Returns:
A tuple containing a boolean indicating whether to wait for the job to
complete and the write disposition to use for the job.
"""
full_table_ref = '%s:%s.%s' % (
copy_to_reference.projectId,
copy_to_reference.datasetId,
copy_to_reference.tableId)
if full_table_ref not in self._observed_tables:
write_disposition = self.write_disposition
wait_for_job = True
self._observed_tables.add(full_table_ref)
Lineage.sinks().add(
'bigquery',
copy_to_reference.projectId,
copy_to_reference.datasetId,
copy_to_reference.tableId)
else:
wait_for_job = False
write_disposition = 'WRITE_APPEND'
return wait_for_job, write_disposition
for i, chunk in enumerate(chunks):
if i == 0 and is_first_time:
write_disposition = self.write_disposition
# Wait inline only if we have multiple chunks and write disposition is WRITE_TRUNCATE or WRITE_EMPTY.
# This ensures the first chunk initializes the table, and subsequent chunks (WRITE_APPEND) append to it.
wait_for_job = (
self.write_disposition in ('WRITE_TRUNCATE', 'WRITE_EMPTY') and
len(chunks) > 1)
else:
write_disposition = 'WRITE_APPEND'
wait_for_job = False

chunk_job_name = copy_job_name_base
if len(chunks) > 1:
chunk_job_name = f"{copy_job_name_base}_{i}"

_LOGGER.info(
"Triggering copy job %s from %s to %s (write_disposition: %s)",
chunk_job_name, [str(r) for r in chunk],
copy_to_reference,
write_disposition)

job_reference = self.bq_wrapper._insert_copy_job(
project_id,
chunk_job_name,
chunk,
copy_to_reference,
create_disposition=self.create_disposition,
write_disposition=write_disposition,
job_labels=self.bq_io_metadata.add_additional_bq_job_labels()
if self.bq_io_metadata else None)

if wait_for_job:
self.bq_wrapper.wait_for_bq_job(job_reference, sleep_duration_sec=10)

self.pending_jobs.append(
GlobalWindows.windowed_value((first_destination, job_reference)))

def finish_bundle(self):
for windowed_value in self.pending_jobs:
Expand Down Expand Up @@ -744,7 +740,7 @@ def process(
else:
try:
schema = bigquery_tools.table_schema_to_dict(
bigquery_tools.BigQueryWrapper().get_table(
self.bq_wrapper.get_table(
project_id=table_reference.projectId,
dataset_id=table_reference.datasetId,
table_id=table_reference.tableId).schema)
Expand Down Expand Up @@ -855,7 +851,8 @@ def process(self, element):
if latest_partition.can_accept(file_size):
latest_partition.add(file_path, file_size)
else:
partitions.append(latest_partition.files)
if latest_partition.files:
partitions.append(latest_partition.files)
latest_partition = PartitionFiles.Partition(
self.max_partition_size, self.max_files_per_partition)
latest_partition.add(file_path, file_size)
Expand Down Expand Up @@ -1181,12 +1178,13 @@ def _load_data(
# the truncation happens only once. See
# https://github.com/apache/beam/issues/24535.
finished_temp_tables_load_job_ids_list_pc = (
finished_temp_tables_load_job_ids_pc | beam.MapTuple(
finished_temp_tables_load_job_ids_pc
| beam.MapTuple(
lambda destination, job_reference: (
bigquery_tools.parse_table_reference(destination).tableId,
bigquery_tools.get_hashable_destination(destination),
(destination, job_reference)))
| beam.GroupByKey()
| beam.MapTuple(lambda tableId, batch: list(batch)))
| beam.MapTuple(lambda dest, batch: list(batch)))
else:
# Loads can happen in parallel.
finished_temp_tables_load_job_ids_list_pc = (
Expand Down
Loading
Loading