spuru9 commented on code in PR #29008:
URL: https://github.com/apache/flink/pull/29008#discussion_r4262582408


##########
flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/history/HistoryServer.java:
##########
@@ -301,7 +306,8 @@ public HistoryServer(
                         archiveStorage,
                         archiveMetaInfoCache,
                         lazyFetchExecutorCommonPoolSize,
-                        lazyFetchExecutorIndividualPoolSize);
+                        lazyFetchExecutorIndividualPoolSize,
+                        retainRemoteBeyondLocalLimit);
         applicationArchiveFetcher =
                 new HistoryServerApplicationArchiveFetcher<>(

Review Comment:
   major: Since 2.3 (FLINK-38761), every job is archived under 
`applications/<app>/jobs/`, and the app fetcher never gets this flag. So it 
only covers archives from 2.2 and older JMs, and app jobs can't be fetched on 
demand anyway, because `lazyFetchArchiveProactively` only looks in 
`<refresh-dir>/<job-id>`.



##########
flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/history/HistoryServerArchiveFetcher.java:
##########
@@ -240,9 +282,20 @@ void scanArchives(
                     && processExpiredArchiveDeletion) {
                 events.addAll(cleanupExpiredArchives(archivesToRemove));
             }
-            // clean remote and local
+            if (!archivesExpiredByTtl.isEmpty()) {
+                // clean remote and local; TTL expiry is unaffected by 
retainRemoteBeyondLocalLimit
+                
events.addAll(cleanupArchivesBeyondRetainedLimit(archivesExpiredByTtl));
+            }
             if (!archivesBeyondRetainedLimit.isEmpty()) {
-                
events.addAll(cleanupArchivesBeyondRetainedLimit(archivesBeyondRetainedLimit));
+                if (retainRemoteBeyondLocalLimit) {
+                    // clean local only; the remote archive is left in place 
and remains
+                    // fetchable on demand (e.g. via LAZY archive load mode).
+                    events.addAll(
+                            
cleanupLocalArchivesBeyondRetainedLimit(archivesBeyondRetainedLimit));
+                } else {
+                    // clean remote and local
+                    
events.addAll(cleanupArchivesBeyondRetainedLimit(archivesBeyondRetainedLimit));
+                }
             }

Review Comment:
   minor: Goes with the comment on L259-272. Change L234 to `Map<Path, 
Set<String>> archivesToEvictLocally`; then the flag-off path matches master and 
`cleanupLocalArchivesBeyondRetainedLimit` can go.
   
   ```suggestion
               // clean remote and local
               if (!archivesBeyondRetainedLimit.isEmpty()) {
                   
events.addAll(cleanupArchivesBeyondRetainedLimit(archivesBeyondRetainedLimit));
               }
               // clean local only, the remote archive is kept
               if (!archivesToEvictLocally.isEmpty()) {
                   
events.addAll(cleanupExpiredArchives(archivesToEvictLocally));
               }
   ```



##########
flink-runtime-web/src/test/java/org/apache/flink/runtime/webmonitor/history/HistoryServerArchiveFetcherTest.java:
##########
@@ -369,6 +397,104 @@ void testScanArchivesWithoutFetch() throws Exception {
         assertThat(archiveStorage.exists("overviews/" + jobId + 
".json")).isFalse();
     }
 
+    @TestTemplate
+    void testArchivesBeyondRetainedLimitAreDeletedFromRemoteByDefault() throws 
Exception {
+        JobID retainedJobId = JobID.generate();
+        JobID beyondLimitJobId = JobID.generate();
+        Path beyondLimitArchivePath =
+                createJobArchive(remoteArchiveRootPath, beyondLimitJobId, 
true);
+        createJobArchive(remoteArchiveRootPath, retainedJobId, true);
+
+        // retain only the archive belonging to retainedJobId, regardless of 
file ordering
+        
org.apache.flink.runtime.webmonitor.history.retaining.ArchiveRetainedStrategy
+                retainOnlyRetainedJob =
+                        (file, index) -> 
file.getPath().getName().equals(retainedJobId.toString());
+
+        HistoryServerArchiveFetcher<?> fetcher =
+                createArchiveFetcher(
+                        remoteArchiveRootPath, true, archiveStorage, 
retainOnlyRetainedJob, false);
+
+        fetcher.fetchArchives(EAGER);
+
+        
assertThat(beyondLimitArchivePath.getFileSystem().exists(beyondLimitArchivePath))
+                .as("archive beyond the retained limit should be deleted from 
remote by default")
+                .isFalse();
+    }
+
+    @TestTemplate
+    void testArchivesBeyondRetainedLimitAreKeptRemotelyWhenConfigured() throws 
Exception {
+        JobID retainedJobId = JobID.generate();
+        JobID beyondLimitJobId = JobID.generate();
+        Path beyondLimitArchivePath =
+                createJobArchive(remoteArchiveRootPath, beyondLimitJobId, 
true);
+        createJobArchive(remoteArchiveRootPath, retainedJobId, true);
+
+        // retain only the archive belonging to retainedJobId, regardless of 
file ordering
+        
org.apache.flink.runtime.webmonitor.history.retaining.ArchiveRetainedStrategy
+                retainOnlyRetainedJob =
+                        (file, index) -> 
file.getPath().getName().equals(retainedJobId.toString());
+
+        HistoryServerArchiveFetcher<?> fetcher =
+                createArchiveFetcher(
+                        remoteArchiveRootPath, true, archiveStorage, 
retainOnlyRetainedJob, true);
+
+        fetcher.fetchArchives(EAGER);
+
+        // remote archive beyond the limit must still exist ...
+        
assertThat(beyondLimitArchivePath.getFileSystem().exists(beyondLimitArchivePath))
+                .as(
+                        "archive beyond the retained limit must not be deleted 
from remote when "
+                                + "retainRemoteBeyondLocalLimit is enabled")
+                .isTrue();
+        // ... but must not have been processed/cached locally
+        assertThat(archiveStorage.exists("overviews/" + beyondLimitJobId + 
".json")).isFalse();
+
+        // and it must still be fetchable on demand
+        fetcher.lazyFetchArchiveProactively(beyondLimitJobId.toString(), 
beyondLimitArchivePath);
+        waitForArchiveLoaded(archiveMetaInfoCache, 
beyondLimitJobId.toString());
+        assertThat(archiveStorage.exists("overviews/" + beyondLimitJobId + 
".json")).isTrue();
+    }

Review Comment:
   minor: A second scan here would have caught the re-cleaning in 
`scanArchives`. The application layout and the real `isExpiredByTtl` aren't 
tested either.
   
   ```suggestion
           // and it must still be fetchable on demand
           fetcher.lazyFetchArchiveProactively(beyondLimitJobId.toString(), 
beyondLimitArchivePath);
           waitForArchiveLoaded(archiveMetaInfoCache, 
beyondLimitJobId.toString());
           assertThat(archiveStorage.exists("overviews/" + beyondLimitJobId + 
".json")).isTrue();
   
           // the next scan must neither re-emit events for it nor wipe the 
on-demand copy
           archiveEvents.clear();
           fetcher.fetchArchives(EAGER);
           assertThat(archiveEvents)
                   .extracting(HistoryServerArchiveFetcher.ArchiveEvent::getId)
                   .doesNotContain(beyondLimitJobId.toString());
           assertThat(archiveStorage.exists("overviews/" + beyondLimitJobId + 
".json")).isTrue();
       }
   ```



##########
flink-runtime-web/src/test/java/org/apache/flink/runtime/webmonitor/history/HistoryServerArchiveFetcherTest.java:
##########
@@ -369,6 +397,104 @@ void testScanArchivesWithoutFetch() throws Exception {
         assertThat(archiveStorage.exists("overviews/" + jobId + 
".json")).isFalse();
     }
 
+    @TestTemplate
+    void testArchivesBeyondRetainedLimitAreDeletedFromRemoteByDefault() throws 
Exception {
+        JobID retainedJobId = JobID.generate();
+        JobID beyondLimitJobId = JobID.generate();
+        Path beyondLimitArchivePath =
+                createJobArchive(remoteArchiveRootPath, beyondLimitJobId, 
true);
+        createJobArchive(remoteArchiveRootPath, retainedJobId, true);
+
+        // retain only the archive belonging to retainedJobId, regardless of 
file ordering
+        
org.apache.flink.runtime.webmonitor.history.retaining.ArchiveRetainedStrategy
+                retainOnlyRetainedJob =
+                        (file, index) -> 
file.getPath().getName().equals(retainedJobId.toString());
+
+        HistoryServerArchiveFetcher<?> fetcher =
+                createArchiveFetcher(
+                        remoteArchiveRootPath, true, archiveStorage, 
retainOnlyRetainedJob, false);
+
+        fetcher.fetchArchives(EAGER);
+
+        
assertThat(beyondLimitArchivePath.getFileSystem().exists(beyondLimitArchivePath))
+                .as("archive beyond the retained limit should be deleted from 
remote by default")
+                .isFalse();
+    }
+
+    @TestTemplate
+    void testArchivesBeyondRetainedLimitAreKeptRemotelyWhenConfigured() throws 
Exception {
+        JobID retainedJobId = JobID.generate();
+        JobID beyondLimitJobId = JobID.generate();
+        Path beyondLimitArchivePath =
+                createJobArchive(remoteArchiveRootPath, beyondLimitJobId, 
true);
+        createJobArchive(remoteArchiveRootPath, retainedJobId, true);
+
+        // retain only the archive belonging to retainedJobId, regardless of 
file ordering
+        
org.apache.flink.runtime.webmonitor.history.retaining.ArchiveRetainedStrategy
+                retainOnlyRetainedJob =
+                        (file, index) -> 
file.getPath().getName().equals(retainedJobId.toString());
+
+        HistoryServerArchiveFetcher<?> fetcher =

Review Comment:
   minor: After a rebase, these locals hide the `fetcher` field that 
FLINK-40567 added, so `tearDown` never closes them and the `@TempDir` cleanup 
race comes back. Assign the field instead (same at L413 and L484).



##########
flink-core/src/main/java/org/apache/flink/configuration/HistoryServerOptions.java:
##########
@@ -178,6 +178,34 @@ public class HistoryServerOptions {
                                     .text(LEGACY_NOTE_MESSAGE)
                                     .build());
 
+    /**
+     * If this option is enabled, job archives that fall outside {@link
+     * #HISTORY_SERVER_RETAINED_JOBS} are no longer processed/refreshed 
locally, but are kept in the
+     * remote archive directory instead of being deleted. They remain 
reachable on demand (e.g. by
+     * directly requesting {@code /jobs/&lt;jobId&gt;} in {@link 
HistoryServerArchiveLoadMode#LAZY}
+     * mode).
+     */
+    public static final ConfigOption<Boolean> 
HISTORY_SERVER_RETAIN_REMOTE_BEYOND_LOCAL_LIMIT =
+            key("historyserver.archive.retain-remote-beyond-local-limit")

Review Comment:
   major: FLIP-505 was voted with `cached-retained-jobs` and 
`num-cached-most-recently-viewed-jobs`, not this key. Can you get this agreed 
on dev@ (the "Reopening FLIP-505" thread got no replies), or switch to the 
voted keys? #26878 is also still open on the same JIRA.



##########
flink-runtime-web/src/main/java/org/apache/flink/runtime/webmonitor/history/HistoryServerArchiveFetcher.java:
##########
@@ -223,9 +257,17 @@ void scanArchives(
 
                     fileOrderedIndexOnModifiedTime++;
                     if (!retainedStrategy.shouldRetain(archive, 
fileOrderedIndexOnModifiedTime)) {
-                        archivesBeyondRetainedLimit
-                                .computeIfAbsent(refreshDir, ignored -> new 
HashSet<>())
-                                .add(archivePath);
+                        if (retainedStrategy.isExpiredByTtl(archive)) {
+                            // TTL expiry always applies remote deletion, 
regardless of
+                            // retainRemoteBeyondLocalLimit, which only 
concerns the count limit.
+                            archivesExpiredByTtl
+                                    .computeIfAbsent(refreshDir, ignored -> 
new HashSet<>())
+                                    .add(archivePath);
+                        } else {
+                            archivesBeyondRetainedLimit
+                                    .computeIfAbsent(refreshDir, ignored -> 
new HashSet<>())
+                                    .add(archivePath);
+                        }
                         continue;
                     }

Review Comment:
   major: Archives past the limit stay in the remote dir, so every scan cleans 
all of them again (an INFO log, 3 deletes and a `DELETED` event each). That 
same pass also wipes or cancels whatever was fetched on demand. Only evict what 
an earlier scan cached:
   
   ```suggestion
                       if (!retainedStrategy.shouldRetain(archive, 
fileOrderedIndexOnModifiedTime)) {
                           if (retainRemoteBeyondLocalLimit
                                   && 
!retainedStrategy.isExpiredByTtl(archive)) {
                               // the remote copy stays; only evict what an 
earlier scan cached
                               if (cachedArchivesPerRefreshDirectory
                                       .get(refreshDir)
                                       .contains(archiveId)) {
                                   archivesToEvictLocally
                                           .computeIfAbsent(refreshDir, ignored 
-> new HashSet<>())
                                           .add(archiveId);
                               }
                           } else {
                               archivesBeyondRetainedLimit
                                       .computeIfAbsent(refreshDir, ignored -> 
new HashSet<>())
                                       .add(archivePath);
                           }
                           continue;
                       }
   ```



##########
flink-core/src/main/java/org/apache/flink/configuration/HistoryServerOptions.java:
##########
@@ -178,6 +178,34 @@ public class HistoryServerOptions {
                                     .text(LEGACY_NOTE_MESSAGE)
                                     .build());
 
+    /**
+     * If this option is enabled, job archives that fall outside {@link
+     * #HISTORY_SERVER_RETAINED_JOBS} are no longer processed/refreshed 
locally, but are kept in the
+     * remote archive directory instead of being deleted. They remain 
reachable on demand (e.g. by
+     * directly requesting {@code /jobs/&lt;jobId&gt;} in {@link 
HistoryServerArchiveLoadMode#LAZY}
+     * mode).
+     */
+    public static final ConfigOption<Boolean> 
HISTORY_SERVER_RETAIN_REMOTE_BEYOND_LOCAL_LIMIT =
+            key("historyserver.archive.retain-remote-beyond-local-limit")
+                    .booleanType()
+                    .defaultValue(false)
+                    .withDescription(
+                            Description.builder()
+                                    .text(
+                                            "Whether job archives beyond the 
limit configured by %s should still be "
+                                                    + "retained in the remote 
archive directory defined by %s, instead of being "
+                                                    + "deleted. ",
+                                            
code(HISTORY_SERVER_RETAINED_JOBS_KEY),
+                                            
code(HISTORY_SERVER_ARCHIVE_DIRS.key()))
+                                    .text(
+                                            "When enabled, such archives are 
no longer polled/processed locally, but remain "
+                                                    + "fetchable on demand 
when %s is set to %s. ",
+                                            
code("historyserver.archive.load.mode"), code("LAZY"))
+                                    .text(
+                                            "This option has no effect unless 
%s is set to a value other than %s. ",
+                                            
code(HISTORY_SERVER_RETAINED_JOBS_KEY), code("-1"))

Review Comment:
   minor: Worth stating the cost in the description: the remote dir then grows 
without limit and gets listed on every refresh, so pair it with `retained-ttl`. 
A line in `historyserver.md` (en and zh) would help too.



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