We are happy to present the new 2.78.0 release of Beam.
This release includes both improvements and new functionality.
See the download page for this release.
For more information on changes in 2.78.0, check out the detailed release notes.
Highlights
I/Os
- Added a SchemaTransform for SplunkIO writes, available in Beam YAML as
WriteToSplunk(Java/YAML) (#40470). - (Java) FirestoreIO: added
RpcQosOptions.Builder.withRampupThrottlingDisabled(), mirroring the existing DatastoreIO option, to opt out of the 500/50/5 write ramp-up (#27170). - Added support for attributing BigQuery API quota and billing to a specific GCP project (quota project):
quota_project_idparameter inReadFromBigQueryor--bigquery_quota_project_idpipeline option, honored by the Python BigQuery IO transforms (WriteToBigQuerywithFILE_LOADSorSTREAMING_INSERTS; not yetSTORAGE_WRITE_API, which is Java-backed),--bigQueryQuotaProjectIdpipeline option (Java), andbigqueryio.WithQuotaProjectread/query option (Go) (#37431). - (Java/Python/Go) DebeziumIO:
readAsJson()can now includeopand the remaining Debeziumsourcefields (e.g. MySQLfile,pos,row,ts_ms; PostgreSQLlsn,txId,ts_ms) inmetadataviawithFullMetadata(true)/full_metadata=True/debeziumio.FullMetadata(). Off by default (#40701). - [IcebergIO] AddFiles is now available through the Managed API with
Managed.write(Managed.ICEBERG_ADD_FILES)(Java/Python) (#40827). - (Java) IcebergIO:
AddFilescan register files in several tables in one transform, naming each file's table from its path. Setpath_pattern(withPathPatternin Java) to a glob with{name}placeholders, such asgs://lake/export/{table}/**, and maketablea template over what it captures, such asdefault.{table}. The schema pre-pass then runs per table, so a table whose files conflict is refused and its files reported while every other table is still registered. Requiresschema_evolution_optionsanderror_handling; not supported withdry_run. Error rows gain atablefield only whenpath_patternis set, so an existing pipeline's error output is unchanged (#40849).
New Features / Improvements
- (Java) Flink runner: sources now forward Beam metrics to Flink at most once per second instead of after every record, which was a significant share of the CPU of reading. Metrics in the pipeline result are not affected (#40607).
- (Go) Added
wait.On, which delays each input window until the corresponding windows in its signal PCollections have closed (#39909). - (Python) Expanded the SDK worker heap dump (
--experiments=enable_heap_dump) with process RSS, CPython allocator/GC stats, and glibcmallinfo2native-heap/fragmentation stats to help distinguish native-heap from Python-object memory growth (#39244). - (Python) Added typed decision questions and the
EvaluateDecisionstransform, with an optional Jev model adapter (#40220). - The
disableCounterMetrics,disableStringSetMetricsanddisableBoundedTrieMetricsexperiments are now honored by the Python SDK, as they already were in Java (Python) (#38746). - (Go) Pipeline-level
--resource_hintsare now forwarded to expansion services, so they apply to cross-language transforms as they do in the Java and Python SDKs. Themax_active_bundles_per_workerhint name is now also accepted by--resource_hints(#23893). QdrantWriteConfigcan now optionally store chunk content in the point payload viacontent_payload_key, so retrieved points include the chunk text (Python) (#40476).- ReadFromBigQuery now supports Lakehouse runtime catalog (BigLake metastore) tables with
method=DIRECT_READ, using 4-partproject.catalog.namespace.tableidentifiers. Previouslyproject:catalog.namespace.tablewas silently mis-parsed and tables that report nonumBytesfailed to split (Python) (#39597). - (Python) Added
AwsOptionswith--aws_*pipeline options (e.g.--aws_access_key_id,--aws_region_name,--aws_endpoint_url) that set defaults for all AWS service clients. The existing--s3_*options inS3Optionsoverride these defaults for the S3 client only (#40528). - (Python) Added
--aws_credentials_secret_managerso AWS credentials (--aws_access_key_id,--aws_secret_access_key,--aws_session_tokenand their--s3_*overrides) can be secret specs resolved through a secret manager such asGoogleCloudSecretManager, instead of plain-text values (#40528). - (Python) Added
AwsSecret(AwsSecretsManager/type:awssecret) to retrieve secrets from AWS Secrets Manager, configured fromAwsOptions(--aws_*) with optional per-secret connection overrides (#40528). - (Go) Added
schema.RegisterLogicalTypeConversionandschema.RegisterLogicalTypeCoderto register portable logical types in one call, and Go types for the standardbeam:logical_type:date:v1andbeam:logical_type:micros_instant:v1logical types, so schemas from other SDKs with these types can be used in cross-language pipelines (#39684). - (Python, Java, Go) Added
--dataflow_container_base_repository(Python, Go) and--dataflowContainerBaseRepository(Java) to set the repository (e.g.docker.example.com/my-prefix/cloud-dataflow/v1beta3) from which the Dataflow runner pulls its default SDK container images, including the default images of cross-language Apache Beam environments. Defaults togcr.io/cloud-dataflow/v1beta3(#40569). - (Go) GCS and Dataflow clients now support non-default universes such as Trusted Partner Cloud (TPC), using the universe domain from
GOOGLE_CLOUD_UNIVERSE_DOMAINor the default credentials. Added a--gcs_endpointpipeline option to override the GCS endpoint (#40569). - (Java)
GcsUtilnow uses the java-storage client (GcsUtil V2) for GCS operations by default instead of gcsio (GcsUtil V1). To go back to V1, pass--experiments=disable_gcsutil_v2. Pipelines with theuse_grpc_for_gcsexperiment keep using V1. Theuse_gcsutil_v2experiment is no longer needed and is now ignored (#40712).
Breaking Changes
- (Java) Removed Beam SQL Google Cloud Data Catalog table provider (
beam-sdks-java-extensions-sql-datacatalog) because GCP Data Catalog has been decommissioned (#40663). - (Go) The row coder now encodes
int16anduint16struct fields as 2 byte big endian INT16 values, matching the Java and Python SDKs. This is an update incompatible change for streaming pipelines that use rows withint16oruint16fields (#40151). - (Java) JmsIO migrated to
jakarta.jms(JMS 3.1) and ActiveMQ 6.2.5. User code implementingJmsIO.MessageMapper,
valueMapper,topicNameMapper, or providing aConnectionFactorymust update imports fromjavax.jms.*to
jakarta.jms.*. The module now requires Java 17 at runtime (#38729). - (Java) Dataflow runner:
--apiRootUrlno longer defaults tohttps://dataflow.googleapis.com/. When neither--apiRootUrlnor an absolute--dataflowEndpointis set, the Google API client library resolves the Dataflow endpoint and honorsGOOGLE_CLOUD_UNIVERSE_DOMAIN, so Dataflow jobs can be submitted in non-default universes. Code that readsDataflowPipelineDebugOptions.getApiRootUrl()should handle anullvalue (#40569). - (Python)
Regex.find_allwithgroup=Regex.ALLnow returns every group, as(match, group1, group2, ...), instead of only group 1, and withoutputEmpty=Falseit drops a match only when the whole match is empty, matching thegroup=0behavior (#40399). - (Python)
Regex.all_matchesnow returns every group of the regex, instead of failing with aTypeErrorwhen the regex has no groups and dropping nested or unmatched trailing groups (#40562). - (Python)
--dataflow_endpointnow defaults to''instead ofhttps://dataflow.googleapis.com. When unset, the Google Cloud client library resolves the Dataflow endpoint and honorsGOOGLE_CLOUD_UNIVERSE_DOMAIN, so Dataflow jobs can be submitted in non-default universes. Code that readsGoogleCloudOptions.dataflow_endpointshould handle an empty value (#40569). - (Python) The
xgboostextras now installxgboost>=2.0.0,<4anddatatable>=1.1.0,<2, moving both the lower and upper bounds for both. The previous minimumxgboostanddatatableversions (1.6.0and1.0.0respectively) are no longer allowed (#40694). - (Java) [IcebergIO] AddFiles (
IcebergAddFilesin YAML) now carries the destination table alongside each file through the registration chain, so that one transform can register files into many tables. The coders of its internalPCollections change, which makes this an update incompatible change for streaming pipelines that use it (#40817). - (YAML)
IcebergAddFileswill be automatically replaced with the compatible managed transform. For older Beam versions, streaming update compatibility can be maintained by specifying the pipeline optionupdate_compatibility_version(#40827). - (Java) [IcebergIO] AddFiles now collapses batch input into the global window. Manifests and snapshots are sized per key and window, so windowing applied upstream of AddFiles only divided
manifest_file_sizeinto smaller manifests and more snapshots; its outputs are now globally windowed in batch. Streaming pipelines keep the windowing they had (#40849). - Twister2 runner supported is removed (#35905)).
Deprecations
- Support for Python 3.10 was removed following its end-of-life (#40250).
- (Python)
XGBoostModelHandlerDatatableis deprecated and will be removed in Beam 2.79.0, along with thedatatabledependency of thexgboostextra.datatablehas not had a release since 2023 and has no wheels for Python 3.13+. UseXGBoostModelHandlerPandas(or the NumPy/SciPy handlers) instead (#40705).
Bugfixes
- (Python) Fixed timestamp and side-input argument binding when a
DoFn.processorDoFn.process_batchmethod gives its element or batch parameter a default value (#19631). - (Python) Fixed BigQuery
FILE_LOADSsilently omitting files when dynamic destinations use different spellings of the same table (#40001). - (Python) DaskRunner now preserves outputs emitted by
DoFn.finish_bundle()instead of silently dropping them (#40519). - (Python) BigQuery streaming inserts now reset the retry budget and backoff for each batch instead of sharing them across a bundle (#39995).
- (Python) State write failures reported by portable runners now fail the bundle instead of silently losing state updates (#39992).
- (Python) Type inference now gives the right type for a slice of a fixed-size tuple, such as
row[1:]inbeam.Map(lambda row: row[1:]). The slice used to keep the type of the whole tuple, which could make a followingCombinePerKeyfail with aTypeCheckErroror encode elements with the wrong coder (#20899). - (Go) Fixed a data race on the Prism runner's artifact cache map in JobServices (#32656).
- (Go) Fixed the harness leaking Data/State gRPC streams after the worker stops, and a deadlock when Send returns EOF (#40260).
- (Go) Wrap last dataWriter/timerWriter send errors on Close (#40405).
- (Go) Fixed a deadlock recreating a data channel while holding the channel lock (#40414).
- (Python) Fixed
KeyedModelHandlernot enforcingmax_models_per_worker_hintwhen several copies of the model handler run in the same SDK process (#40468). - (Python) Fixed a bug where
RunInferencewas serving the first batch on each worker with the original models instead of the paths from aKeyModelPathMappingside input (#40665). - (Python) Fixed a bug in
RunInferencewhere adaptive throttling in remote inference codepath was accidentally disabled (#40598). - (Python) Fixed an inefficiency in
RunInferencethat caused unnecessary model reloading in some cases whenlarge_model=Truewas not set (#40441). - (Java) Fixed the declared schema of the error output of the Kafka write SchemaTransform, which wrapped the error schema a second time and did not match the rows it emits (#39760).
- (Go) Fixed pubsubio importing a
google.golang.org/genprotopackage removed in recent releases, which broke builds of Go modules depending on a currentgenprotoversion (#40018). - (Python)
VertexAIFeatureStoreEnrichmentHandlernow logs a warning instead of failing when the caller lacks permission to read the feature store metadata (#40442). - (Java) BigQueryIO now treats a 404 when deleting a temporary table or dataset as success, so a replayed work item whose earlier attempt already deleted it no longer retries forever (#24997).
- (Java) Fixed BigQuery Storage Write API failure outputs returning the wrong original row after consecutive serialization errors (#39999).
- (Java) IcebergIO now writes rows containing
EnumerationType(proto enum) fields as strings, instead of throwingUnsupported Beam logical type Enum(#40299). - (Python) Fixed stateful DoFns with side inputs sometimes taking the timer key coder from a side input instead of the main input, which could make the worker fail to decode timer keys with
Unknown type tag(#40374). - (Python) Fixed
AsyncWrapperfinalizing bundles before generator processing, which could drop buffered writes, and nesting iterable outputs (#40000). - (Python)
Durationbuilt from float seconds now rounds to the nearest microsecond instead of truncating, which could lose a microsecond (#40263). - (Python)
ReadFromTextwithescapecharno longer skips a delimiter that directly follows an escaped delimiter, which merged two records into one (#40459). - (Python) DataFrame API:
aggwith a list that includes'count'or'size'now labels those rowscount/sizeinstead ofsum(#40504). - (Python)
ConcatSourceno longer raisesZeroDivisionErrorwhen every source bundle reports a weight of 0, e.g. bundles smaller than 1 MB (#21316). - (Python) DataFrame API:
Series.value_counts(bins=...)no longer always fails with aTypeError(#40503). - (Python)
ReadBigQueryChangeHistoryno longer fails at pipeline construction withTypeCheckError: The transform 'ExecuteQueries' requires PCollections of type ... _QueryRangewhen pipeline type checking is enabled (the default); a regression in 2.76.0 (#40618). - (Python)
ReadBigQueryChangeHistorywithchange_function='CHANGES'no longer fails reading its temp table with a Storage Read API500 InternalServerError: the_CHANGE_IS_FOR_UPDATEpseudo-column is now excluded from the query instead of being written to the temp table (#40618). - (Python)
ReadBigQueryChangeHistorynow backtick-quotes the column names it puts in the generated CHANGES/APPENDS query (columns,change_type_column,change_timestamp_column), so reserved words and names with spaces work, andcolumns=[]selects only the pseudo-columns instead of generating invalid SQL (#40618). - (Python) Setting
max_batch_weighton a RunInferenceModelHandlerno longer fails with aTypeError:BatchElementsnow supportsmax_batch_weight, which limits the totalelement_size_fnof a batch in addition to its number of elements (#40644). - (Python)
BatchElementsnow honorselement_size_fnwhenmax_batch_duration_secsis set, and no longer emits an empty batch before an element that exceeds the batch size on its own (#40644). - (Python)
KeyedModelHandlerandMaybeKeyedModelHandlernow apply theelement_size_fnandbatch_length_fnof the wrappedModelHandlerto examples, instead of to(key, example)pairs (#40644). - (Java) SplunkIO write errors no longer lose their status code, status message and payload when encoded. The schema inferred for
SplunkWriteErrorhad no fields, so it now has a hand-written schema withstatusCode,statusMessageandpayload(#40470). - (Python) FnApiRunner now length prefixes timer key coders it doesn't know, as it already did for PCollection coders, so cross-language stateful DoFns with timers keyed by e.g. Java's
VarIntCoderno longer fail (#40470). - (Java) Portable runners no longer deadlock when a single bundle produces more output than the Fn API data buffers hold (about 1 GB with the Python SDK, e.g. one Flink batch partition). The runner now consumes a bundle's output before waiting for its
ProcessBundleResponse(#40720). - (Python) DataFrame API:
drop_duplicates,duplicatedandnuniqueno longer fail on rows with NaN values or on categorical columns with unused categories, andnunique(dropna=False)now counts NaN (#40570). - (Java) BigQuery Storage Write API
TableRowconversion now honorsallowMissingRequiredFieldswhen building protocol-buffer messages, so callers that passtruewith required descriptors get partial messages instead of a conversion-timeUninitializedMessageException(#40189). - (Go) Fixed generic registration (for example
register.Function1x1orregister.DoFn1x1) panicking on functions and methods that take or return an anonymous struct. Unnamed struct types are now only registered with the schema registry, since they have no type key for the runtime type registry (#21789).
According to git shortlog, the following people contributed to the 2.78.0 release. Thank you to all contributors!
Abdelrahman Ibrahim, Ahmed Abualsaud, Ali Ebrahim, Amir Fathi, Anavay Sharma, Andrew Crites, Anish Mehta, Arun Pandian, Ashwin S, Ayoub, Balázs Németh, Biao Geng, Bruno Volpato, CH-Abhinav, CarloMariaProietti, Chamikara Jayalath, Chetan Ruparel, Claude, Clay Johnson, Danish Amjad, Danny McCormick, Deepika Mamidipelly, Derrick Williams, Dhruv Dagar, DiezNobach, Divyansh Yadav, Elia LIU, Elia Liu, Florent Biville, GAURAV THAKUR, Ganesh Sivakumar, Goutam Adwant, Igor, Jack McCluskey, Jan Lukavský, Jean-Baptiste Onofré, Jiang Zhu, Julien Tournay, Kenneth Knowles, Khaled Hammouda, Maksym Tymoshyk, Malo Denielou, Manan Mangal, Mazlum Tosun, Nicolas Gibanel, Nitin Ware, Pablo Villanueva, Parv Arhatia, Paulius Kuzmickas, Radosław Stankiewicz, Rakesh Pai, Reuven Lax, RuiLong J., Ryan McGinnis, Sam Whittle, Shahar Epstein, Shunping Huang, Steven van Rossum, Tobias Kaymak, Udaya Chathuranga, Udit Jain, Utkarsh Bhangale, Valentyn Tymofieiev, Valient Gough, Vishal More, Vitaly Terentyev, Wangshu, XuJian, Yashvardhan Thanvi, Yi Hu, Yusuf Quazi, ZIHAN DAI, akshayjadiyanv, alisha-1000, claudevdm, darshan-sj, ddebowczyk92, ilyas ahsan, junaiddshaukat, mola, nitinware, pablo rodriguez defino, parveensania, raams95, singhmrinal02, waneal