zhuxiangyi commented on code in PR #9667:
URL: https://github.com/apache/paimon/pull/9667#discussion_r3998730175


##########
paimon-spark/paimon-spark-common/src/main/scala/org/apache/paimon/spark/commands/PaimonSparkWriter.scala:
##########
@@ -472,14 +507,45 @@ case class PaimonSparkWriter(
       postCommit(finalMessages)
       return
     }
-    val activeWriteBuilder =
-      Option(directPostponeWriteBuilder).getOrElse(writeBuilder)
-    val tableCommit = activeWriteBuilder.newCommit()
+    val tableCommit: InnerTableCommit =
+      if (directPostponeWriteBuilder != null) {
+        directPostponeWriteBuilder.newCommit()
+      } else {
+        writeBuilder.asInstanceOf[BatchWriteBuilderImpl].newCommit()
+      }
     if (operation != null) {
       tableCommit.withOperation(operation)
     }
     try {
-      tableCommit.commit(commitMessages.toList.asJava)
+      idempotentCommitIdentifier match {
+        case Some(identifier) =>
+          // Structured Streaming replays a micro-batch with its original 
batch id after a failure.
+          // Committing under a stable commit user lets Paimon skip a replay 
it already committed,
+          // instead of duplicating the whole batch.
+          //
+          // This committer is closed right after the batch, so maintenance 
cannot be left to an
+          // executor that is about to be shut down, nor a failure to a commit 
that never comes.
+          tableCommit.inlineMaintenance(true)
+          if (directPostponeWriteBuilder != null) {
+            // The direct postpone committer runs in strict mode, and 
filterAndCommit bounds its
+            // lookup of the previous commit by the snapshot this write 
started from. The batch
+            // being replayed was committed before that snapshot, so look it 
up without the bound.
+            if (alreadyCommitted(identifier)) {
+              logInfo(s"Micro-batch $identifier is already committed, skipping 
the replay.")

Review Comment:
   Confirmed, and reproduced first with a fail-once `PartitionModification` 
through the Spark direct path: after the first attempt failed past snapshot 
publication, the replay left `attempts=1` and the partition unregistered, 
exactly as you saw. The lookup I had moved into the connector returned before 
`filterCommitted`, and with it the `callback.retry` that half of the semantics 
lives in. I had seen that line and dropped it.
   
   The callbacks live in core, so the direct path commits through 
`filterAndCommit` again, in e7a9976bb. What had forced the connector-side 
lookup was the strict mode bound on `filterCommitted`; I traced it to 
#7239/#7275, an optimisation for a commit user created for one run, which 
cannot have committed before its base snapshot. A caller-provided user that 
survives a restart can have, so `FileStoreCommit` can now look the previous 
commit up without the bound, and both write builders enable that when 
`withCommitUser` was called. Conflict detection keeps the bound, and so does a 
committer created for an explicitly passed user — the staged committer with its 
per-run user goes through that overload, and a core test pins both sides for 
each builder.
   
   While reading `filterAndCommitMultiple` for this I found a second cost of 
the same kind as the file-existence check: it passes `checkAppendFiles=true`, 
so every micro-batch on the ordinary path scanned the base files of the 
partitions it touches for conflicts, which an append of files the batch just 
wrote cannot have. A batch commit never did that scan before this series and 
the Flink committer does not in steady state either; `InnerTableCommit` can now 
turn it off and the sink does. For an append commit the scan has no observable 
effect, so this is covered by the existing suites rather than by a test of its 
own.
   
   Suite is at 12 cases, run on 3.2, 3.4, 3.5, 4.0 and 4.1; core regression 
(`FileStoreCommitTest`, `TableCommitTest`, both `SimpleTableTest` subclasses) 
is green.
   
   The two red jobs are `UTCase and ITCase Others` on JDK 11 and 17, failing in 
`S3FileIOTest` because Testcontainers cannot pull 
`minio/minio:RELEASE.2022-02-07T08-17-33Z`; the same workflow fails the same 
way on the last four runs of master (e.g. run 34697050592), so it is 
independent of this PR.



-- 
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]

Reply via email to