Copilot commented on code in PR #7779:
URL: https://github.com/apache/ignite-3/pull/7779#discussion_r2938319331


##########
modules/client-handler/src/main/java/org/apache/ignite/client/handler/requests/table/ClientTableCommon.java:
##########
@@ -538,6 +539,24 @@ public static TableNotFoundException 
tableIdNotFoundException(Integer tableId) {
                                         
MESSAGE_TX_ALREADY_FINISHED_DUE_TO_TIMEOUT + " [tx=" + remote + "].");
                             }
 
+                            // Track this remote enlistment for cleanup if 
client disconnects.
+                            try {
+                                resources.addTxCleaner(txId, tableId, 
commitPart, txManager, (IgniteTablesInternal) tables);
+                            } catch (IgniteInternalCheckedException e) {
+                                // Client disconnected (resource registry 
closed).
+                                try {
+                                    remote.rollback();
+                                } catch (Exception ex) {
+                                    e.addSuppressed(ex);
+                                }
+
+                                throw new IgniteException(e.traceId(), 
e.code(), "Client disconnected, tx rolled back: " + remote, e);
+                            }
+
+                            // Stop tracking on tx finish.
+                            txManager.resourceRegistry().register(
+                                    new FullyQualifiedResourceId(txId, txId), 
txId, () -> () -> resources.removeTxCleaner(txId));

Review Comment:
   In this register() call, the second argument (`remoteHostId`) is supposed to 
be the cluster node UUID that created/triggered the resource. Passing `txId` 
here will make ResourceVacuumManager treat it as an orphan (no such node in 
topology) and close it on the next vacuum cycle, which can remove the cleaner 
while the tx is still active and break disconnect cleanup. Use the coordinator 
node id (`coord`) or another real cluster node id as `remoteHostId` instead of 
`txId`.
   



##########
modules/core/src/main/java/org/apache/ignite/internal/util/ExceptionUtils.java:
##########
@@ -455,6 +456,31 @@ private static boolean hasCauseOrSuppressedInternal(
         return false;
     }
 
+    /**
+     * Checks if the given throwable is already present in the cause or 
suppressed hierarchy of the given throwable.
+     *
+     * @param t Throwable.
+     * @param dejaVu Known throwables.
+     * @return True if seen before, false otherwise.
+     */
+    public static boolean existingCauseOrSuppressed(Throwable t, 
HashSet<Throwable> dejaVu) {
+        if (t == null) {
+            return false;
+        }
+
+        if (!dejaVu.add(t)) {
+            return true;
+        }
+
+        for (Throwable sup : t.getSuppressed()) {
+            if (existingCauseOrSuppressed(sup, dejaVu)) {
+                return true;
+            }
+        }
+
+        return existingCauseOrSuppressed(t.getCause(), dejaVu);
+    }

Review Comment:
   The new `existingCauseOrSuppressed` helper exposes `HashSet<Throwable>` in 
its public signature and even accepts `t == null` while the parameter isn't 
marked nullable. Consider changing the signature to use `Set<Throwable>` (and 
annotate `t` as `@Nullable` if null is supported) so callers can reuse the 
IdentityHashMap-backed Set pattern already used elsewhere in this class and so 
the API isn't tied to a concrete collection type.



##########
modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientResourceRegistry.java:
##########
@@ -112,6 +123,44 @@ public ClientResource remove(long id) throws 
IgniteInternalCheckedException {
         }
     }
 
+    /**
+     * Records that a remote transaction enlisted a partition on this node.
+     *
+     * @param txId Transaction ID.
+     * @param tableId Table ID.
+     * @param partitionId Partition ID.

Review Comment:
   The Javadoc for `addTxCleaner` doesn't mention the `txManager` and `tables` 
parameters that are part of the method signature. Please update the comment so 
it documents all parameters (or adjust the signature if those should be stored 
in the registry instead).
   



##########
modules/client-handler/src/main/java/org/apache/ignite/client/handler/ClientResourceRegistry.java:
##########
@@ -122,24 +171,35 @@ public void close() {
 
         busyLock.block();
 
-        IgniteInternalException ex = null;
+        AtomicReference<IgniteInternalException> ex = new AtomicReference<>();
+        var dejaVu = new HashSet<Throwable>();
 
-        for (ClientResource r : res.values()) {
+        Consumer<Runnable> releaseSafe = r -> {
             try {
-                r.release();
-            } catch (Exception e) {
-                if (ex == null) {
-                    ex = new IgniteInternalException(e);
-                } else {
-                    ex.addSuppressed(e);
+                r.run();
+            } catch (Throwable e) {
+                if (ex.get() == null) {
+                    ex.set(new IgniteInternalException(e));
+                    existingCauseOrSuppressed(e, dejaVu); // Seed dejaVu.
+                } else if (!existingCauseOrSuppressed(e, dejaVu)) {
+                    ex.get().addSuppressed(e);
                 }
             }
+        };
+
+        for (ClientResource r : res.values()) {
+            releaseSafe.accept(r::release);
         }
 
         res.clear();
 
-        if (ex != null) {
-            throw ex;
+        for (var cleaner : txCleaners.values()) {
+            // Don't block the thread, clean in background. 
discardLocalWriteIntents swallows errors anyway.
+            releaseSafe.accept(cleaner::clean);

Review Comment:
   `releaseSafe` accepts a `Runnable`, but 
`ClientTxPartitionEnlistmentCleaner.clean()` returns `CompletableFuture<Void>`, 
so `cleaner::clean` is not compatible with `Runnable` and should not compile. 
Wrap it in a void-compatible lambda (e.g., invoke `clean()` inside `Runnable`) 
or change `releaseSafe` to accept a functional type that can return a value 
(Supplier/Callable).
   



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