FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

Resubmit "Migrate AvroIO.Write to a custom sink" · bakey/DataflowJavaSDK@01a0da0 · GitHub

Repository navigation

Commit 01a0da0

Browse files
authored andcommitted
Resubmit "Migrate AvroIO.Write to a custom sink"
Note for user requested sharding limits to be supported, each pipeline runner must support applying those sharding limits. Google Cloud Dataflow supports sharding limits. ----Release Notes---- [] ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=115402880
1 parent 2e89a4b commit 01a0da0

6 files changed

Lines changed: 433 additions & 291 deletions

File tree

‎sdk/src/main/java/com/google/cloud/dataflow/sdk/io/AvroIO.java‎

Lines changed: 115 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -22,24 +22,25 @@
2222
import com.google.cloud.dataflow.sdk.coders.Coder;
2323
import com.google.cloud.dataflow.sdk.coders.VoidCoder;
2424
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;
2626
import com.google.cloud.dataflow.sdk.runners.PipelineRunner;
27-
import com.google.cloud.dataflow.sdk.runners.worker.AvroSink;
2827
import com.google.cloud.dataflow.sdk.transforms.PTransform;
2928
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;
3230
import com.google.cloud.dataflow.sdk.values.PCollection;
3331
import com.google.cloud.dataflow.sdk.values.PDone;
3432
import com.google.cloud.dataflow.sdk.values.PInput;
33+
import com.google.common.annotations.VisibleForTesting;
3534
import com.google.common.base.Preconditions;
3635

3736
import org.apache.avro.Schema;
37+
import org.apache.avro.file.DataFileWriter;
3838
import org.apache.avro.generic.GenericRecord;
3939
import org.apache.avro.reflect.ReflectData;
4040

4141
import java.io.IOException;
42-
import java.util.List;
42+
import java.nio.channels.Channels;
43+
import java.nio.channels.WritableByteChannel;
4344
import java.util.regex.Pattern;
4445

4546
import javax.annotation.Nullable;
@@ -317,7 +318,7 @@ public PCollection<T> apply(PInput input) {
317318
: com.google.cloud.dataflow.sdk.io.Read.from(
318319
AvroSource.from(filepattern).withSchema(type));
319320

320-
PCollection<T> pcol = input.getPipeline().apply(read);
321+
PCollection<T> pcol = input.getPipeline().apply("Read", read);
321322
// Honor the default output coder that would have been used by this PTransform.
322323
pcol.setCoder(getDefaultOutputCoder());
323324
return pcol;
@@ -473,8 +474,6 @@ public static class Bound<T> extends PTransform<PCollection<T>, PDone> {
473474
final int numShards;
474475
/** Shard template string. */
475476
final String shardTemplate;
476-
/** Insert a shuffle before writing to decouple parallelism when numShards != 0. */
477-
final boolean forceReshard;
478477
/** The class type of the records. */
479478
final Class<T> type;
480479
/** The schema of the output file. */
@@ -484,18 +483,23 @@ public static class Bound<T> extends PTransform<PCollection<T>, PDone> {
484483
final boolean validate;
485484

486485
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);
488487
}
489488

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,
492497
boolean validate) {
493498
super(name);
494499
this.filenamePrefix = filenamePrefix;
495500
this.filenameSuffix = filenameSuffix;
496501
this.numShards = numShards;
497502
this.shardTemplate = shardTemplate;
498-
this.forceReshard = forceReshard;
499503
this.type = type;
500504
this.schema = schema;
501505
this.validate = validate;
@@ -509,8 +513,7 @@ public static class Bound<T> extends PTransform<PCollection<T>, PDone> {
509513
*/
510514
public Bound<T> named(String name) {
511515
return new Bound<>(
512-
name, filenamePrefix, filenameSuffix, numShards, shardTemplate, forceReshard,
513-
type, schema, validate);
516+
name, filenamePrefix, filenameSuffix, numShards, shardTemplate, type, schema, validate);
514517
}
515518

516519
/**
@@ -525,8 +528,7 @@ public Bound<T> named(String name) {
525528
public Bound<T> to(String filenamePrefix) {
526529
validateOutputComponent(filenamePrefix);
527530
return new Bound<>(
528-
name, filenamePrefix, filenameSuffix, numShards, shardTemplate, forceReshard,
529-
type, schema, validate);
531+
name, filenamePrefix, filenameSuffix, numShards, shardTemplate, type, schema, validate);
530532
}
531533

532534
/**
@@ -540,8 +542,7 @@ public Bound<T> to(String filenamePrefix) {
540542
public Bound<T> withSuffix(String filenameSuffix) {
541543
validateOutputComponent(filenameSuffix);
542544
return new Bound<>(
543-
name, filenamePrefix, filenameSuffix, numShards, shardTemplate, forceReshard, type,
544-
schema, validate);
545+
name, filenamePrefix, filenameSuffix, numShards, shardTemplate, type, schema, validate);
545546
}
546547

547548
/**
@@ -559,32 +560,9 @@ public Bound<T> withSuffix(String filenameSuffix) {
559560
* @see ShardNameTemplate
560561
*/
561562
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) {
584563
Preconditions.checkArgument(numShards >= 0);
585564
return new Bound<>(
586-
name, filenamePrefix, filenameSuffix, numShards, shardTemplate, forceReshard,
587-
type, schema, validate);
565+
name, filenamePrefix, filenameSuffix, numShards, shardTemplate, type, schema, validate);
588566
}
589567

