You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@beam.apache.org by tv...@apache.org on 2026/04/03 17:17:21 UTC
(beam) branch master updated: Fix BigQueryEnrichmentHandler batch handling for duplicate keys (#38040)
This is an automated email from the ASF dual-hosted git repository.
tvalentyn pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/beam.git
The following commit(s) were added to refs/heads/master by this push:
new a03784efc93 Fix BigQueryEnrichmentHandler batch handling for duplicate keys (#38040)
a03784efc93 is described below
commit a03784efc939855f687f110fc8b99f0c646c40c8
Author: Prabhnoor Singh <pr...@gmail.com>
AuthorDate: Fri Apr 3 13:17:04 2026 -0400
Fix BigQueryEnrichmentHandler batch handling for duplicate keys (#38040)
* Fix BigQuery enrichment batch handling for duplicate keys
* Apply yapf formatting for BigQuery enrichment changes
---
.../transforms/enrichment_handlers/bigquery.py | 19 +++++----
.../enrichment_handlers/bigquery_test.py | 47 ++++++++++++++++++++++
2 files changed, 59 insertions(+), 7 deletions(-)
diff --git a/sdks/python/apache_beam/transforms/enrichment_handlers/bigquery.py b/sdks/python/apache_beam/transforms/enrichment_handlers/bigquery.py
index 115c5320767..2306d7d97a5 100644
--- a/sdks/python/apache_beam/transforms/enrichment_handlers/bigquery.py
+++ b/sdks/python/apache_beam/transforms/enrichment_handlers/bigquery.py
@@ -15,6 +15,7 @@
# limitations under the License.
#
import logging
+from collections import defaultdict
from collections.abc import Callable
from collections.abc import Mapping
from typing import Any
@@ -189,7 +190,7 @@ class BigQueryEnrichmentHandler(EnrichmentSourceHandler[Union[Row, list[Row]],
if isinstance(request, list):
values = []
responses = []
- requests_map: dict[Any, Any] = {}
+ requests_map: dict[Any, list[beam.Row]] = defaultdict(list)
batch_size = len(request)
raw_query = self.query_template
if batch_size > 1:
@@ -208,25 +209,29 @@ class BigQueryEnrichmentHandler(EnrichmentSourceHandler[Union[Row, list[Row]],
"Make sure the values passed in `fields` are the "
"keys in the input `beam.Row`." + str(e))
values.extend(current_values)
- requests_map[self.create_row_key(req)] = req
+ requests_map[self.create_row_key(req)].append(req)
query = raw_query.format(*values)
responses_dict = self._execute_query(query)
- unmatched_requests = requests_map.copy()
+ unmatched_requests = {
+ key: list(reqs)
+ for key, reqs in requests_map.items()
+ }
if responses_dict:
for response in responses_dict:
response_row = beam.Row(**response)
response_key = self.create_row_key(response_row)
if response_key in unmatched_requests:
- req = unmatched_requests.pop(response_key)
- responses.append((req, response_row))
+ for req in unmatched_requests.pop(response_key):
+ responses.append((req, response_row))
if unmatched_requests:
if self.throw_exception_on_empty_results:
raise ValueError(f"no matching row found for query: {query}")
else:
_LOGGER.warning('no matching row found for query: %s', query)
- for req in unmatched_requests.values():
- responses.append((req, beam.Row()))
+ for reqs in unmatched_requests.values():
+ for req in reqs:
+ responses.append((req, beam.Row()))
return responses
else:
request_dict = request._asdict()
diff --git a/sdks/python/apache_beam/transforms/enrichment_handlers/bigquery_test.py b/sdks/python/apache_beam/transforms/enrichment_handlers/bigquery_test.py
index 98ac6244910..98508baf661 100644
--- a/sdks/python/apache_beam/transforms/enrichment_handlers/bigquery_test.py
+++ b/sdks/python/apache_beam/transforms/enrichment_handlers/bigquery_test.py
@@ -15,9 +15,12 @@
# limitations under the License.
#
import unittest
+from unittest import mock
from parameterized import parameterized
+import apache_beam as beam
+
# pylint: disable=ungrouped-imports
try:
from apache_beam.transforms.enrichment_handlers.bigquery import BigQueryEnrichmentHandler
@@ -65,6 +68,50 @@ class TestBigQueryEnrichment(unittest.TestCase):
max_batch_size=max_batch_size,
)
+ def test_batch_mode_fans_out_response_for_duplicate_keys(self):
+ handler = BigQueryEnrichmentHandler(
+ project=self.project,
+ table_name='project.dataset.table',
+ row_restriction_template="id='{}'",
+ fields=['id'],
+ min_batch_size=2,
+ max_batch_size=2,
+ )
+ requests = [beam.Row(id='1', name='first'), beam.Row(id='1', name='second')]
+
+ with mock.patch.object(handler,
+ '_execute_query',
+ return_value=[{'id': '1', 'value': 'enriched'}]):
+ responses = handler(requests)
+
+ self.assertEqual(
+ responses,
+ [
+ (requests[0], beam.Row(id='1', value='enriched')),
+ (requests[1], beam.Row(id='1', value='enriched')),
+ ],
+ )
+
+ def test_batch_mode_emits_empty_rows_for_all_unmatched_duplicate_keys(self):
+ handler = BigQueryEnrichmentHandler(
+ project=self.project,
+ table_name='project.dataset.table',
+ row_restriction_template="id='{}'",
+ fields=['id'],
+ min_batch_size=2,
+ max_batch_size=2,
+ throw_exception_on_empty_results=False,
+ )
+ requests = [beam.Row(id='1', name='first'), beam.Row(id='1', name='second')]
+
+ with mock.patch.object(handler, '_execute_query', return_value=None):
+ responses = handler(requests)
+
+ self.assertEqual(
+ responses,
+ [(requests[0], beam.Row()), (requests[1], beam.Row())],
+ )
+
if __name__ == '__main__':
unittest.main()