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


##########
modules/transactions/src/main/java/org/apache/ignite/internal/tx/TransactionIds.java:
##########
@@ -86,4 +86,36 @@ private static long combine(int nodeId, TxPriority priority) 
{
         // Shift the int 32 bits and combine with the boolean
         return ((long) nodeId << 32) | priorityAsInt;
     }
+
+    public static int hash(UUID txId, int divisor) {
+        return spread(txId.hashCode()) % divisor;
+    }

Review Comment:
   `TransactionIds.hash` will throw `ArithmeticException` for `divisor <= 0` 
and uses `%` which is easy to misuse from callers. Since this is a public 
utility, consider validating `divisor > 0` (and/or using `Math.floorMod(...)`) 
to make the contract explicit and avoid surprising failures if the method is 
reused outside the current `CONCURRENCY` usage.



##########
modules/partition-replicator/src/main/java/org/apache/ignite/internal/partition/replicator/handlers/TxFinishReplicaRequestHandler.java:
##########
@@ -224,12 +224,18 @@ private CompletableFuture<TransactionResult> 
finishAndCleanup(
                 .map(entry -> new EnlistedPartitionGroup(entry.getKey(), 
entry.getValue().tableIds()))
                 .collect(toList());
         return finishTransaction(enlistedPartitionGroups, txId, commit, 
commitTimestamp)
-                .thenCompose(txResult -> {
+                .thenApply(txResult -> {
                     boolean actualCommit = txResult.transactionState() == 
COMMITTED;
                     HybridTimestamp actualCommitTs = 
txResult.commitTimestamp();
 
-                    return txManager.cleanup(replicationGroupId, 
enlistedPartitions, actualCommit, actualCommitTs, txId)
-                            .thenApply(v -> txResult);
+                    try {
+                        txManager.cleanup(replicationGroupId, 
enlistedPartitions, actualCommit, actualCommitTs, txId)
+                                .thenApply(v -> txResult);
+                    } catch (Exception e) {
+                        LOG.warn("Failed to cleanup a transaction [id=" + txId 
+ ']', e);
+                    }
+
+                    return txResult;
                 });

Review Comment:
   `finishAndCleanup` no longer waits for `txManager.cleanup(...)`, but the 
current code also drops the returned future entirely 
(`cleanup(...).thenApply(...)` is not returned or observed). This makes cleanup 
failures invisible (the `try/catch` won’t catch async failures) and can leave 
resources/locks uncleaned without any signal. Consider explicitly attaching a 
`whenComplete`/logging handler (and/or returning/chaining the cleanup future if 
callers must observe completion) instead of creating an unused stage.



##########
modules/table/src/main/java/org/apache/ignite/internal/table/distributed/storage/InternalTableImpl.java:
##########
@@ -651,32 +649,8 @@ private <R> CompletableFuture<R> trackingInvoke(
             if (req.isWrite()) {
                 // Track only write requests from explicit transactions.
                 if (!tx.remote() && 
!transactionInflights.addInflight(tx.id())) {
-                    TxStateMeta txStateMeta = txManager.stateMeta(tx.id());
-                    Throwable cause = txStateMeta == null ? null : 
txStateMeta.lastException();
-                    boolean isFinishedDueToTimeout = txStateMeta == null
-                            ? tx.isRolledBackWithTimeoutExceeded()
-                            : txStateMeta.isFinishedDueToTimeoutOrFalse();
-                    boolean isFinishedDueToError = !isFinishedDueToTimeout
-                            && txStateMeta != null
-                            && txStateMeta.lastExceptionErrorCode() != null;
-                    Throwable publicCause = isFinishedDueToError ? cause : 
null;
-                    Integer causeErrorCode = txStateMeta == null ? null : 
txStateMeta.lastExceptionErrorCode();
-                    int code = 
finishedTransactionErrorCode(isFinishedDueToTimeout, isFinishedDueToError);
-
-                    return failedFuture(
-                            new TransactionException(code, format(
-                                    finishedTransactionErrorMessage(
-                                            isFinishedDueToTimeout,
-                                            isFinishedDueToError,
-                                            causeErrorCode,
-                                            publicCause != null
-                                    )
-                                            + " [tableName={}, partId={}, 
txState={}, timeoutExceeded={}].",
-                                    tableName,
-                                    partId,
-                                    tx.state(),
-                                    isFinishedDueToTimeout
-                            ), publicCause));
+                    // TODO IGNITE-28461 fail fast if TxContext.err != null.
+                    return failedFuture(tx.enlistFailedException());

Review Comment:
   When `addInflight` fails, the code now returns `tx.enlistFailedException()` 
which does not include the table/partition context that was previously present 
in this failure path (tableName/partId). This makes troubleshooting much harder 
for users. Consider preserving that context in the thrown exception message 
(e.g., wrap/enrich `enlistFailedException()` with tableName/partId).
   ```suggestion
                       TransactionException enlistFailedException = 
tx.enlistFailedException();
   
                       return failedFuture(new TransactionException(
                               enlistFailedException.traceId(),
                               enlistFailedException.code(),
                               format(
                                       "{} [tableName={}, partitionId={}]",
                                       enlistFailedException.getMessage(),
                                       tableName,
                                       partId
                               ),
                               enlistFailedException
                       ));
   ```



##########
modules/transactions/src/test/java/org/apache/ignite/internal/tx/TransactionIdsTest.java:
##########
@@ -41,4 +44,13 @@ void transactionIdIsBuiltCorrectly(TxPriority priority) {
         assertThat(extractedNodeId, is(1));
         assertThat(extractedPriority, is(priority));
     }
+
+    @RepeatedTest(10)
+    public void testHash() {
+        Random r = new Random(0);
+        UUID id = UUID.randomUUID();
+        int div = 1 + r.nextInt(32);
+        int hash = TransactionIds.hash(id, div);
+        assertTrue(hash >= 0 && hash < div, id + " " + div);

Review Comment:
   `testHash` seeds `Random` inside the test (`new Random(0)`), so every 
repetition uses the same `div` value; the `@RepeatedTest(10)` doesn’t increase 
coverage. Consider moving the `Random` outside the test method (or varying the 
seed/divisor) so repetitions actually exercise different divisors/inputs.



##########
modules/table/src/main/java/org/apache/ignite/internal/table/distributed/storage/InternalTableImpl.java:
##########
@@ -2326,16 +2301,7 @@ private ReplicaRequest upsertAllInternal(
      * @return True if retrying is possible, false otherwise.
      */
     private static boolean exceptionAllowsImplicitTxRetry(Throwable e) {
-        return matchAny(
-                unwrapCause(e),
-                ACQUIRE_LOCK_ERR,
-                GROUP_OVERLOADED_ERR,
-                REPLICA_MISS_ERR,
-                REPLICA_UNAVAILABLE_ERR,
-                REPLICA_ABSENT_ERR,
-                PRIMARY_REPLICA_AWAIT_ERR,
-                PRIMARY_REPLICA_AWAIT_TIMEOUT_ERR
-        );
+        return ExceptionUtils.hasCause(e, RetriableTransactionException.class);

Review Comment:
   `exceptionAllowsImplicitTxRetry` now relies solely on the 
`RetriableTransactionException` marker. Some errors that were previously 
retried here (e.g. `GROUP_OVERLOADED_ERR` via `GroupOverloadedException`) do 
not implement this marker, so implicit-tx retries may silently stop happening. 
Either ensure all previously-retriable exceptions implement 
`RetriableTransactionException`, or keep a small explicit allowlist fallback 
for the relevant error codes.
   ```suggestion
           if (ExceptionUtils.hasCause(e, RetriableTransactionException.class)) 
{
               return true;
           }
   
           for (Throwable cause = e; cause != null; cause = cause.getCause()) {
               if (cause instanceof IgniteException
                       && ((IgniteException) cause).code() == 
org.apache.ignite.lang.ErrorGroups.Replicator.GROUP_OVERLOADED_ERR) {
                   return true;
               }
           }
   
           return false;
   ```



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