MartijnVisser commented on code in PR #259:
URL: 
https://github.com/apache/flink-connector-jdbc/pull/259#discussion_r4543266147


##########
flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/core/datastream/sink/committer/JdbcCommitter.java:
##########
@@ -21,40 +21,51 @@
 import org.apache.flink.api.connector.sink2.Committer;
 import org.apache.flink.connector.base.DeliveryGuarantee;
 import org.apache.flink.connector.jdbc.JdbcExactlyOnceOptions;
-import 
org.apache.flink.connector.jdbc.core.datastream.sink.writer.JdbcWriterState;
 import 
org.apache.flink.connector.jdbc.datasource.connections.JdbcConnectionProvider;
 import 
org.apache.flink.connector.jdbc.datasource.connections.xa.XaConnectionProvider;
-import 
org.apache.flink.connector.jdbc.datasource.transactions.xa.XaTransaction;
 import 
org.apache.flink.connector.jdbc.datasource.transactions.xa.domain.TransactionId;
+import 
org.apache.flink.connector.jdbc.datasource.transactions.xa.exceptions.TransientXaException;
+import org.apache.flink.util.ExceptionUtils;
 import org.apache.flink.util.FlinkRuntimeException;
 
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import javax.transaction.xa.Xid;
+
 import java.io.IOException;
 import java.util.Collection;
+import java.util.OptionalLong;
 
 /** The responsible for committing the {@link JdbcCommitable}. */
 @Internal
 public class JdbcCommitter implements Committer<JdbcCommitable> {
 
-    private transient XaTransaction jdbcTransaction;
+    private static final Logger LOG = 
LoggerFactory.getLogger(JdbcCommitter.class);
+
+    private transient XaConnectionProvider xaConnectionProvider;
+    private transient boolean opened;

Review Comment:
   The lazy open is new in this PR, so I fixed it here: the committer closes 
the provider if `open()` fails. `testCloseConnectionOfFailedOpen` and 
`testFailToConnect` fail without it. aa48f483
   



##########
flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/datasource/transactions/xa/domain/TransactionId.java:
##########
@@ -143,6 +143,14 @@ public TransactionId withAttemptsIncremented() {
                 jobId, subtaskId, numberOfSubtasks, checkpointId, attempts + 
1, restored);
     }
 