590568
/**
@@ -597,8 +575,7 @@ private Bound<T> withNumShards(int numShards, boolean forceReshard) {
597575
*/
598576
public Bound<T> withShardNameTemplate(String shardTemplate) {
599577
return new Bound<>(
600-
name, filenamePrefix, filenameSuffix, numShards, shardTemplate, forceReshard,
601-
type, schema, validate);
578+
name, filenamePrefix, filenameSuffix, numShards, shardTemplate, type, schema, validate);
602579
}
603580

604581
/**
@@ -611,23 +588,7 @@ public Bound<T> withShardNameTemplate(String shardTemplate) {
611588
* <p>Does not modify this object.
612589
*/
613590
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);
631592
}
632593

633594
/**
@@ -640,8 +601,15 @@ private Bound<T> withoutSharding(boolean forceReshard) {
640601
* @param <X> the type of the elements of the input PCollection
641602
*/
642603
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);
645613
}
646614

647615
/**
@@ -652,8 +620,15 @@ public <X> Bound<X> withSchema(Class<X> type) {
652620
* <p>Does not modify this object.
653621
*/
654622
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);
657632
}
658633

659634
/**
@@ -679,8 +654,7 @@ public Bound<GenericRecord> withSchema(String schema) {
679654
*/
680655
public Bound<T> withoutValidation() {
681656
return new Bound<>(
682-
name, filenamePrefix, filenameSuffix, numShards, shardTemplate, forceReshard,
683-
type, schema, false);
657+
name, filenamePrefix, filenameSuffix, numShards, shardTemplate, type, schema, false);
684658
}
685659

686660
@Override
@@ -693,14 +667,14 @@ public PDone apply(PCollection<T> input) {
693667
throw new IllegalStateException("need to set the schema of an AvroIO.Write transform");
694668
}
695669

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))));
704678
}
705679

706680
/**
@@ -742,21 +716,6 @@ public Schema getSchema() {
742716
public boolean needsValidation() {
743717
return validate;
744718
}
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-
}
760719
}
761720

762721
/** Disallow construction of utility class. */
@@ -779,25 +738,73 @@ private static void validateOutputComponent(String partialFilePattern) {
779738
/** Disallow construction of utility class. */
780739
private AvroIO() {}
781740

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+
}
790776
}
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+
}
801808
}
802809
}
803810
}

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL