cloud-fan commented on code in PR #58167:
URL: https://github.com/apache/spark/pull/58167#discussion_r4139411707
##########
core/src/main/scala/org/apache/spark/rdd/ReliableCheckpointRDD.scala:
##########
@@ -83,6 +87,20 @@ private[spark] class ReliableCheckpointRDD[T: ClassTag](
throw SparkCoreErrors.invalidCheckpointDirectoryError(path,
expectedFileName)
}
}
+ // If a partition-count metadata file is present and the integrity check
is enabled,
+ // verify no trailing files are missing. Directories written by earlier
Spark versions
+ // have no such file; a missing file is silently tolerated for backward
compatibility.
+ // Set spark.checkpoint.verifyPartitionCount.enabled=false to suppress
this check when
+ // recovering a legitimately truncated directory. See SPARK-58883.
+ if (context.conf.get(CHECKPOINT_VERIFY_PARTITION_COUNT_ENABLED)) {
+ ReliableCheckpointRDD.readPartitionCountFromCheckpointDir(context,
checkpointPath)
+ .foreach { expected =>
+ if (inputFiles.length != expected) {
Review Comment:
**Non-blocking (P2):** The tests only cover this condition with fewer files
than recorded. A regression from `!=` to `<` would therefore leave all of them
green. Please add a case with a recorded count of 4 and contiguous parts
`part-00000` through `part-00004`, and assert the greater-than mismatch details.
**Verification:**
- **Regression:** A checkpoint with recorded count four and contiguous parts
zero through four is rejected with the expected greater-than mismatch details.
##########
core/src/main/scala/org/apache/spark/internal/config/package.scala:
##########
@@ -3167,6 +3167,20 @@ package object config {
.booleanConf
.createWithDefault(false)
+ private[spark] val CHECKPOINT_VERIFY_PARTITION_COUNT_ENABLED =
+ ConfigBuilder("spark.checkpoint.verifyPartitionCount.enabled")
+ .internal()
+ .doc("When true, a checkpoint directory written by a Spark version that
supports " +
Review Comment:
**Non-blocking (P2):** The writer publishes `_num_partitions` regardless of
this setting; only `getPartitions` checks the flag. An application can
therefore write with the flag disabled and later have the same metadata
enforced by a default-configured reader. Could this text state that publication
is always attempted best-effort and that the flag only bypasses the read-time
comparison?
**Verification:**
- **Inspection:** The configuration text agrees with the unconditional
writer call and the reader-only configuration branch for both enabled and
disabled values.
##########
core/src/main/scala/org/apache/spark/rdd/ReliableCheckpointRDD.scala:
##########
@@ -280,6 +327,122 @@ private[spark] object ReliableCheckpointRDD extends
Logging {
}
}
+ /**
+ * Write the partition count of the checkpointed RDD to the checkpoint
directory so that
+ * a later read via [[SparkContext.checkpointFile]] can detect a truncated
directory.
+ * The file is written atomically (temp path then rename) so a torn write
leaves no partial
+ * file, and the temp file is removed if publication fails at any point
after it is created;
+ * the payload layout is documented at [[PARTITION_COUNT_FORMAT_VERSION]].
+ * This is done on a best-effort basis; any exception is caught, logged and
ignored so that
+ * an inability to write the file does not prevent checkpointing. See
SPARK-58883.
+ */
+ private def writePartitionCountToCheckpointDir(
+ sc: SparkContext, partitionCount: Int, checkpointDirPath: Path): Unit = {
+ try {
+ val countFilePath = new Path(checkpointDirPath,
checkpointPartitionCountFileName())
+ val tmpFilePath = new Path(
+ checkpointDirPath, s".${checkpointPartitionCountFileName()}-tmp")
+ val bufferSize = sc.conf.get(BUFFER_SIZE)
+ val fs = countFilePath.getFileSystem(sc.hadoopConfiguration)
+ // A cleanup failure must not mask the publication failure being
handled, so it is only
+ // logged.
+ def deleteTempFile(): Unit = {
+ try {
+ fs.delete(tmpFilePath, false)
Review Comment:
**Non-blocking (P2):** `FileSystem.delete` can return `false` without
throwing when the temp file was not removed. Ignoring that result means a
post-create publication failure can leave `._num_partitions-tmp` behind without
the promised cleanup warning. Please check the Boolean result, warn when it is
false, and cover that failure signal in the injected-filesystem tests.
**Verification:**
- **Regression:** After a post-create publication failure, an injected false
delete result leaves checkpointing recoverable and emits both
publication-failure and cleanup-failure evidence.
- **Behavior:** A successful cleanup removes the temporary metadata path
without adding a cleanup warning.
##########
core/src/main/scala/org/apache/spark/rdd/ReliableCheckpointRDD.scala:
##########
@@ -69,6 +71,8 @@ private[spark] class ReliableCheckpointRDD[T: ClassTag](
* Since the original RDD may belong to a prior application, there is no way
to know a
* priori the number of partitions to expect. This method assumes that the
original set of
* checkpoint files are fully preserved in a reliable storage across
application lifespans.
+ * When the directory was written by a Spark version that supports
SPARK-58883, a
Review Comment:
**Non-blocking (P2):** A current writer can also omit this file because
publication is best-effort and any publication failure is tolerated. The reader
then reaches the same missing-file path, so describing absence as proof of an
older checkpoint is inaccurate. Please cover both legacy checkpoints and failed
best-effort publication in the documentation and diagnostic.
**Verification:**
- **Inspection:** Every changed statement about missing metadata is
consistent with the writer catch path that completes checkpointing without the
final metadata file.
- **Compatibility:** A missing metadata file remains non-authoritative and
recovery continues without partition-count enforcement.
##########
common/utils/src/main/resources/error/error-conditions.json:
##########
@@ -1076,6 +1076,13 @@
],
"sqlState" : "58030"
},
+ "CHECKPOINT_TRUNCATED_DIRECTORY" : {
+ "message" : [
+ "The checkpoint directory <path> was written with <expected>
partition(s) but only <found> partition file(s) were found. One or more
trailing part files may have been lost.",
Review Comment:
**Non-blocking (P2):** This message is also used when `found > expected`.
For example, a valid count of 4 plus contiguous `part-00004` produces
`found=5`, yet the error says only five files were found and that files may
have been lost. Please use direction-neutral mismatch wording or distinguish
missing files from unexpected extra files.
**Verification:**
- **Behavior:** Valid metadata with fewer contiguous part files and valid
metadata with extra contiguous part files produce direction-appropriate
structured failures with the correct path and counts.
- **Compatibility:** Disabled verification and absent or invalid metadata
continue to bypass the new comparison exactly as before.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]