|
75 | 75 | from apache_beam.transforms.util import GcpHsmGeneratedSecret |
76 | 76 | from apache_beam.transforms.util import GcpSecret |
77 | 77 | from apache_beam.transforms.util import Secret |
| 78 | +from apache_beam.transforms.util import _BatchSizeEstimator |
| 79 | +from apache_beam.transforms.util import _GlobalWindowsBatchingDoFn |
78 | 80 | from apache_beam.transforms.window import FixedWindows |
79 | 81 | from apache_beam.transforms.window import GlobalWindow |
80 | 82 | from apache_beam.transforms.window import GlobalWindows |
@@ -1258,6 +1260,53 @@ def check_batch_homogeneity(batch): |
1258 | 1260 | checks = batches | beam.Map(check_batch_homogeneity) |
1259 | 1261 | assert_that(checks, is_not_empty()) |
1260 | 1262 |
|
| 1263 | + def test_global_batching_dofn_single_vs_multiple_bundles(self): |
| 1264 | + # This test directly verifies how bundling affects the batch sizes produced by |
| 1265 | + # the internal _GlobalWindowsBatchingDoFn of BatchElements. |
| 1266 | + |
| 1267 | + # 1. Single Bundle Scenario: |
| 1268 | + # Four elements processed within the same start_bundle / finish_bundle lifecycle. |
| 1269 | + # min_batch_size = 2, max_batch_size = 2. |
| 1270 | + estimator = _BatchSizeEstimator(min_batch_size=2, max_batch_size=2) |
| 1271 | + dofn = _GlobalWindowsBatchingDoFn(estimator, element_size_fn=lambda x: 1) |
| 1272 | + |
| 1273 | + dofn.start_bundle() |
| 1274 | + outputs = [] |
| 1275 | + for elem in [1, 2, 3, 4]: |
| 1276 | + outputs.extend(dofn.process(elem)) |
| 1277 | + outputs.extend(dofn.finish_bundle() or []) |
| 1278 | + |
| 1279 | + # We should get exactly two batches of size 2. |
| 1280 | + batch_sizes = [len(wv.value) for wv in outputs] |
| 1281 | + self.assertEqual(batch_sizes, [2, 2]) |
| 1282 | + |
| 1283 | + # 2. Multiple Bundles Scenario (simulating elements split due to Reshuffle/GroupByKey): |
| 1284 | + # The runner splits elements into multiple bundles: |
| 1285 | + # Bundle 1 gets elements 1, 2, 3. |
| 1286 | + # Bundle 2 gets element 4. |
| 1287 | + estimator = _BatchSizeEstimator(min_batch_size=2, max_batch_size=2) |
| 1288 | + dofn = _GlobalWindowsBatchingDoFn(estimator, element_size_fn=lambda x: 1) |
| 1289 | + |
| 1290 | + outputs = [] |
| 1291 | + # Bundle 1 |
| 1292 | + dofn.start_bundle() |
| 1293 | + for elem in [1, 2, 3]: |
| 1294 | + outputs.extend(dofn.process(elem)) |
| 1295 | + outputs.extend(dofn.finish_bundle() or []) |
| 1296 | + |
| 1297 | + # Bundle 2 |
| 1298 | + dofn.start_bundle() |
| 1299 | + for elem in [4]: |
| 1300 | + outputs.extend(dofn.process(elem)) |
| 1301 | + outputs.extend(dofn.finish_bundle() or []) |
| 1302 | + |
| 1303 | + # The batch sizes will be [2, 1, 1] instead of [2, 2] because of bundle flushes. |
| 1304 | + # Specifically: |
| 1305 | + # - Bundle 1 emits a batch of 2, and then the remaining 1 element is flushed at finish_bundle (batch size 1). |
| 1306 | + # - Bundle 2 emits its 1 element at finish_bundle (batch size 1). |
| 1307 | + batch_sizes = [len(wv.value) for wv in outputs] |
| 1308 | + self.assertEqual(batch_sizes, [2, 1, 1]) |
| 1309 | + |
1261 | 1310 |
|
1262 | 1311 | class SortAndBatchElementsTest(unittest.TestCase): |
1263 | 1312 | """Tests for SortAndBatchElements transform.""" |
|
0 commit comments