Skip to content

Commit 05f9167

Browse files
committed
Fix ThreadSanitizer data races between bundle processing and async background writer closing in FileBasedSink and WriteFiles
1 parent aa8869c commit 05f9167

3 files changed

Lines changed: 17 additions & 3 deletions

File tree

sdks/java/core/src/main/java/org/apache/beam/sdk/io/FileBasedSink.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
*/
1818
package org.apache.beam.sdk.io;
1919

20+
import java.util.concurrent.atomic.AtomicBoolean;
2021
import static org.apache.beam.sdk.io.WriteFiles.UNKNOWN_SHARDNUM;
2122
import static org.apache.beam.sdk.values.TypeDescriptors.extractFromTypeParameters;
2223
import static org.apache.beam.vendor.guava.v32_1_2_jre.com.google.common.base.MoreObjects.firstNonNull;
@@ -925,13 +926,20 @@ public abstract static class Writer<DestinationT, OutputT> {
925926
*/
926927
private final @Nullable String mimeType;
927928

929+
private final AtomicBoolean readyToClose =
930+
new AtomicBoolean(false);
931+
928932
/** Construct a new {@link Writer} that will produce files of the given MIME type. */
929933
public Writer(WriteOperation<DestinationT, OutputT> writeOperation, String mimeType) {
930934
checkNotNull(writeOperation);
931935
this.writeOperation = writeOperation;
932936
this.mimeType = mimeType;
933937
}
934938

939+
void releaseForBackgroundClose() {
940+
readyToClose.set(true);
941+
}
942+
935943
/**
936944
* Called with the channel that a subclass will write its header, footer, and values to.
937945
* Subclasses should either keep a reference to the channel provided or create and keep a
@@ -1037,6 +1045,7 @@ private static void closeChannelAndThrow(
10371045
}
10381046

10391047
public final void cleanup() throws Exception {
1048+
readyToClose.get();
10401049
if (outputFile != null) {
10411050
LOG.info("Deleting temporary file {}", outputFile);
10421051
// outputFile may be null if open() was not called or failed.
@@ -1047,6 +1056,7 @@ public final void cleanup() throws Exception {
10471056

10481057
/** Closes the channel and returns the bundle result. */
10491058
public final void close() throws Exception {
1059+
readyToClose.get();
10501060
checkState(outputFile != null, "FileResult.close cannot be called with a null outputFile");
10511061
LOG.debug("Closing {}", outputFile);
10521062

sdks/java/core/src/main/java/org/apache/beam/sdk/io/WriteFiles.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1272,6 +1272,9 @@ public void processElement(
12721272
}
12731273

12741274
private void closeWriterInBackground(Writer<DestinationT, OutputT> writer) {
1275+
// Release memory barrier so background thread acquires all prior writes on Writer and
1276+
// channels.
1277+
writer.releaseForBackgroundClose();
12751278
// Close in parallel so flushing of buffered writes to files for many windows happens in
12761279
// parallel.
12771280
closeFutures.add(

sdks/java/core/src/test/java/org/apache/beam/sdk/io/TFRecordSchemaTransformProviderTest.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -434,9 +434,10 @@ private void runTestWrite(String[] elems, String... base64) throws IOException {
434434
assertTrue("File should exist", tmpFile.exists());
435435
assertTrue("File should have content", tmpFile.length() > 0);
436436

437-
FileInputStream fis = new FileInputStream(tmpFile);
438-
String written = BaseEncoding.base64().encode(ByteStreams.toByteArray(fis));
439-
assertThat(written, is(in(base64)));
437+
try (FileInputStream fis = new FileInputStream(tmpFile)) {
438+
String written = BaseEncoding.base64().encode(ByteStreams.toByteArray(fis));
439+
assertThat(written, is(in(base64)));
440+
}
440441
}
441442

442443
@Test

0 commit comments

Comments
 (0)