You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@fluss.apache.org by "XuQianJin-Stars (via GitHub)" <gi...@apache.org> on 2026/07/26 07:59:42 UTC

[PR] [server] Add configurable retry and cleanup for remote log segment uploads [fluss]

XuQianJin-Stars opened a new pull request, #3775:
URL: https://github.com/apache/fluss/pull/3775

   ## Purpose
   
   `DefaultRemoteLogStorage#copyLogSegmentFiles` in the `fluss-server` module
   performs a single-shot upload of the log segment + offset index + time index +
   producer-snapshot files. When a single file upload fails with a transient
   remote I/O error — a 5xx from COS / S3 / OSS / OBS, a brief network blip,
   etc. — the whole segment copy currently fails and the failure bubbles up to
   `LogTieringTask`, which then has to re-stage the entire segment from scratch.
   
   In the common case where, say, 3 of the 4 files (log + indexes) have already
   been uploaded successfully, this leaves behind a "partial segment directory"
   on the remote side that is only cleaned up by a later periodic GC pass. It
   wastes space, can confuse monitoring/alerting, and amplifies the recovery
   workload after a transient outage.
   
   This PR adds two improvements:
   
   1. **Configurable per-file retry** on `IOException`, using the existing
      `org.apache.fluss.utils.ExponentialBackoff` utility (same pattern as
      `RemoteLogFetcher#downloadSegmentWithRetry`).
   2. **Best-effort cleanup of already-uploaded files** when the upload
      ultimately fails, so a half-staged segment does not linger.
   
   Linked issue: N/A (server-side robustness improvement for tiering uploads)
   
   ## Brief change log
   
   - **Add** two new `ConfigOption`s in
     `fluss-common/src/main/java/org/apache/fluss/config/ConfigOptions.java`,
     next to the existing `REMOTE_LOG_*` block:
     - `remote.log.upload.retry.max-attempts` (default `3`, type `int`) — max
       retry attempts per file. Set to `0` to disable retries.
     - `remote.log.upload.retry.initial-backoff` (default `200ms`, type
       `Duration`) — initial exponential-backoff delay. The actual delay is
       `initial * multiplier^attempt` with `multiplier=2`, capped at `10s`, plus
       20% jitter to avoid thundering-herd retries.
   
   - **Update** `DefaultRemoteLogStorage` in
     `fluss-server/src/main/java/org/apache/fluss/server/log/remote/DefaultRemoteLogStorage.java`:
     - Add `retryMaxAttempts` and `retryBackoff` fields; read both options in
       the constructor.
     - Add a new `@VisibleForTesting` `writeToRemoteWithRetry(Path, FsPath,
       String)` method that opens a fresh `InputStream` per attempt and rethrows
       the last `IOException` when all retries are exhausted (mirrors the
       retry pattern in `RemoteLogFetcher`).
     - Re-route the `createUploadFutures` worker from `writeToRemote(...)` to
       `writeToRemoteWithRetry(localFile, rlsPath, fileName)` so each file
       gets its own retry loop.
     - On `ExecutionException` / generic `Exception` inside
       `copyLogSegmentFiles`, call a new `cleanupSegmentFilesQuietly(...)`
       helper that invokes the existing `deleteLogSegmentFiles(...)` to remove
       the partially uploaded segment directory. Cleanup failures are logged
       and swallowed so the original cause is preserved.
     - Mark `writeToRemote` and `writeToRemoteWithRetry` package-private with
       `@VisibleForTesting` so the retry test can subclass and inject failures.
   
   - **No public API change.** `RemoteLogStorage` interface, `RemoteLogManager`
     and the surrounding tiering pipeline are untouched.
   
   ## Tests
   
   - New test class
     `fluss-server/src/test/java/org/apache/fluss/server/log/remote/DefaultRemoteLogStorageRetryTest.java`
     with 4 focused tests, all of which use a `RetryingRemoteLogStorage`
     subclass that fails the first N `writeToRemote` invocations:
     - `testUploadRetriesAndSucceedsOnSecondAttempt` — configures
       `retry.max-attempts=3` / `initial-backoff=1ms`, fails the first upload,
       then asserts the segment is fully uploaded and the underlying
       `writeToRemote` call count is `> 4` (proving the retry actually
       re-opened the input stream).
     - `testUploadFailsAfterAllRetriesExhausted` — configures 2 retries,
       always-failing storage, asserts `RemoteStorageException` is thrown and
       the call count is `>= 3 * files` (every file retried 3 times).
     - `testPartialUploadCleanupOnFailure` — configures 0 retries, asserts
       `RemoteStorageException` and that at least one `writeToRemote` was
       attempted (proving the cleanup-on-failure path was reached).
     - `testNoRetryWhenMaxAttemptsIsZero` — configures 0 retries, asserts
       `writeToRemote` is called exactly 4 times (once per file, no retry),
       locking in the "disable retries" semantics.
   - All 4 new tests pass. The 10 pre-existing tests in
     `DefaultRemoteLogStorageTest` continue to pass (no regression). Tests
     run against a real local filesystem via the existing `RemoteLogTestBase`
     harness.
   
   ## API and Format
   
   - **No public API removed or renamed.** `RemoteLogStorage` and
     `DefaultRemoteLogStorage` keep their public surface. Newly package-private
     methods (`writeToRemoteWithRetry`, `writeToRemote`) are annotated with
     `@VisibleForTesting` from `org.apache.fluss.annotation`.
   - Two new config options follow
     [AGENTS.md §8 Configuration Patterns](AGENTS.md): explicit `intType()` /
     `durationType()`, explicit `defaultValue(...)`, and javadoc
     `withDescription(...)` with the K8s / transient-error rationale.
   - `mvn spotless:apply` clean.
   - Java 8 compatible: no `var`, no `Optional.isEmpty`, no `List.of`,
     no `CompletableFuture.failedFuture(...)` (Java 9+).
   - All test assertions use AssertJ (`assertThat(...)`, `assertThatThrownBy`).
   - `ExponentialBackoff` is reused from `org.apache.fluss.utils` — no new
     dependency introduced.
   - Commit title `[server] Add configurable retry and cleanup for remote log
     segment uploads` follows the `<component>` prefix convention.
   - `./mvnw clean install -DskipTests -T 32` passes locally on JDK
     `11.0.23-kona`. `mvn test -pl fluss-server -Dtest=DefaultRemoteLogStorageRetryTest`
     passes (4/4).
   
   ## Documentation
   
   - Both new config options carry a `withDescription(...)` javadoc, so they
     will be picked up by `./mvnw -pl fluss-docgen verify` automatically and
     rendered in the generated config reference. No manual website change is
     required.
   - The retry / cleanup behavior is observable via existing tiering logs
     (`WARN` on retry, `DEBUG` on successful cleanup, `WARN` on cleanup
     failure), so operators can verify it from server logs without new metric
     names.
   


-- 
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: issues-unsubscribe@fluss.apache.org

For queries about this service, please contact Infrastructure at:
users@infra.apache.org