| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 2e89a4b commit 01a0da0
6 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -22,24 +22,25 @@ | |||
| 22 | 22 | import com.google.cloud.dataflow.sdk.coders.Coder; | |
| 23 | 23 | import com.google.cloud.dataflow.sdk.coders.VoidCoder; | |
| 24 | 24 | import com.google.cloud.dataflow.sdk.io.Read.Bounded; | |
| 25 | - import com.google.cloud.dataflow.sdk.runners.DirectPipelineRunner; | ||
| 25 | + import com.google.cloud.dataflow.sdk.options.PipelineOptions; | ||
| 26 | 26 | import com.google.cloud.dataflow.sdk.runners.PipelineRunner; | |
| 27 | - import com.google.cloud.dataflow.sdk.runners.worker.AvroSink; | ||
| 28 | 27 | import com.google.cloud.dataflow.sdk.transforms.PTransform; | |
| 29 | 28 | import com.google.cloud.dataflow.sdk.util.IOChannelUtils; | |
| 30 | - import com.google.cloud.dataflow.sdk.util.WindowedValue; | ||
| 31 | - import com.google.cloud.dataflow.sdk.util.common.worker.Sink; | ||
| 29 | + import com.google.cloud.dataflow.sdk.util.MimeTypes; | ||
| 32 | 30 | import com.google.cloud.dataflow.sdk.values.PCollection; | |
| 33 | 31 | import com.google.cloud.dataflow.sdk.values.PDone; | |
| 34 | 32 | import com.google.cloud.dataflow.sdk.values.PInput; | |
| 33 | + import com.google.common.annotations.VisibleForTesting; | ||
| 35 | 34 | import com.google.common.base.Preconditions; | |
| 36 | 35 | ||
| 37 | 36 | import org.apache.avro.Schema; | |
| 37 | + import org.apache.avro.file.DataFileWriter; | ||
| 38 | 38 | import org.apache.avro.generic.GenericRecord; | |
| 39 | 39 | import org.apache.avro.reflect.ReflectData; | |
| 40 | 40 | ||
| 41 | 41 | import java.io.IOException; | |
| 42 | - import java.util.List; | ||
| 42 | + import java.nio.channels.Channels; | ||
| 43 | + import java.nio.channels.WritableByteChannel; | ||
| 43 | 44 | import java.util.regex.Pattern; | |
| 44 | 45 | ||
| 45 | 46 | import javax.annotation.Nullable; | |
@@ -317,7 +318,7 @@ public PCollection<T> apply(PInput input) { | |||
| 317 | 318 | : com.google.cloud.dataflow.sdk.io.Read.from( | |
| 318 | 319 | AvroSource.from(filepattern).withSchema(type)); | |
| 319 | 320 | ||
| 320 | - PCollection<T> pcol = input.getPipeline().apply(read); | ||
| 321 | + PCollection<T> pcol = input.getPipeline().apply("Read", read); | ||
| 321 | 322 | // Honor the default output coder that would have been used by this PTransform. | |
| 322 | 323 | pcol.setCoder(getDefaultOutputCoder()); | |
| 323 | 324 | return pcol; | |
@@ -473,8 +474,6 @@ public static class Bound<T> extends PTransform<PCollection<T>, PDone> { | |||
| 473 | 474 | final int numShards; | |
| 474 | 475 | /** Shard template string. */ | |
| 475 | 476 | final String shardTemplate; | |
| 476 | - /** Insert a shuffle before writing to decouple parallelism when numShards != 0. */ | ||
| 477 | - final boolean forceReshard; | ||
| 478 | 477 | /** The class type of the records. */ | |
| 479 | 478 | final Class<T> type; | |
| 480 | 479 | /** The schema of the output file. */ | |
@@ -484,18 +483,23 @@ public static class Bound<T> extends PTransform<PCollection<T>, PDone> { | |||
| 484 | 483 | final boolean validate; | |
| 485 | 484 | ||
| 486 | 485 | Bound(Class<T> type) { | |
| 487 | - this(null, null, "", 0, ShardNameTemplate.INDEX_OF_MAX, true, type, null, true); | ||
| 486 | + this(null, null, "", 0, ShardNameTemplate.INDEX_OF_MAX, type, null, true); | ||
| 488 | 487 | } | |
| 489 | 488 | ||
| 490 | - Bound(String name, String filenamePrefix, String filenameSuffix, int numShards, | ||
| 491 | - String shardTemplate, boolean forceReshard, Class<T> type, Schema schema, | ||
| 489 | + Bound( | ||
| 490 | + String name, | ||
| 491 | + String filenamePrefix, | ||
| 492 | + String filenameSuffix, | ||
| 493 | + int numShards, | ||
| 494 | + String shardTemplate, | ||
| 495 | + Class<T> type, | ||
| 496 | + Schema schema, | ||
| 492 | 497 | boolean validate) { | |
| 493 | 498 | super(name); | |
| 494 | 499 | this.filenamePrefix = filenamePrefix; | |
| 495 | 500 | this.filenameSuffix = filenameSuffix; | |
| 496 | 501 | this.numShards = numShards; | |
| 497 | 502 | this.shardTemplate = shardTemplate; | |
| 498 | - this.forceReshard = forceReshard; | ||
| 499 | 503 | this.type = type; | |
| 500 | 504 | this.schema = schema; | |
| 501 | 505 | this.validate = validate; | |
@@ -509,8 +513,7 @@ public static class Bound<T> extends PTransform<PCollection<T>, PDone> { | |||
| 509 | 513 | */ | |
| 510 | 514 | public Bound<T> named(String name) { | |
| 511 | 515 | return new Bound<>( | |
| 512 | - name, filenamePrefix, filenameSuffix, numShards, shardTemplate, forceReshard, | ||
| 513 | - type, schema, validate); | ||
| 516 | + name, filenamePrefix, filenameSuffix, numShards, shardTemplate, type, schema, validate); | ||
| 514 | 517 | } | |
| 515 | 518 | ||
| 516 | 519 | /** | |
@@ -525,8 +528,7 @@ public Bound<T> named(String name) { | |||
| 525 | 528 | public Bound<T> to(String filenamePrefix) { | |
| 526 | 529 | validateOutputComponent(filenamePrefix); | |
| 527 | 530 | return new Bound<>( | |
| 528 | - name, filenamePrefix, filenameSuffix, numShards, shardTemplate, forceReshard, | ||
| 529 | - type, schema, validate); | ||
| 531 | + name, filenamePrefix, filenameSuffix, numShards, shardTemplate, type, schema, validate); | ||
| 530 | 532 | } | |
| 531 | 533 | ||
| 532 | 534 | /** | |
@@ -540,8 +542,7 @@ public Bound<T> to(String filenamePrefix) { | |||
| 540 | 542 | public Bound<T> withSuffix(String filenameSuffix) { | |
| 541 | 543 | validateOutputComponent(filenameSuffix); | |
| 542 | 544 | return new Bound<>( | |
| 543 | - name, filenamePrefix, filenameSuffix, numShards, shardTemplate, forceReshard, type, | ||
| 544 | - schema, validate); | ||
| 545 | + name, filenamePrefix, filenameSuffix, numShards, shardTemplate, type, schema, validate); | ||
| 545 | 546 | } | |
| 546 | 547 | ||
| 547 | 548 | /** | |
@@ -559,32 +560,9 @@ public Bound<T> withSuffix(String filenameSuffix) { | |||
| 559 | 560 | * @see ShardNameTemplate | |
| 560 | 561 | */ | |
| 561 | 562 | public Bound<T> withNumShards(int numShards) { | |
| 562 | - return withNumShards(numShards, forceReshard); | ||
| 563 | - } | ||
| 564 | - | ||
| 565 | - /** | ||
| 566 | - * Returns a new {@link PTransform} that's like this one but | ||
| 567 | - * that uses the provided shard count. | ||
| 568 | - * | ||
| 569 | - * <p>Constraining the number of shards is likely to reduce | ||
| 570 | - * the performance of a pipeline. If forceReshard is true, the output | ||
| 571 | - * will be shuffled to obtain the desired sharding. If it is false, | ||
| 572 | - * data will not be reshuffled, but parallelism of preceeding stages | ||
| 573 | - * may be constrained. Setting this value is not recommended | ||
| 574 | - * unless you require a specific number of output files. | ||
| 575 | - * | ||
| 576 | - * <p>Does not modify this object. | ||
| 577 | - * | ||
| 578 | - * @param numShards the number of shards to use, or 0 to let the system | ||
| 579 | - * decide. | ||
| 580 | - * @param forceReshard whether to force a reshard to obtain the desired sharding. | ||
| 581 | - * @see ShardNameTemplate | ||
| 582 | - */ | ||
| 583 | - private Bound<T> withNumShards(int numShards, boolean forceReshard) { | ||
| 584 | 563 | Preconditions.checkArgument(numShards >= 0); | |
| 585 | 564 | return new Bound<>( | |
| 586 | - name, filenamePrefix, filenameSuffix, numShards, shardTemplate, forceReshard, | ||
| 587 | - type, schema, validate); | ||
| 565 | + name, filenamePrefix, filenameSuffix, numShards, shardTemplate, type, schema, validate); | ||
| 588 | 566 | } | |
| 589 | 567 | ||
| 590 | 568 | /** | |
@@ -597,8 +575,7 @@ private Bound<T> withNumShards(int numShards, boolean forceReshard) { | |||
| 597 | 575 | */ | |
| 598 | 576 | public Bound<T> withShardNameTemplate(String shardTemplate) { | |
| 599 | 577 | return new Bound<>( | |
| 600 | - name, filenamePrefix, filenameSuffix, numShards, shardTemplate, forceReshard, | ||
| 601 | - type, schema, validate); | ||
| 578 | + name, filenamePrefix, filenameSuffix, numShards, shardTemplate, type, schema, validate); | ||
| 602 | 579 | } | |
| 603 | 580 | ||
| 604 | 581 | /** | |
@@ -611,23 +588,7 @@ public Bound<T> withShardNameTemplate(String shardTemplate) { | |||
| 611 | 588 | * <p>Does not modify this object. | |
| 612 | 589 | */ | |
| 613 | 590 | public Bound<T> withoutSharding() { | |
| 614 | - return withoutSharding(forceReshard); | ||
| 615 | - } | ||
| 616 | - | ||
| 617 | - /** | ||
| 618 | - * Returns a new {@link PTransform} that's like this one but | ||
| 619 | - * that forces a single file as output. | ||
| 620 | - * | ||
| 621 | - * <p>This is a shortcut for | ||
| 622 | - * {@code .withNumShards(1, forceReshard).withShardNameTemplate("")} | ||
| 623 | - * | ||
| 624 | - * <p>Does not modify this object. | ||
| 625 | - * | ||
| 626 | - * @param forceReshard whether to force a reshard to obtain the desired sharding. | ||
| 627 | - */ | ||
| 628 | - private Bound<T> withoutSharding(boolean forceReshard) { | ||
| 629 | - return new Bound<>(name, filenamePrefix, filenameSuffix, 1, "", forceReshard, | ||
| 630 | - type, schema, validate); | ||
| 591 | + return new Bound<>(name, filenamePrefix, filenameSuffix, 1, "", type, schema, validate); | ||
| 631 | 592 | } | |
| 632 | 593 | ||
| 633 | 594 | /** | |
@@ -640,8 +601,15 @@ private Bound<T> withoutSharding(boolean forceReshard) { | |||
| 640 | 601 | * @param <X> the type of the elements of the input PCollection | |
| 641 | 602 | */ | |
| 642 | 603 | public <X> Bound<X> withSchema(Class<X> type) { | |
| 643 | - return new Bound<>(name, filenamePrefix, filenameSuffix, numShards, shardTemplate, | ||
| 644 | - forceReshard, type, ReflectData.get().getSchema(type), validate); | ||
| 604 | + return new Bound<>( | ||
| 605 | + name, | ||
| 606 | + filenamePrefix, | ||
| 607 | + filenameSuffix, | ||
| 608 | + numShards, | ||
| 609 | + shardTemplate, | ||
| 610 | + type, | ||
| 611 | + ReflectData.get().getSchema(type), | ||
| 612 | + validate); | ||
| 645 | 613 | } | |
| 646 | 614 | ||
| 647 | 615 | /** | |
@@ -652,8 +620,15 @@ public <X> Bound<X> withSchema(Class<X> type) { | |||
| 652 | 620 | * <p>Does not modify this object. | |
| 653 | 621 | */ | |
| 654 | 622 | public Bound<GenericRecord> withSchema(Schema schema) { | |
| 655 | - return new Bound<>(name, filenamePrefix, filenameSuffix, numShards, shardTemplate, | ||
| 656 | - forceReshard, GenericRecord.class, schema, validate); | ||
| 623 | + return new Bound<>( | ||
| 624 | + name, | ||
| 625 | + filenamePrefix, | ||
| 626 | + filenameSuffix, | ||
| 627 | + numShards, | ||
| 628 | + shardTemplate, | ||
| 629 | + GenericRecord.class, | ||
| 630 | + schema, | ||
| 631 | + validate); | ||
| 657 | 632 | } | |
| 658 | 633 | ||
| 659 | 634 | /** | |
@@ -679,8 +654,7 @@ public Bound<GenericRecord> withSchema(String schema) { | |||
| 679 | 654 | */ | |
| 680 | 655 | public Bound<T> withoutValidation() { | |
| 681 | 656 | return new Bound<>( | |
| 682 | - name, filenamePrefix, filenameSuffix, numShards, shardTemplate, forceReshard, | ||
| 683 | - type, schema, false); | ||
| 657 | + name, filenamePrefix, filenameSuffix, numShards, shardTemplate, type, schema, false); | ||
| 684 | 658 | } | |
| 685 | 659 | ||
| 686 | 660 | @Override | |
@@ -693,14 +667,14 @@ public PDone apply(PCollection<T> input) { | |||
| 693 | 667 | throw new IllegalStateException("need to set the schema of an AvroIO.Write transform"); | |
| 694 | 668 | } | |
| 695 | 669 | ||
| 696 | - if (numShards > 0 && forceReshard) { | ||
| 697 | - // Reshard and re-apply a version of this write without resharding. | ||
| 698 | - return input | ||
| 699 | - .apply(new FileBasedSink.ReshardForWrite<T>()) | ||
| 700 | - .apply(withNumShards(numShards, false)); | ||
| 701 | - } else { | ||
| 702 | - return PDone.in(input.getPipeline()); | ||
| 703 | - } | ||
| 670 | + // Note that custom sinks currently do not expose sharding controls. | ||
| 671 | + // Thus pipeline runner writers need to individually add support internally to | ||
| 672 | + // apply user requested sharding limits. | ||
| 673 | + return input.apply( | ||
| 674 | + "Write", | ||
| 675 | + com.google.cloud.dataflow.sdk.io.Write.to( | ||
| 676 | + new AvroSink<>( | ||
| 677 | + filenamePrefix, filenameSuffix, shardTemplate, AvroCoder.of(type, schema)))); | ||
| 704 | 678 | } | |
| 705 | 679 | ||
| 706 | 680 | /** | |
@@ -742,21 +716,6 @@ public Schema getSchema() { | |||
| 742 | 716 | public boolean needsValidation() { | |
| 743 | 717 | return validate; | |
| 744 | 718 | } | |
| 745 | - | ||
| 746 | - static { | ||
| 747 | - @SuppressWarnings("rawtypes") | ||
| 748 | - DirectPipelineRunner.TransformEvaluator<Bound> transformEvaluator = | ||
| 749 | - new DirectPipelineRunner.TransformEvaluator<Bound>() { | ||
| 750 | - @Override | ||
| 751 | - @SuppressWarnings("unchecked") | ||
| 752 | - public void evaluate( | ||
| 753 | - Bound transform, DirectPipelineRunner.EvaluationContext context) { | ||
| 754 | - evaluateWriteHelper(transform, context); | ||
| 755 | - } | ||
| 756 | - }; | ||
| 757 | - DirectPipelineRunner.registerDefaultTransformEvaluator( | ||
| 758 | - Bound.class, transformEvaluator); | ||
| 759 | - } | ||
| 760 | 719 | } | |
| 761 | 720 | ||
| 762 | 721 | /** Disallow construction of utility class. */ | |
@@ -779,25 +738,73 @@ private static void validateOutputComponent(String partialFilePattern) { | |||
| 779 | 738 | /** Disallow construction of utility class. */ | |
| 780 | 739 | private AvroIO() {} | |
| 781 | 740 | ||
| 782 | - private static <T> void evaluateWriteHelper( | ||
| 783 | - Write.Bound<T> transform, DirectPipelineRunner.EvaluationContext context) { | ||
| 784 | - List<WindowedValue<T>> elems = | ||
| 785 | - context.getPCollectionWindowedValues(context.getInput(transform)); | ||
| 786 | - int numShards = transform.numShards; | ||
| 787 | - if (numShards < 1) { | ||
| 788 | - // System gets to choose. For direct mode, choose 1. | ||
| 789 | - numShards = 1; | ||
| 741 | + /** | ||
| 742 | + * A {@link FileBasedSink} for Avro files. | ||
| 743 | + */ | ||
| 744 | + @VisibleForTesting | ||
| 745 | + static class AvroSink<T> extends FileBasedSink<T> { | ||
| 746 | + private final AvroCoder<T> coder; | ||
| 747 | + | ||
| 748 | + @VisibleForTesting | ||
| 749 | + AvroSink( | ||
| 750 | + String baseOutputFilename, String extension, String fileNameTemplate, AvroCoder<T> coder) { | ||
| 751 | + super(baseOutputFilename, extension, fileNameTemplate); | ||
| 752 | + this.coder = coder; | ||
| 753 | + } | ||
| 754 | + | ||
| 755 | + @Override | ||
| 756 | + public FileBasedSink.FileBasedWriteOperation<T> createWriteOperation(PipelineOptions options) { | ||
| 757 | + return new AvroWriteOperation<>(this, coder); | ||
| 758 | + } | ||
| 759 | + | ||
| 760 | + /** | ||
| 761 | + * A {@link com.google.cloud.dataflow.sdk.io.FileBasedSink.FileBasedWriteOperation | ||
| 762 | + * FileBasedWriteOperation} for Avro files. | ||
| 763 | + */ | ||
| 764 | + private static class AvroWriteOperation<T> extends FileBasedWriteOperation<T> { | ||
| 765 | + private final AvroCoder<T> coder; | ||
| 766 | + | ||
| 767 | + private AvroWriteOperation(AvroSink<T> sink, AvroCoder<T> coder) { | ||
| 768 | + super(sink); | ||
| 769 | + this.coder = coder; | ||
| 770 | + } | ||
| 771 | + | ||
| 772 | + @Override | ||
| 773 | + public FileBasedWriter<T> createWriter(PipelineOptions options) throws Exception { | ||
| 774 | + return new AvroWriter<>(this, coder); | ||
| 775 | + } | ||
| 790 | 776 | } | |
| 791 | - AvroSink<T> writer = new AvroSink<>(transform.filenamePrefix, transform.shardTemplate, | ||
| 792 | - transform.filenameSuffix, numShards, | ||
| 793 | - WindowedValue.getValueOnlyCoder(AvroCoder.of(transform.type, transform.schema))); | ||
| 794 | - try (Sink.SinkWriter<WindowedValue<T>> sink = writer.writer()) { | ||
| 795 | - for (WindowedValue<T> elem : elems) { | ||
| 796 | - sink.add(elem); | ||
| 797 | - } | ||
| 798 | - } catch (IOException exn) { | ||
| 799 | - throw new RuntimeException( | ||
| 800 | - "unable to write to output file \"" + transform.filenamePrefix + "\"", exn); | ||
| 777 | + | ||
| 778 | + /** | ||
| 779 | + * A {@link com.google.cloud.dataflow.sdk.io.FileBasedSink.FileBasedWriter FileBasedWriter} | ||
| 780 | + * for Avro files. | ||
| 781 | + */ | ||
| 782 | + private static class AvroWriter<T> extends FileBasedWriter<T> { | ||
| 783 | + private final AvroCoder<T> coder; | ||
| 784 | + private DataFileWriter<T> dataFileWriter; | ||
| 785 | + | ||
| 786 | + public AvroWriter(FileBasedWriteOperation<T> writeOperation, AvroCoder<T> coder) { | ||
| 787 | + super(writeOperation); | ||
| 788 | + this.mimeType = MimeTypes.BINARY; | ||
| 789 | + this.coder = coder; | ||
| 790 | + } | ||
| 791 | + | ||
| 792 | + @SuppressWarnings("deprecation") // uses internal test functionality. | ||
| 793 | + @Override | ||
| 794 | + protected void prepareWrite(WritableByteChannel channel) throws Exception { | ||
| 795 | + dataFileWriter = new DataFileWriter<>(coder.createDatumWriter()); | ||
| 796 | + dataFileWriter.create(coder.getSchema(), Channels.newOutputStream(channel)); | ||
| 797 | + } | ||
| 798 | + | ||
| 799 | + @Override | ||
| 800 | + public void write(T value) throws Exception { | ||
| 801 | + dataFileWriter.append(value); | ||
| 802 | + } | ||
| 803 | + | ||
| 804 | + @Override | ||
| 805 | + protected void writeFooter() throws Exception { | ||
| 806 | + dataFileWriter.flush(); | ||
| 807 | + } | ||
| 801 | 808 | } | |
| 802 | 809 | } | |
| 803 | 810 | } | |
| Back | FazBrowse Home | New Git URL |
0 commit comments