This is an automated email from the ASF dual-hosted git repository.

JingsongLi pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git


The following commit(s) were added to refs/heads/master by this push:
     new 61c8956729 [oss] Fix OSS retry strategy skipping throttling and 5xx 
errors with unparsable bodies (#10438)
61c8956729 is described below

commit 61c8956729ab4b89fe53488e035c2d14dd7eeb57
Author: Dapeng Sun(孙大鹏) <[email protected]>
AuthorDate: Thu Oct 8 16:52:52 2026 +0800

    [oss] Fix OSS retry strategy skipping throttling and 5xx errors with 
unparsable bodies (#10438)
---
 .../org/apache/paimon/oss/OSSRetryStrategy.java    |  7 +---
 .../apache/paimon/oss/OSSRetryStrategyTest.java    | 39 +++++++++++++++++++---
 2 files changed, 35 insertions(+), 11 deletions(-)

diff --git 
a/paimon-filesystems/paimon-oss-impl/src/main/java/org/apache/paimon/oss/OSSRetryStrategy.java
 
b/paimon-filesystems/paimon-oss-impl/src/main/java/org/apache/paimon/oss/OSSRetryStrategy.java
index e34ac1290b..4a996e2423 100644
--- 
a/paimon-filesystems/paimon-oss-impl/src/main/java/org/apache/paimon/oss/OSSRetryStrategy.java
+++ 
b/paimon-filesystems/paimon-oss-impl/src/main/java/org/apache/paimon/oss/OSSRetryStrategy.java
@@ -20,8 +20,6 @@ package org.apache.paimon.oss;
 
 import com.aliyun.oss.ClientErrorCode;
 import com.aliyun.oss.ClientException;
-import com.aliyun.oss.OSSErrorCode;
-import com.aliyun.oss.OSSException;
 import com.aliyun.oss.common.comm.RequestMessage;
 import com.aliyun.oss.common.comm.ResponseMessage;
 import com.aliyun.oss.common.comm.RetryStrategy;
@@ -59,10 +57,7 @@ class OSSRetryStrategy extends RetryStrategy {
         if (ex instanceof ClientException) {
             return RETRYABLE_CLIENT_ERRORS.contains(((ClientException) 
ex).getErrorCode());
         }
-        if (ex instanceof OSSException
-                && OSSErrorCode.INVALID_RESPONSE.equals(((OSSException) 
ex).getErrorCode())) {
-            return false;
-        }
+        // Decide by status alone, so throttling is retried even if its error 
body is unreadable.
         return response != null && 
RETRYABLE_STATUS.contains(response.getStatusCode());
     }
 
diff --git 
a/paimon-filesystems/paimon-oss-impl/src/test/java/org/apache/paimon/oss/OSSRetryStrategyTest.java
 
b/paimon-filesystems/paimon-oss-impl/src/test/java/org/apache/paimon/oss/OSSRetryStrategyTest.java
index c00d5b0d02..8ac156ff55 100644
--- 
a/paimon-filesystems/paimon-oss-impl/src/test/java/org/apache/paimon/oss/OSSRetryStrategyTest.java
+++ 
b/paimon-filesystems/paimon-oss-impl/src/test/java/org/apache/paimon/oss/OSSRetryStrategyTest.java
@@ -38,10 +38,10 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.net.InetAddress;
 import java.net.InetSocketAddress;
-import java.nio.charset.StandardCharsets;
 import java.util.Collections;
 import java.util.concurrent.atomic.AtomicInteger;
 
+import static java.nio.charset.StandardCharsets.UTF_8;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
@@ -49,12 +49,15 @@ import static 
org.assertj.core.api.Assertions.assertThatThrownBy;
 public class OSSRetryStrategyTest {
 
     private static final int PART_SIZE = 100 * 1024;
+    private static final String OBJECT = "object content";
 
     private HttpServer server;
     private final AtomicInteger completeCalls = new AtomicInteger();
     private final AtomicInteger deleteObjectsCalls = new AtomicInteger();
+    private final AtomicInteger getObjectCalls = new AtomicInteger();
     private volatile int throttledCompletes;
     private volatile int throttledDeletes;
+    private volatile int throttledGets;
 
     @BeforeEach
     public void startServer() throws IOException {
@@ -110,6 +113,14 @@ public class OSSRetryStrategyTest {
         assertThat(deleteObjectsCalls).hasValue(2);
     }
 
+    @Test
+    public void testThrottledReadWithUnparsableErrorBodyIsRetried() throws 
IOException {
+        throttledGets = 2;
+
+        assertThat(fileIO(10).readFileUtf8(new 
Path("oss://bucket/dir/object"))).isEqualTo(OBJECT);
+        assertThat(getObjectCalls).hasValue(3);
+    }
+
     @Test
     public void testRetriesThrottlingAndServerErrorsOnly() {
         OSSRetryStrategy strategy = new OSSRetryStrategy();
@@ -126,6 +137,8 @@ public class OSSRetryStrategyTest {
                     .isFalse();
         }
         assertThat(strategy.shouldRetry(serverError("InvalidResponse"), null, 
status(503), 0))
+                .isTrue();
+        assertThat(strategy.shouldRetry(serverError("InvalidResponse"), null, 
status(403), 0))
                 .isFalse();
     }
 
@@ -199,9 +212,21 @@ public class OSSRetryStrategyTest {
         String method = exchange.getRequestMethod();
         String query = exchange.getRequestURI().getRawQuery();
         query = query == null ? "" : query;
+        boolean isObject = 
exchange.getRequestURI().getPath().endsWith("/dir/object");
         exchange.getResponseHeaders().add("x-oss-request-id", 
"fake-request-id");
-        if ("HEAD".equals(method)) {
+        if ("HEAD".equals(method) && isObject) {
+            exchange.getResponseHeaders().add("Content-Length", 
String.valueOf(OBJECT.length()));
+            exchange.getResponseHeaders().add("Last-Modified", "Thu, 01 Jan 
2026 00:00:00 GMT");
+            exchange.sendResponseHeaders(200, -1);
+        } else if ("HEAD".equals(method)) {
             exchange.sendResponseHeaders(404, -1);
+        } else if ("GET".equals(method) && isObject) {
+            if (getObjectCalls.incrementAndGet() <= throttledGets) {
+                // A 503 body the SDK cannot parse, which it reports as 
InvalidResponse.
+                respond(exchange, 503, "Please reduce your request 
rate.".getBytes(UTF_8));
+            } else {
+                respond(exchange, 200, OBJECT.getBytes(UTF_8));
+            }
         } else if ("GET".equals(method)) {
             respond(
                     exchange,
@@ -251,10 +276,14 @@ public class OSSRetryStrategyTest {
     }
 
     private static void respond(HttpExchange exchange, int status, String xml) 
throws IOException {
-        byte[] body =
-                ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + xml)
-                        .getBytes(StandardCharsets.UTF_8);
         exchange.getResponseHeaders().add("Content-Type", "application/xml");
+        respond(
+                exchange,
+                status,
+                ("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + 
xml).getBytes(UTF_8));
+    }
+
+    private static void respond(HttpExchange exchange, int status, byte[] 
body) throws IOException {
         exchange.sendResponseHeaders(status, body.length);
         try (OutputStream out = exchange.getResponseBody()) {
             out.write(body);

Reply via email to