+    public byte[] getJobId() {

Review Comment:
   `getGlobalTransactionId()` builds a new array on every call, so it was only 
`getJobId()` that handed out the internal one. It returns a copy now, pinned by 
`testGetJobIdReturnsACopy`. 8940a09e
   



##########
flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/JdbcExactlyOnceOptions.java:
##########
@@ -27,15 +27,18 @@
  * JDBC exactly once sink options.
  *
  * <p><b>maxCommitAttempts</b> - maximum number of commit attempts to make per 
transaction; must be
- * > 0; state size is proportional to the product of max number of in-flight 
snapshots and this
- * number.
+ * > 0. The committer retries a transient failure right away, and Flink stops 
after {@code
+ * sink.committer.retries} retries, so a transaction gets at most the smaller 
of maxCommitAttempts
+ * and {@code sink.committer.retries} + 1 attempts.
  *
- * <p><b>allowOutOfOrderCommits</b> - If true, all prepared transactions will 
be attempted to commit
- * regardless of any transient failures during this operation. This may lead 
to inconsistency.
- * Default: false.
+ * <p><b>allowOutOfOrderCommits</b> - If true, all prepared transactions 
restored from a writer

Review Comment:
   I've deprecated the builder method and the getter, and I'll add it to the 
release note. It only still matters when a writer restores a 4.1 state with 
more than one prepared transaction: if the first commit fails again, it then 
still tries the others. 570badc9
   



##########
flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/datasource/transactions/xa/XaTransaction.java:
##########
@@ -51,7 +52,7 @@ public class XaTransaction implements Serializable, 
AutoCloseable {
     private final XaConnectionProvider xaConnectionProvider;
     private final JdbcExactlyOnceOptions exactlyOnceOptions;
 
-    // checkpoints and the corresponding transactions waiting for completion 
notification from JM
+    // prepared xids from the restored writer state, committed on open

Review Comment:
   Added to `XaTransactionTest`, on Derby and PostgreSQL, with states of the 
released 4.1.0 writer after one and two transient commit failures. 2dfa8b36
   



##########
flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/datasource/transactions/xa/XaTransaction.java:
##########
@@ -141,6 +151,52 @@ public void checkState() {
                 "inconsistent internal state");
     }
 
+    /**
+     * Rolls back the prepared transactions of this writer from the given 
checkpoint on. A failed
+     * attempt may have left them prepared, the restart replays their records, 
and the committer
+     * only commits transactions of earlier checkpoints. It runs whether or 
not {@code
+     * discoverAndRollbackOnRecovery} is set.
+     */
+    public void rollbackPreparedTxs(long checkpointId) {
+        Collection<Xid> recovered;
+        try {
+            recovered = xaConnectionProvider.recover();
+        } catch (Exception e) {
+            LOG.warn("unable to recover prepared transactions", e);
+            return;
+        }
+        for (Xid xid : recovered) {
+            if (isOwnFromCheckpoint(xid, checkpointId)) {
+                LOG.info("rolling back transaction of a failed attempt, 
xid={}", xid);
+                xaConnectionProvider.rollback(xid);
+            }
+        }
+    }
+
+    /**
+     * A transaction id names the job, the subtask and the parallelism, but 
not the sink. Another
+     * exactly-once sink of the job at the same parallelism can't run, as it 
starts the same xids.
+     * At another parallelism its xids differ only in the parallelism, so that 
has to match too.
+     */
+    private boolean isOwnFromCheckpoint(Xid xid, long checkpointId) {
+        if (xid.getFormatId() != baseTransaction.getFormatId()
+                || !Arrays.equals(
+                        xid.getGlobalTransactionId(), 
baseTransaction.getGlobalTransactionId())) {
+            return false;
+        }
+        try {
+            TransactionId transactionId =
+                    TransactionId.createFromXid(
+                            xid.getFormatId(),
+                            xid.getGlobalTransactionId(),
+                            xid.getBranchQualifier());
+            return transactionId.getNumberOfSubtasks() == 
baseTransaction.getNumberOfSubtasks()

Review Comment:
   Added to the docs, but without `discoverAndRollbackOnRecovery`: its lookup 
never matches a driver Xid, so it rolls nothing back. At another parallelism 
the writer still rolls back the transaction the restored state names; only 
those of later checkpoints stay 
(`testOpenAtOtherParallelismKeepsLaterLeftovers`, 8940a09e). FLINK-41101 (#262) 
covers them. 627b6dde
   



##########
flink-connector-jdbc-core/src/main/java/org/apache/flink/connector/jdbc/datasource/transactions/xa/XaTransaction.java:
##########
@@ -141,6 +151,52 @@ public void checkState() {
                 "inconsistent internal state");
     }
 
+    /**
+     * Rolls back the prepared transactions of this writer from the given 
checkpoint on. A failed
+     * attempt may have left them prepared, the restart replays their records, 
and the committer
+     * only commits transactions of earlier checkpoints. It runs whether or 
not {@code
+     * discoverAndRollbackOnRecovery} is set.
+     */
+    public void rollbackPreparedTxs(long checkpointId) {
+        Collection<Xid> recovered;
+        try {
+            recovered = xaConnectionProvider.recover();
+        } catch (Exception e) {
+            LOG.warn("unable to recover prepared transactions", e);

Review Comment:
   Agreed, it fails the task now. MySQL then needs `XA_RECOVER_ADMIN` and 
Oracle `SELECT` on `DBA_PENDING_TRANSACTIONS` and `EXECUTE` on `SYS.DBMS_XA`, 
also with `withRecoveredAndRollback(false)`, which goes in the release note. A 
writer that fails to start also closes its connection now (30d7f3ab). fc188bff
   



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