Skip to content
Closed
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ public interface DataSourceWriter {
*/
DataWriterFactory<Row> createWriterFactory();

/**
* Handles a commit message on receiving from a successful data writer.
*
* If this method fails (by throwing an exception), this writing job is considered to to have been
* failed, and {@link #abort(WriterCommitMessage[])} would be called. The state of the destination
* is undefined and {@link #abort(WriterCommitMessage[])} may not be able to deal with it.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does it mean that "the state of the destination is undefined"? I think it is sufficient to say that abort will be called and the contract for aborting commits applies.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense, let's remove the last sentence.

*/
default void onDataWriterCommit(WriterCommitMessage message) {}

/**
* Commits this writing job with a list of commit messages. The commit messages are collected from
* successful data writers and are produced by {@link DataWriter#commit()}.
Expand All @@ -78,8 +87,10 @@ public interface DataSourceWriter {
void commit(WriterCommitMessage[] messages);

/**
* Aborts this writing job because some data writers are failed and keep failing when retry, or
* the Spark job fails with some unknown reasons, or {@link #commit(WriterCommitMessage[])} fails.
* Aborts this writing job because some data writers are failed and keep failing when retry,
* or the Spark job fails with some unknown reasons,
* or {@link #onDataWriterCommit(WriterCommitMessage)} fails,
* or {@link #commit(WriterCommitMessage[])} fails.
*
* If this method fails (by throwing an exception), the underlying data source may require manual
* cleanup.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,10 @@ case class WriteToDataSourceV2Exec(writer: DataSourceWriter, query: SparkPlan) e
rdd,
runTask,
rdd.partitions.indices,
(index, message: WriterCommitMessage) => messages(index) = message
(index, message: WriterCommitMessage) => {
messages(index) = message
writer.onDataWriterCommit(message)
}
)

if (!writer.isInstanceOf[StreamWriter]) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import java.util.{ArrayList, List => JList}

import test.org.apache.spark.sql.sources.v2._

import org.apache.spark.SparkException
import org.apache.spark.{SparkConf, SparkException}
import org.apache.spark.sql.{AnalysisException, QueryTest, Row}
import org.apache.spark.sql.catalyst.expressions.UnsafeRow
import org.apache.spark.sql.execution.exchange.ShuffleExchangeExec
Expand Down Expand Up @@ -197,6 +197,25 @@ class DataSourceV2Suite extends QueryTest with SharedSQLContext {
}
}
}

test("simple counter in writer with onDataWriterCommit") {
Seq(classOf[SimpleWritableDataSource]).foreach { cls =>
withTempPath { file =>
val path = file.getCanonicalPath
assert(spark.read.format(cls.getName).option("path", path).load().collect().isEmpty)

val numPartition = 6
spark.range(0, 10, 1, numPartition).select('id, -'id).write.format(cls.getName)
.option("path", path).save()
checkAnswer(
spark.read.format(cls.getName).option("path", path).load(),
spark.range(10).select('id, -'id))

assert(SimpleCounter.getCounter == numPartition,
"method onDataWriterCommit should be called as many as the number of partitions")
}
}
}
}

class SimpleDataSourceV2 extends DataSourceV2 with ReadSupport {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,14 @@ class SimpleWritableDataSource extends DataSourceV2 with ReadSupport with WriteS

class Writer(jobId: String, path: String, conf: Configuration) extends DataSourceWriter {
override def createWriterFactory(): DataWriterFactory[Row] = {
SimpleCounter.resetCounter
new SimpleCSVDataWriterFactory(path, jobId, new SerializableConfiguration(conf))
}

override def onDataWriterCommit(message: WriterCommitMessage): Unit = {
SimpleCounter.increaseCounter
}

override def commit(messages: Array[WriterCommitMessage]): Unit = {
val finalPath = new Path(path)
val jobPath = new Path(new Path(finalPath, "_temporary"), jobId)
Expand Down Expand Up @@ -183,6 +188,22 @@ class SimpleCSVDataReaderFactory(path: String, conf: SerializableConfiguration)
}
}

private[v2] object SimpleCounter {
private var count: Int = 0

def increaseCounter: Unit = {
count += 1
}

def getCounter: Int = {
count
}

def resetCounter: Unit = {
count = 0
}
}

class SimpleCSVDataWriterFactory(path: String, jobId: String, conf: SerializableConfiguration)
extends DataWriterFactory[Row] {

Expand Down