SebastianGruza commented on code in PR #3204:
URL: https://github.com/apache/hugegraph/pull/3204#discussion_r3984259105


##########
hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/NodeTxExecutor.java:
##########
@@ -376,32 +378,44 @@ <T> Optional<T> retryingInvoke(Supplier<T> supplier) {
         return IntStream.rangeClosed(0, NODE_MAX_RETRYING_TIMES).boxed()
                         .map(
                                 i -> {
+                                    if 
(Thread.currentThread().isInterrupted()) {
+                                        // The caller (e.g. a REST worker 
hitting
+                                        // restserver.request_timeout) gave 
up: stop
+                                        // retrying instead of holding its 
thread.
+                                        throw HgStoreClientException.of(
+                                                "Interrupted before retry " + 
i);
+                                    }
                                     T buffer = null;
                                     try {
                                         buffer = supplier.get();
                                     } catch (Throwable t) {
-                                        if (i + 1 <= NODE_MAX_RETRYING_TIMES) {
-                                            try {
-                                                int sleepTime;
-                                                // The first three times try 
once every second
-                                                if (i < 3) {
-                                                    sleepTime = 1;
-                                                } else {
-                                                    // Subsequent incremental
-                                                    sleepTime = i - 1;
-                                                }
-                                                log.info("Waiting {} seconds " 
+
-                                                         "for the next try.",
-                                                         sleepTime);
-                                                Thread.sleep(sleepTime * 
1000L);
-                                            } catch (InterruptedException e) {
-                                                log.error("Failed to sleep", 
e);
-                                            }
-                                        } else {
+                                        if (i + 1 > NODE_MAX_RETRYING_TIMES) {
                                             log.error(maxTryMsg, t);
                                             throw HgStoreClientException.of(
                                                     t.getMessage(), t);
                                         }
+                                        if (!isRetryable(t)) {

Review Comment:
   Done in 8adf522. `commitSessions()` now collects every session failure in a 
`ConcurrentLinkedQueue` and throws one `HgStoreClientException` (first failure 
unwrapped one level as before, the others attached as suppressed); 
`isRetryable()` walks the suppressed failures as well as the cause chain 
(identity-set guarded against cycles), so one `DEADLINE_EXCEEDED` among several 
failures makes the attempt non-retryable regardless of which partition reported 
first. Tests: `testMixedCommitFailuresAreNotRetried` (two sessions, 
`UNAVAILABLE` + `DEADLINE_EXCEEDED` thrown fresh on every call, exactly one 
attempt, both sessions rolled back, one suppressed) and 
`testAllRetryableCommitFailuresAreRetried`.



##########
hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/NodeTxExecutor.java:
##########
@@ -376,32 +378,44 @@ <T> Optional<T> retryingInvoke(Supplier<T> supplier) {
         return IntStream.rangeClosed(0, NODE_MAX_RETRYING_TIMES).boxed()
                         .map(
                                 i -> {
+                                    if 
(Thread.currentThread().isInterrupted()) {
+                                        // The caller (e.g. a REST worker 
hitting
+                                        // restserver.request_timeout) gave 
up: stop
+                                        // retrying instead of holding its 
thread.
+                                        throw HgStoreClientException.of(
+                                                "Interrupted before retry " + 
i);
+                                    }
                                     T buffer = null;
                                     try {
                                         buffer = supplier.get();
                                     } catch (Throwable t) {
-                                        if (i + 1 <= NODE_MAX_RETRYING_TIMES) {
-                                            try {
-                                                int sleepTime;
-                                                // The first three times try 
once every second
-                                                if (i < 3) {
-                                                    sleepTime = 1;
-                                                } else {
-                                                    // Subsequent incremental
-                                                    sleepTime = i - 1;
-                                                }
-                                                log.info("Waiting {} seconds " 
+
-                                                         "for the next try.",
-                                                         sleepTime);
-                                                Thread.sleep(sleepTime * 
1000L);
-                                            } catch (InterruptedException e) {
-                                                log.error("Failed to sleep", 
e);
-                                            }
-                                        } else {
+                                        if (i + 1 > NODE_MAX_RETRYING_TIMES) {
                                             log.error(maxTryMsg, t);
                                             throw HgStoreClientException.of(
                                                     t.getMessage(), t);
                                         }
+                                        if (!isRetryable(t)) {
+                                            // A deadline or a cancellation 
will not
+                                            // get better by waiting the full 
deadline
+                                            // again; fail fast and let the 
caller decide.
+                                            log.warn("Not retrying after: {}",

Review Comment:
   Done in 8adf522 — the throwable is passed as the trailing argument.



##########
hugegraph-store/hg-store-client/src/main/java/org/apache/hugegraph/store/client/NodeTxExecutor.java:
##########
@@ -376,32 +378,44 @@ <T> Optional<T> retryingInvoke(Supplier<T> supplier) {
         return IntStream.rangeClosed(0, NODE_MAX_RETRYING_TIMES).boxed()
                         .map(
                                 i -> {
+                                    if 
(Thread.currentThread().isInterrupted()) {
+                                        // The caller (e.g. a REST worker 
hitting
+                                        // restserver.request_timeout) gave 
up: stop
+                                        // retrying instead of holding its 
thread.
+                                        throw HgStoreClientException.of(
+                                                "Interrupted before retry " + 
i);
+                                    }
                                     T buffer = null;
                                     try {
                                         buffer = supplier.get();
                                     } catch (Throwable t) {
-                                        if (i + 1 <= NODE_MAX_RETRYING_TIMES) {
-                                            try {
-                                                int sleepTime;
-                                                // The first three times try 
once every second
-                                                if (i < 3) {
-                                                    sleepTime = 1;
-                                                } else {
-                                                    // Subsequent incremental
-                                                    sleepTime = i - 1;
-                                                }
-                                                log.info("Waiting {} seconds " 
+
-                                                         "for the next try.",
-                                                         sleepTime);
-                                                Thread.sleep(sleepTime * 
1000L);
-                                            } catch (InterruptedException e) {
-                                                log.error("Failed to sleep", 
e);
-                                            }
-                                        } else {
+                                        if (i + 1 > NODE_MAX_RETRYING_TIMES) {
                                             log.error(maxTryMsg, t);

Review Comment:
   Done in 8adf522 — `isRetryable()` is checked first, the attempt budget 
second.



##########
hugegraph-store/hg-store-test/src/main/java/org/apache/hugegraph/store/client/NodeTxExecutorTest.java:
##########
@@ -115,4 +123,67 @@ public void testParallelReplacementUsesOneCurrentSession() 
throws Exception {
             workers.shutdownNow();
         }
     }
+
+    @Test
+    public void testIsRetryableClassifiesFailures() {
+        
assertFalse(NodeTxExecutor.isRetryable(Status.DEADLINE_EXCEEDED.asRuntimeException()));
+        
assertFalse(NodeTxExecutor.isRetryable(Status.CANCELLED.asRuntimeException()));
+        assertFalse(NodeTxExecutor.isRetryable(new 
InterruptedException("interrupted")));
+        // The status is usually wrapped by the time it reaches the retry loop
+        assertFalse(NodeTxExecutor.isRetryable(HgStoreClientException.of(
+                "commit failed", new 
RuntimeException(Status.DEADLINE_EXCEEDED.asRuntimeException()))));
+        
assertTrue(NodeTxExecutor.isRetryable(Status.UNAVAILABLE.asRuntimeException()));
+        assertTrue(NodeTxExecutor.isRetryable(new RuntimeException("simulated 
transport failure")));
+    }
+
+    @Test
+    public void testDeadlineExceededIsNotRetried() {
+        NodeTxExecutor executor = NodeTxExecutor.graphOf("graph", null);
+        AtomicInteger attempts = new AtomicInteger();
+        HgStoreClientException e = assertThrows(HgStoreClientException.class, 
() ->
+                executor.retryingInvoke(() -> {
+                    attempts.incrementAndGet();
+                    throw Status.DEADLINE_EXCEEDED.withDescription("deadline 
exceeded after 20s")
+                                                  .asRuntimeException();
+                }));
+        assertEquals(1, attempts.get());
+        assertTrue(e.getMessage(), 
e.getMessage().contains("DEADLINE_EXCEEDED"));
+    }
+
+    @Test
+    public void testInterruptStopsRetrying() {
+        // A REST worker hitting restserver.request_timeout (or a Gremlin 
evaluationTimeout)
+        // interrupts the calling thread while the store call is failing; the 
loop must
+        // stop instead of sleeping and retrying with the interrupt swallowed.
+        NodeTxExecutor executor = NodeTxExecutor.graphOf("graph", null);
+        AtomicInteger attempts = new AtomicInteger();
+        try {
+            assertThrows(HgStoreClientException.class, () ->
+                    executor.retryingInvoke(() -> {
+                        attempts.incrementAndGet();
+                        Thread.currentThread().interrupt();
+                        throw new RuntimeException("simulated transport 
failure");
+                    }));
+            assertEquals(1, attempts.get());

Review Comment:
   Done in 8adf522 — `testInterruptBeforeCallSkipsTheAttempt` sets the flag 
before the call and asserts `attempts == 0` with the flag still set.



##########
hugegraph-store/hg-store-test/src/main/java/org/apache/hugegraph/store/client/NodeTxExecutorTest.java:
##########
@@ -115,4 +123,67 @@ public void testParallelReplacementUsesOneCurrentSession() 
throws Exception {
             workers.shutdownNow();
         }
     }
+
+    @Test
+    public void testIsRetryableClassifiesFailures() {
+        
assertFalse(NodeTxExecutor.isRetryable(Status.DEADLINE_EXCEEDED.asRuntimeException()));
+        
assertFalse(NodeTxExecutor.isRetryable(Status.CANCELLED.asRuntimeException()));
+        assertFalse(NodeTxExecutor.isRetryable(new 
InterruptedException("interrupted")));
+        // The status is usually wrapped by the time it reaches the retry loop
+        assertFalse(NodeTxExecutor.isRetryable(HgStoreClientException.of(
+                "commit failed", new 
RuntimeException(Status.DEADLINE_EXCEEDED.asRuntimeException()))));

Review Comment:
   Done in 8adf522 — line wrapped, unused import removed; no added line exceeds 
100 characters now.



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

Reply via email to