|
23 | 23 | import com.google.cloud.bigquery.storage.v1.AppendRowsRequest; |
24 | 24 | import java.io.IOException; |
25 | 25 | import java.nio.ByteBuffer; |
| 26 | +import java.nio.charset.StandardCharsets; |
26 | 27 | import java.util.Map; |
27 | 28 | import java.util.concurrent.ThreadLocalRandom; |
28 | 29 | import java.util.function.Predicate; |
|
38 | 39 | import org.apache.beam.sdk.transforms.ParDo; |
39 | 40 | import org.apache.beam.sdk.transforms.Redistribute; |
40 | 41 | import org.apache.beam.sdk.transforms.SerializableFunction; |
| 42 | +import org.apache.beam.sdk.transforms.Values; |
41 | 43 | import org.apache.beam.sdk.transforms.errorhandling.BadRecord; |
42 | 44 | import org.apache.beam.sdk.transforms.errorhandling.BadRecordRouter; |
43 | 45 | import org.apache.beam.sdk.transforms.errorhandling.BadRecordRouter.ThrowingBadRecordRouter; |
|
50 | 52 | import org.apache.beam.sdk.values.PCollectionList; |
51 | 53 | import org.apache.beam.sdk.values.PCollectionTuple; |
52 | 54 | import org.apache.beam.sdk.values.TupleTag; |
| 55 | +import org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.hash.Hashing; |
53 | 56 | import org.joda.time.Duration; |
54 | 57 |
|
55 | 58 | /** This {@link PTransform} manages loads into BigQuery using the Storage API. */ |
@@ -379,12 +382,21 @@ public WriteResult expandUntriggered( |
379 | 382 | PCollection<KV<DestinationT, StorageApiWritePayload>> successfulConvertedRows = |
380 | 383 | convertMessagesResult.get(successfulConvertedRowsTag); |
381 | 384 |
|
382 | | - if (numShards > 0) { |
| 385 | + if (numShards > 0 && input.isBounded() == PCollection.IsBounded.UNBOUNDED) { |
383 | 386 | successfulConvertedRows = |
384 | 387 | successfulConvertedRows.apply( |
385 | 388 | "ResdistibuteNumShards", |
386 | 389 | Redistribute.<KV<DestinationT, StorageApiWritePayload>>arbitrarily() |
387 | 390 | .withNumBuckets(numShards)); |
| 391 | + } else if (numShards > 0 && input.isBounded() == PCollection.IsBounded.BOUNDED) { |
| 392 | + successfulConvertedRows = |
| 393 | + successfulConvertedRows |
| 394 | + .apply( |
| 395 | + "AddKeyWithSideInputs", |
| 396 | + ParDo.of(new AddShardKeyFn<>(dynamicDestinations, numShards)) |
| 397 | + .withSideInputs(dynamicDestinations.getSideInputs())) |
| 398 | + .apply("RedistributeNumShards", Redistribute.byKey()) |
| 399 | + .apply("Remove shard", Values.create()); |
388 | 400 | } |
389 | 401 |
|
390 | 402 | PCollectionTuple writeRecordsResult = |
@@ -457,6 +469,37 @@ private void addErrorCollections( |
457 | 469 | } |
458 | 470 | } |
459 | 471 |
|
| 472 | + private static class AddShardKeyFn<DestT, ElemT> |
| 473 | + extends DoFn< |
| 474 | + KV<DestT, StorageApiWritePayload>, KV<Integer, KV<DestT, StorageApiWritePayload>>> { |
| 475 | + |
| 476 | + private final StorageApiDynamicDestinations<ElemT, DestT> dynamicDestinations; |
| 477 | + private final int numShards; |
| 478 | + |
| 479 | + public AddShardKeyFn( |
| 480 | + StorageApiDynamicDestinations<ElemT, DestT> dynamicDestinations, int numShards) { |
| 481 | + this.dynamicDestinations = dynamicDestinations; |
| 482 | + this.numShards = numShards; |
| 483 | + } |
| 484 | + |
| 485 | + @ProcessElement |
| 486 | + public void processElement( |
| 487 | + ProcessContext c, |
| 488 | + @Element KV<DestT, StorageApiWritePayload> element, |
| 489 | + OutputReceiver<KV<Integer, KV<DestT, StorageApiWritePayload>>> outputReceiver) { |
| 490 | + dynamicDestinations.setSideInputAccessorFromProcessContext(c); |
| 491 | + |
| 492 | + String tableUrn = dynamicDestinations.getTable(element.getKey()).getShortTableUrn(); |
| 493 | + |
| 494 | + int hash = Hashing.murmur3_32_fixed().hashString(tableUrn, StandardCharsets.UTF_8).asInt(); |
| 495 | + |
| 496 | + int shardKey = |
| 497 | + Math.floorMod(hash + ThreadLocalRandom.current().nextInt(numShards), numShards); |
| 498 | + |
| 499 | + outputReceiver.output(KV.of(shardKey, element)); |
| 500 | + } |
| 501 | + } |
| 502 | + |
460 | 503 | private static class ConvertInsertErrorToBadRecord |
461 | 504 | extends DoFn<BigQueryStorageApiInsertError, BadRecord> { |
462 | 505 |
|
|
0 commit comments