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

ramanathan1504 pushed a commit to branch 2.x
in repository https://gitbox.apache.org/repos/asf/logging-log4j2.git


The following commit(s) were added to refs/heads/2.x by this push:
     new 7775e125ae Adds `maxRandomDelay` configure to Rolling Appenders (#4165)
7775e125ae is described below

commit 7775e125ae10f28d64b6442faaad56adc3c7849a
Author: Volkan Yazıcı <[email protected]>
AuthorDate: Wed Jul 1 11:20:48 2026 +0200

    Adds `maxRandomDelay` configure to Rolling Appenders (#4165)
    
    * Rework random delay for Rolling Appender action chain
    
    * Stabilize `RollingAppenderDirectCronTest`
---
 .../rolling/FileExtensionCompressDelayTest.java    | 179 ----------------
 .../rolling/RollingAppenderDirectCronTest.java     |  12 +-
 .../rolling/RollingAppenderRandomDelayTest.java    | 235 +++++++++++++++++++++
 .../rolling/action/GzCompressActionTest.java       |  70 ++----
 .../rolling/action/ZipCompressActionTest.java      |  70 ++----
 .../log4j/core/appender/RollingFileAppender.java   |  17 +-
 .../appender/RollingRandomAccessFileAppender.java  |  12 ++
 .../logging/log4j/core/appender/package-info.java  |   2 +-
 .../appender/rolling/DefaultRolloverStrategy.java  |  77 +------
 .../log4j/core/appender/rolling/FileExtension.java | 108 +---------
 .../core/appender/rolling/RollingFileManager.java  | 167 ++++++++++-----
 .../rolling/RollingRandomAccessFileManager.java    |  38 +++-
 .../appender/rolling/action/AbstractAction.java    |  19 --
 .../rolling/action/CommonsCompressAction.java      |  27 ---
 .../appender/rolling/action/GzCompressAction.java  |  45 +---
 .../appender/rolling/action/ZipCompressAction.java |  31 +--
 .../core/appender/rolling/action/package-info.java |   2 +-
 ...ion_delay.xml => 4012_add_max_random_delay.xml} |   2 +-
 .../ROOT/pages/manual/appenders/rolling-file.adoc  |  24 ++-
 19 files changed, 492 insertions(+), 645 deletions(-)

diff --git 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/FileExtensionCompressDelayTest.java
 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/FileExtensionCompressDelayTest.java
deleted file mode 100644
index 98ebf8a691..0000000000
--- 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/FileExtensionCompressDelayTest.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to you under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.logging.log4j.core.appender.rolling;
-
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertInstanceOf;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import org.apache.logging.log4j.core.appender.rolling.action.Action;
-import org.apache.logging.log4j.core.appender.rolling.action.GzCompressAction;
-import org.apache.logging.log4j.core.appender.rolling.action.ZipCompressAction;
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.io.TempDir;
-
-/**
- * Issue #4012 — verifies that FileExtension.GZ and FileExtension.ZIP 
correctly pass
- * maxCompressionDelaySeconds through to the compression action.
- *
- * This was the root cause of the bug: the 5-argument createCompressAction() 
in GZ and ZIP
- * fell back to the 4-argument version, silently dropping the delay value.
- */
-class FileExtensionCompressDelayTest {
-
-    @Test
-    void testGzCreateCompressActionRejectsInvalidCompressionLevel(@TempDir 
File tempDir) {
-        File source = new File(tempDir, "invalid-level.log");
-        File dest = new File(tempDir, "invalid-level.log.gz");
-
-        assertThrows(
-                IllegalArgumentException.class,
-                () -> FileExtension.GZ.createCompressAction(source.getPath(), 
dest.getPath(), true, -2, 0));
-        assertThrows(
-                IllegalArgumentException.class,
-                () -> FileExtension.GZ.createCompressAction(source.getPath(), 
dest.getPath(), true, 10, 0));
-    }
-
-    @Test
-    void testZipCreateCompressActionRejectsInvalidCompressionLevel(@TempDir 
File tempDir) {
-        File source = new File(tempDir, "invalid-level.log");
-        File dest = new File(tempDir, "invalid-level.log.zip");
-
-        assertThrows(
-                IllegalArgumentException.class,
-                () -> FileExtension.ZIP.createCompressAction(source.getPath(), 
dest.getPath(), true, -2, 0));
-        assertThrows(
-                IllegalArgumentException.class,
-                () -> FileExtension.ZIP.createCompressAction(source.getPath(), 
dest.getPath(), true, 10, 0));
-    }
-
-    // ── GZ ────────────────────────────────────────────────────────────────
-
-    /**
-     * FileExtension.GZ.createCompressAction(5-args) must produce a 
GzCompressAction
-     * that applies the random delay — not fall back to 0.
-     */
-    @Test
-    void testGzCreateCompressActionWithDelay(@TempDir File tempDir) throws 
Exception {
-        File source = new File(tempDir, "app.log");
-        File dest = new File(tempDir, "app.log.gz");
-        writeContent(source, "gz test content");
-
-        int maxDelay = 2;
-        Action action = 
FileExtension.GZ.createCompressAction(source.getPath(), dest.getPath(), true, 
-1, maxDelay);
-
-        // Must return a GzCompressAction (not some other type)
-        assertInstanceOf(GzCompressAction.class, action, "Expected 
GzCompressAction");
-
-        long start = System.currentTimeMillis();
-        action.execute();
-        long elapsed = System.currentTimeMillis() - start;
-
-        // Must finish within maxDelay + margin (delay IS applied via 
FileExtension)
-        assertTrue(
-                elapsed <= (maxDelay * 1000L) + 500,
-                "GZ compress via FileExtension exceeded maxDelay=" + maxDelay 
+ "s: " + elapsed + "ms");
-        assertTrue(dest.exists(), "Compressed .gz file must exist");
-        assertFalse(source.exists(), "Source must be deleted after 
compression");
-    }
-
-    /**
-     * FileExtension.GZ.createCompressAction(5-args, delay=0) must behave 
identically
-     * to the 4-arg version — instant compression, no delay.
-     */
-    @Test
-    void testGzCreateCompressActionNoDelay(@TempDir File tempDir) throws 
Exception {
-        File source = new File(tempDir, "app-nodelay.log");
-        File dest = new File(tempDir, "app-nodelay.log.gz");
-        writeContent(source, "gz no-delay content");
-
-        Action action = 
FileExtension.GZ.createCompressAction(source.getPath(), dest.getPath(), true, 
-1, 0);
-
-        assertInstanceOf(GzCompressAction.class, action);
-
-        long start = System.currentTimeMillis();
-        action.execute();
-        long elapsed = System.currentTimeMillis() - start;
-
-        assertTrue(elapsed < 500, "GZ with delay=0 should be instant, took " + 
elapsed + "ms");
-        assertTrue(dest.exists(), "Compressed .gz file must exist");
-        assertFalse(source.exists(), "Source must be deleted");
-    }
-
-    // ── ZIP ───────────────────────────────────────────────────────────────
-
-    /**
-     * FileExtension.ZIP.createCompressAction(5-args) must produce a 
ZipCompressAction
-     * that applies the random delay — not fall back to 0.
-     */
-    @Test
-    void testZipCreateCompressActionWithDelay(@TempDir File tempDir) throws 
Exception {
-        File source = new File(tempDir, "app.log");
-        File dest = new File(tempDir, "app.log.zip");
-        writeContent(source, "zip test content");
-
-        int maxDelay = 2;
-        Action action = 
FileExtension.ZIP.createCompressAction(source.getPath(), dest.getPath(), true, 
0, maxDelay);
-
-        assertInstanceOf(ZipCompressAction.class, action, "Expected 
ZipCompressAction");
-
-        long start = System.currentTimeMillis();
-        action.execute();
-        long elapsed = System.currentTimeMillis() - start;
-
-        assertTrue(
-                elapsed <= (maxDelay * 1000L) + 500,
-                "ZIP compress via FileExtension exceeded maxDelay=" + maxDelay 
+ "s: " + elapsed + "ms");
-        assertTrue(dest.exists(), "Compressed .zip file must exist");
-        assertFalse(source.exists(), "Source must be deleted after 
compression");
-    }
-
-    /**
-     * FileExtension.ZIP.createCompressAction(5-args, delay=0) must behave 
identically
-     * to the 4-arg version — instant compression, no delay.
-     */
-    @Test
-    void testZipCreateCompressActionNoDelay(@TempDir File tempDir) throws 
Exception {
-        File source = new File(tempDir, "app-nodelay.log");
-        File dest = new File(tempDir, "app-nodelay.log.zip");
-        writeContent(source, "zip no-delay content");
-
-        Action action = 
FileExtension.ZIP.createCompressAction(source.getPath(), dest.getPath(), true, 
0, 0);
-
-        assertInstanceOf(ZipCompressAction.class, action);
-
-        long start = System.currentTimeMillis();
-        action.execute();
-        long elapsed = System.currentTimeMillis() - start;
-
-        assertTrue(elapsed < 500, "ZIP with delay=0 should be instant, took " 
+ elapsed + "ms");
-        assertTrue(dest.exists(), "Compressed .zip file must exist");
-        assertFalse(source.exists(), "Source must be deleted");
-    }
-
-    // ── helpers ───────────────────────────────────────────────────────────
-
-    private static void writeContent(final File file, final String content) 
throws IOException {
-        try (FileWriter writer = new FileWriter(file)) {
-            writer.write(content);
-        }
-    }
-}
diff --git 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDirectCronTest.java
 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDirectCronTest.java
index 444016dfd1..a1fbe28ff6 100644
--- 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDirectCronTest.java
+++ 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderDirectCronTest.java
@@ -50,10 +50,10 @@ class RollingAppenderDirectCronTest {
     
@LoggerContextSource("classpath:appender/rolling/RollingAppenderDirectCronTest.xml")
     void testAppender(final LoggerContext ctx, @Named("RollingFile") final 
RollingFileAppender app) throws Exception {
         final Logger logger = 
ctx.getLogger(RollingAppenderDirectCronTest.class);
+        final RolloverDelay delay = new RolloverDelay(app.getManager());
         int msgNumber = 1;
         logger.debug("This is test message number {}.", msgNumber++);
         assertThat(loggingPath).isNotEmptyDirectory();
-        final RolloverDelay delay = new RolloverDelay(app.getManager());
         delay.waitForRollover();
 
         delay.reset(3);
@@ -100,11 +100,11 @@ class RollingAppenderDirectCronTest {
                 final Path path = Paths.get(fileName);
                 final Matcher matcher = 
FILE_PATTERN.matcher(path.getFileName().toString());
                 assertThat(matcher).as("Rolled file").matches();
-                waitAtMost(2, TimeUnit.SECONDS).untilAsserted(() -> {
-                    final List<String> lines = Files.readAllLines(path);
-                    assertThat(lines).isNotEmpty();
-                    assertThat(lines.get(0)).startsWith(matcher.group(1));
-                });
+                final List<String> lines = Files.readAllLines(path);
+                if (lines.isEmpty()) {
+                    return;
+                }
+                assertThat(lines.get(0)).startsWith(matcher.group(1));
                 latch.countDown();
             } catch (final Exception ex) {
                 verificationFailure =
diff --git 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderRandomDelayTest.java
 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderRandomDelayTest.java
new file mode 100644
index 0000000000..9fd691d261
--- /dev/null
+++ 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/RollingAppenderRandomDelayTest.java
@@ -0,0 +1,235 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to you under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.logging.log4j.core.appender.rolling;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.Arrays;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
+import org.apache.logging.log4j.core.appender.RollingFileAppender;
+import org.apache.logging.log4j.core.appender.RollingRandomAccessFileAppender;
+import org.apache.logging.log4j.core.appender.rolling.action.AbstractAction;
+import org.apache.logging.log4j.core.appender.rolling.action.Action;
+import org.apache.logging.log4j.core.appender.rolling.action.CompositeAction;
+import org.apache.logging.log4j.core.config.Configuration;
+import org.apache.logging.log4j.core.config.NullConfiguration;
+import org.apache.logging.log4j.core.layout.PatternLayout;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+class RollingAppenderRandomDelayTest {
+
+    @Test
+    void testRollingFileAppenderPassesMaxRandomDelayToManager(@TempDir File 
tempDir) {
+        final Configuration configuration = new NullConfiguration();
+        final int maxRandomDelay = 17;
+        final RollingFileAppender appender = RollingFileAppender.newBuilder()
+                .setName("RollingFileAppender")
+                .setFileName(new File(tempDir, "app.log").getAbsolutePath())
+                .setFilePattern(new File(tempDir, 
"app-%i.log.gz").getAbsolutePath())
+                .setPolicy(NoOpTriggeringPolicy.INSTANCE)
+                .setConfiguration(configuration)
+                .setMaxRandomDelay(maxRandomDelay)
+                .build();
+        assertNotNull(appender);
+        try {
+            assertEquals(maxRandomDelay, 
appender.getManager().getMaxRandomDelay());
+        } finally {
+            appender.stop();
+        }
+    }
+
+    @Test
+    void 
testRollingRandomAccessFileAppenderPassesMaxRandomDelayToManager(@TempDir File 
tempDir) {
+        final Configuration configuration = new NullConfiguration();
+        final int maxRandomDelay = 19;
+        final RollingRandomAccessFileAppender appender = 
RollingRandomAccessFileAppender.newBuilder()
+                .setName("RollingRandomAccessFileAppender")
+                .setFileName(new File(tempDir, "app-ra.log").getAbsolutePath())
+                .setFilePattern(new File(tempDir, 
"app-ra-%i.log.gz").getAbsolutePath())
+                .setPolicy(NoOpTriggeringPolicy.INSTANCE)
+                .setConfiguration(configuration)
+                .setMaxRandomDelay(maxRandomDelay)
+                .build();
+        assertNotNull(appender);
+        try {
+            assertEquals(maxRandomDelay, 
appender.getManager().getMaxRandomDelay());
+        } finally {
+            appender.stop();
+        }
+    }
+
+    @Test
+    void 
testRollingFileManagerSchedulesAsyncActionAfterDelayAndKeepsActionsSequential(@TempDir
 File tempDir)
+            throws Exception {
+        final long delayMillis = 150;
+        final CountDownLatch completed = new CountDownLatch(2);
+        final AtomicInteger executionOrder = new AtomicInteger();
+        final RecordingAction firstAction = new 
RecordingAction(executionOrder, completed);
+        final RecordingAction secondAction = new 
RecordingAction(executionOrder, completed);
+        final Action asyncAction = new 
CompositeAction(Arrays.asList(firstAction, secondAction), true);
+        final RolloverStrategy strategy =
+                manager -> new RolloverDescriptionImpl(manager.getFileName(), 
false, null, asyncAction);
+        final File file = new File(tempDir, "scheduled.log");
+
+        try (TestRollingFileManager manager = new TestRollingFileManager(file, 
strategy, delayMillis)) {
+            final long rolloverStartNanos = System.nanoTime();
+            manager.rollover();
+
+            assertTrue(completed.await(3, TimeUnit.SECONDS), "Async rollover 
actions did not complete");
+            final long actualDelayMillis = 
TimeUnit.NANOSECONDS.toMillis(firstAction.startNanos - rolloverStartNanos);
+            assertTrue(
+                    actualDelayMillis >= delayMillis - 10,
+                    "Async rollover action started before the configured 
scheduling delay");
+            assertEquals(1, firstAction.order);
+            assertEquals(2, secondAction.order);
+            assertTrue(secondAction.startNanos >= firstAction.endNanos, 
"Composite actions must execute sequentially");
+        }
+    }
+
+    @Test
+    void testRollingFileManagerUsesNoAsyncActionDelayByDefault(@TempDir File 
tempDir) {
+        final RolloverStrategy strategy =
+                manager -> new RolloverDescriptionImpl(manager.getFileName(), 
false, null, null);
+        final File file = new File(tempDir, "immediate.log");
+
+        try (TestRollingFileManager manager = new TestRollingFileManager(file, 
strategy)) {
+            assertEquals(0, manager.getMaxRandomDelay());
+            assertEquals(0, manager.getAsyncActionDelayMillis());
+        }
+    }
+
+    @Test
+    void 
testRollingFileManagerRandomAsyncActionDelayDoesNotRequireCompression(@TempDir 
File tempDir) {
+        final int maxRandomDelay = 2;
+        final long maxDelayMillis = TimeUnit.SECONDS.toMillis(maxRandomDelay);
+        final RolloverStrategy strategy =
+                manager -> new RolloverDescriptionImpl(manager.getFileName(), 
false, null, null);
+        final File file = new File(tempDir, "no-compression-pattern.log");
+
+        try (TestRollingFileManager manager = new TestRollingFileManager(file, 
strategy)) {
+            manager.setMaxRandomDelay(maxRandomDelay);
+            boolean nonZeroDelayObserved = false;
+            for (int index = 0; index < 100; index++) {
+                final long delayMillis = manager.getAsyncActionDelayMillis();
+                assertTrue(delayMillis >= 0, "Async rollover delay must not be 
negative");
+                assertTrue(delayMillis <= maxDelayMillis, "Async rollover 
delay exceeded configured maximum");
+                nonZeroDelayObserved |= delayMillis > 0;
+            }
+            assertTrue(nonZeroDelayObserved, "Configured async rollover delay 
should not require compression");
+        }
+    }
+
+    @Test
+    void 
testRollingFileManagerRandomAsyncActionDelayFallsWithinConfiguredRangeForCompression(@TempDir
 File tempDir) {
+        final int maxRandomDelay = 2;
+        final long maxDelayMillis = TimeUnit.SECONDS.toMillis(maxRandomDelay);
+        final RolloverStrategy strategy =
+                manager -> new RolloverDescriptionImpl(manager.getFileName(), 
false, null, null);
+        final File file = new File(tempDir, "random-compression-pattern.log");
+
+        try (TestRollingFileManager manager =
+                new TestRollingFileManager(file, file.getAbsolutePath() + 
".%i.gz", strategy)) {
+            manager.setMaxRandomDelay(maxRandomDelay);
+            for (int index = 0; index < 100; index++) {
+                final long delayMillis = manager.getAsyncActionDelayMillis();
+                assertTrue(delayMillis >= 0, "Async rollover delay must not be 
negative");
+                assertTrue(delayMillis <= maxDelayMillis, "Async rollover 
delay exceeded configured maximum");
+            }
+        }
+    }
+
+    private static final class TestRollingFileManager extends 
RollingFileManager {
+
+        private final long asyncActionDelayMillis;
+
+        private TestRollingFileManager(final File file, final RolloverStrategy 
strategy) {
+            this(file, file.getAbsolutePath() + ".%i", strategy, -1);
+        }
+
+        private TestRollingFileManager(
+                final File file, final RolloverStrategy strategy, final long 
asyncActionDelayMillis) {
+            this(file, file.getAbsolutePath() + ".%i", strategy, 
asyncActionDelayMillis);
+        }
+
+        private TestRollingFileManager(final File file, final String pattern, 
final RolloverStrategy strategy) {
+            this(file, pattern, strategy, -1);
+        }
+
+        private TestRollingFileManager(
+                final File file,
+                final String pattern,
+                final RolloverStrategy strategy,
+                final long asyncActionDelayMillis) {
+            super(
+                    null,
+                    file.getAbsolutePath(),
+                    pattern,
+                    new ByteArrayOutputStream(),
+                    true,
+                    false,
+                    0,
+                    System.currentTimeMillis(),
+                    NoOpTriggeringPolicy.INSTANCE,
+                    strategy,
+                    null,
+                    PatternLayout.createDefaultLayout(),
+                    null,
+                    null,
+                    null,
+                    false,
+                    ByteBuffer.allocate(256));
+            this.asyncActionDelayMillis = asyncActionDelayMillis;
+        }
+
+        @Override
+        long getAsyncActionDelayMillis() {
+            return asyncActionDelayMillis >= 0 ? asyncActionDelayMillis : 
super.getAsyncActionDelayMillis();
+        }
+    }
+
+    private static final class RecordingAction extends AbstractAction {
+
+        private final AtomicInteger executionOrder;
+        private final CountDownLatch completed;
+        private volatile int order;
+        private volatile long startNanos;
+        private volatile long endNanos;
+
+        private RecordingAction(final AtomicInteger executionOrder, final 
CountDownLatch completed) {
+            this.executionOrder = executionOrder;
+            this.completed = completed;
+        }
+
+        @Override
+        public boolean execute() throws IOException {
+            startNanos = System.nanoTime();
+            order = executionOrder.incrementAndGet();
+            endNanos = System.nanoTime();
+            completed.countDown();
+            return true;
+        }
+    }
+}
diff --git 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/action/GzCompressActionTest.java
 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/action/GzCompressActionTest.java
index 14d72c56dd..1ac77c4bbd 100644
--- 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/action/GzCompressActionTest.java
+++ 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/action/GzCompressActionTest.java
@@ -34,7 +34,7 @@ class GzCompressActionTest {
         File source = new File(tempDir, "invalid-low.log");
         File dest = new File(tempDir, "invalid-low.log.gz");
 
-        assertThrows(IllegalArgumentException.class, () -> new 
GzCompressAction(source, dest, true, -2, 0));
+        assertThrows(IllegalArgumentException.class, () -> new 
GzCompressAction(source, dest, true, -2));
     }
 
     @Test
@@ -42,7 +42,7 @@ class GzCompressActionTest {
         File source = new File(tempDir, "invalid-high.log");
         File dest = new File(tempDir, "invalid-high.log.gz");
 
-        assertThrows(IllegalArgumentException.class, () -> new 
GzCompressAction(source, dest, true, 10, 0));
+        assertThrows(IllegalArgumentException.class, () -> new 
GzCompressAction(source, dest, true, 10));
     }
 
     @Test
@@ -50,71 +50,39 @@ class GzCompressActionTest {
         File source = new File(tempDir, "valid.log");
         File dest = new File(tempDir, "valid.log.gz");
 
-        new GzCompressAction(source, dest, true, Deflater.DEFAULT_COMPRESSION, 
0);
-        new GzCompressAction(source, dest, true, Deflater.BEST_COMPRESSION, 0);
+        new GzCompressAction(source, dest, true, Deflater.DEFAULT_COMPRESSION);
+        new GzCompressAction(source, dest, true, Deflater.BEST_COMPRESSION);
     }
 
-    /** Issue #4012 — when maxDelaySeconds > 0, compression must be deferred 
by a random 0..max seconds. */
     @Test
-    void testRandomDelayBeforeCompression(@TempDir File tempDir) throws 
IOException {
+    void testCompression(@TempDir File tempDir) throws IOException {
         File source = new File(tempDir, "test.log");
         File dest = new File(tempDir, "test.log.gz");
-        try (FileWriter writer = new FileWriter(source)) {
-            writer.write("test data");
-        }
-        int maxDelay = 2; // seconds
-        GzCompressAction action = new GzCompressAction(source, dest, true, 0, 
maxDelay);
-        long start = System.currentTimeMillis();
-        action.execute();
-        long elapsed = System.currentTimeMillis() - start;
-
-        // Must complete within maxDelay + small margin
-        assertTrue(
-                elapsed <= (maxDelay * 1000L) + 500,
-                "Compression should not exceed maxDelay=" + maxDelay + "s, but 
took " + elapsed + "ms");
-        // Destination must be created
-        assertTrue(dest.exists(), "Compressed file must exist after 
execute()");
-        // Source must be deleted (deleteSource=true)
-        assertFalse(source.exists(), "Source file must be deleted after 
compression");
-    }
+        writeContent(source, "test data");
 
-    /**
-     * Issue #4012 — when maxDelaySeconds=0, no delay is applied (backward 
compatibility).
-     * Compression must complete well under 500ms.
-     */
-    @Test
-    void testNoDelayWhenMaxDelayIsZero(@TempDir File tempDir) throws 
IOException {
-        File source = new File(tempDir, "test-nodelay.log");
-        File dest = new File(tempDir, "test-nodelay.log.gz");
-        try (FileWriter writer = new FileWriter(source)) {
-            writer.write("test data no delay");
-        }
-        GzCompressAction action = new GzCompressAction(source, dest, true, 0, 
0);
-        long start = System.currentTimeMillis();
-        action.execute();
-        long elapsed = System.currentTimeMillis() - start;
+        GzCompressAction action = new GzCompressAction(source, dest, true, 
Deflater.DEFAULT_COMPRESSION);
 
-        // No delay: must complete in well under 500ms
-        assertTrue(elapsed < 500, "Compression with maxDelay=0 should be 
instant, but took " + elapsed + "ms");
+        assertTrue(action.execute());
         assertTrue(dest.exists(), "Compressed file must exist after 
execute()");
         assertFalse(source.exists(), "Source file must be deleted after 
compression");
     }
 
-    /** Legacy 4-arg constructor must still work with no delay (backward 
compatibility). */
     @Test
-    void testLegacyConstructorNoDelay(@TempDir File tempDir) throws 
IOException {
+    void testLegacyConstructor(@TempDir File tempDir) throws IOException {
         File source = new File(tempDir, "test-legacy.log");
         File dest = new File(tempDir, "test-legacy.log.gz");
-        try (FileWriter writer = new FileWriter(source)) {
-            writer.write("legacy test data");
-        }
-        GzCompressAction action = new GzCompressAction(source, dest, true, 0);
-        long start = System.currentTimeMillis();
-        action.execute();
-        long elapsed = System.currentTimeMillis() - start;
+        writeContent(source, "legacy test data");
+
+        GzCompressAction action = new GzCompressAction(source, dest, true);
 
-        assertTrue(elapsed < 500, "Legacy constructor should have no delay, 
but took " + elapsed + "ms");
+        assertTrue(action.execute());
         assertTrue(dest.exists(), "Compressed file must exist");
         assertFalse(source.exists(), "Source file must be deleted");
     }
+
+    private static void writeContent(final File file, final String content) 
throws IOException {
+        try (FileWriter writer = new FileWriter(file)) {
+            writer.write(content);
+        }
+    }
 }
diff --git 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/action/ZipCompressActionTest.java
 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/action/ZipCompressActionTest.java
index b8bc76ce53..16b8e71f4b 100644
--- 
a/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/action/ZipCompressActionTest.java
+++ 
b/log4j-core-test/src/test/java/org/apache/logging/log4j/core/appender/rolling/action/ZipCompressActionTest.java
@@ -23,6 +23,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 import java.io.File;
 import java.io.FileWriter;
 import java.io.IOException;
+import java.util.zip.Deflater;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
 
@@ -33,7 +34,7 @@ class ZipCompressActionTest {
         File source = new File(tempDir, "invalid-low.log");
         File dest = new File(tempDir, "invalid-low.log.zip");
 
-        assertThrows(IllegalArgumentException.class, () -> new 
ZipCompressAction(source, dest, true, -2, 0));
+        assertThrows(IllegalArgumentException.class, () -> new 
ZipCompressAction(source, dest, true, -2));
     }
 
     @Test
@@ -41,7 +42,7 @@ class ZipCompressActionTest {
         File source = new File(tempDir, "invalid-high.log");
         File dest = new File(tempDir, "invalid-high.log.zip");
 
-        assertThrows(IllegalArgumentException.class, () -> new 
ZipCompressAction(source, dest, true, 10, 0));
+        assertThrows(IllegalArgumentException.class, () -> new 
ZipCompressAction(source, dest, true, 10));
     }
 
     @Test
@@ -49,72 +50,27 @@ class ZipCompressActionTest {
         File source = new File(tempDir, "valid.log");
         File dest = new File(tempDir, "valid.log.zip");
 
-        new ZipCompressAction(source, dest, true, -1, 0);
-        new ZipCompressAction(source, dest, true, 0, 0);
-        new ZipCompressAction(source, dest, true, 9, 0);
+        new ZipCompressAction(source, dest, true, 
Deflater.DEFAULT_COMPRESSION);
+        new ZipCompressAction(source, dest, true, Deflater.NO_COMPRESSION);
+        new ZipCompressAction(source, dest, true, Deflater.BEST_COMPRESSION);
     }
 
-    /** Issue #4012 — when maxDelaySeconds > 0, compression must be deferred 
by a random 0..max seconds. */
     @Test
-    void testRandomDelayBeforeCompression(@TempDir File tempDir) throws 
IOException {
+    void testCompression(@TempDir File tempDir) throws IOException {
         File source = new File(tempDir, "test.log");
         File dest = new File(tempDir, "test.log.zip");
-        try (FileWriter writer = new FileWriter(source)) {
-            writer.write("test data");
-        }
-        int maxDelay = 2; // seconds
-        ZipCompressAction action = new ZipCompressAction(source, dest, true, 
0, maxDelay);
-        long start = System.currentTimeMillis();
-        action.execute();
-        long elapsed = System.currentTimeMillis() - start;
+        writeContent(source, "test data");
 
-        // Must complete within maxDelay + small margin
-        assertTrue(
-                elapsed <= (maxDelay * 1000L) + 500,
-                "Compression should not exceed maxDelay=" + maxDelay + "s, but 
took " + elapsed + "ms");
-        // Destination must be created
-        assertTrue(dest.exists(), "Compressed file must exist after 
execute()");
-        // Source must be deleted (deleteSource=true)
-        assertFalse(source.exists(), "Source file must be deleted after 
compression");
-    }
+        ZipCompressAction action = new ZipCompressAction(source, dest, true, 
Deflater.DEFAULT_COMPRESSION);
 
-    /**
-     * Issue #4012 — when maxDelaySeconds=0, no delay is applied (backward 
compatibility).
-     * Compression must complete well under 500ms.
-     */
-    @Test
-    void testNoDelayWhenMaxDelayIsZero(@TempDir File tempDir) throws 
IOException {
-        File source = new File(tempDir, "test-nodelay.log");
-        File dest = new File(tempDir, "test-nodelay.log.zip");
-        try (FileWriter writer = new FileWriter(source)) {
-            writer.write("test data no delay");
-        }
-        ZipCompressAction action = new ZipCompressAction(source, dest, true, 
0, 0);
-        long start = System.currentTimeMillis();
-        action.execute();
-        long elapsed = System.currentTimeMillis() - start;
-
-        // No delay: must complete in well under 500ms
-        assertTrue(elapsed < 500, "Compression with maxDelay=0 should be 
instant, but took " + elapsed + "ms");
+        assertTrue(action.execute());
         assertTrue(dest.exists(), "Compressed file must exist after 
execute()");
         assertFalse(source.exists(), "Source file must be deleted after 
compression");
     }
 
-    /** Legacy 4-arg constructor must still work with no delay (backward 
compatibility). */
-    @Test
-    void testLegacyConstructorNoDelay(@TempDir File tempDir) throws 
IOException {
-        File source = new File(tempDir, "test-legacy.log");
-        File dest = new File(tempDir, "test-legacy.log.zip");
-        try (FileWriter writer = new FileWriter(source)) {
-            writer.write("legacy test data");
+    private static void writeContent(final File file, final String content) 
throws IOException {
+        try (FileWriter writer = new FileWriter(file)) {
+            writer.write(content);
         }
-        ZipCompressAction action = new ZipCompressAction(source, dest, true, 
0);
-        long start = System.currentTimeMillis();
-        action.execute();
-        long elapsed = System.currentTimeMillis() - start;
-
-        assertTrue(elapsed < 500, "Legacy constructor should have no delay, 
but took " + elapsed + "ms");
-        assertTrue(dest.exists(), "Compressed file must exist");
-        assertFalse(source.exists(), "Source file must be deleted");
     }
 }
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RollingFileAppender.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RollingFileAppender.java
index a78be3e4c5..4323661a3d 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RollingFileAppender.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RollingFileAppender.java
@@ -103,6 +103,9 @@ public final class RollingFileAppender extends 
AbstractOutputStreamAppender<Roll
         @PluginBuilderAttribute
         private String fileGroup;
 
+        @PluginBuilderAttribute
+        private int maxRandomDelay;
+
         @Override
         public RollingFileAppender build() {
             if (!isValid()) {
@@ -151,11 +154,11 @@ public final class RollingFileAppender extends 
AbstractOutputStreamAppender<Roll
                     advertiseUri,
                     layout,
                     bufferSize,
-                    isImmediateFlush(),
                     createOnDemand,
                     filePermissions,
                     fileOwner,
                     fileGroup,
+                    maxRandomDelay,
                     getConfiguration());
             if (manager == null) {
                 return null;
@@ -212,6 +215,10 @@ public final class RollingFileAppender extends 
AbstractOutputStreamAppender<Roll
             return fileGroup;
         }
 
+        public int getMaxRandomDelay() {
+            return maxRandomDelay;
+        }
+
         /**
          * @since 2.26.0
          */
@@ -374,6 +381,14 @@ public final class RollingFileAppender extends 
AbstractOutputStreamAppender<Roll
             return asBuilder();
         }
 
+        /**
+         * @since 2.27.0
+         */
+        public B setMaxRandomDelay(final int maxRandomDelay) {
+            this.maxRandomDelay = maxRandomDelay;
+            return asBuilder();
+        }
+
         /**
          * @deprecated since 2.26.0 use {@link #setFilePattern(String)}.
          */
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RollingRandomAccessFileAppender.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RollingRandomAccessFileAppender.java
index 5e7361f68f..e08deea992 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RollingRandomAccessFileAppender.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/RollingRandomAccessFileAppender.java
@@ -93,6 +93,9 @@ public final class RollingRandomAccessFileAppender
         @PluginBuilderAttribute
         private String fileGroup;
 
+        @PluginBuilderAttribute
+        private int maxRandomDelay;
+
         @Override
         public RollingRandomAccessFileAppender build() {
             final String name = getName();
@@ -147,6 +150,7 @@ public final class RollingRandomAccessFileAppender
                             filePermissions,
                             fileOwner,
                             fileGroup,
+                            maxRandomDelay,
                             getConfiguration());
             if (manager == null) {
                 return null;
@@ -248,6 +252,14 @@ public final class RollingRandomAccessFileAppender
             return asBuilder();
         }
 
+        /**
+         * @since 2.27.0
+         */
+        public B setMaxRandomDelay(final int maxRandomDelay) {
+            this.maxRandomDelay = maxRandomDelay;
+            return asBuilder();
+        }
+
         /**
          * @deprecated since 2.26.0 use {@link #setFileName(String)}.
          */
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/package-info.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/package-info.java
index a18b7715e4..5f5331549c 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/package-info.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/package-info.java
@@ -18,7 +18,7 @@
  * Log4j 2 Appenders.
  */
 @Export
-@Version("2.26.0")
+@Version("2.27.0")
 package org.apache.logging.log4j.core.appender;
 
 import org.osgi.annotation.bundle.Export;
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DefaultRolloverStrategy.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DefaultRolloverStrategy.java
index 74fbbf8359..06eefa84ce 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DefaultRolloverStrategy.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/DefaultRolloverStrategy.java
@@ -109,9 +109,6 @@ public class DefaultRolloverStrategy extends 
AbstractRolloverStrategy {
         @PluginBuilderAttribute(value = "tempCompressedFilePattern")
         private String tempCompressedFilePattern;
 
-        @PluginBuilderAttribute("maxCompressionDelaySeconds")
-        private int maxCompressionDelaySeconds = 0;
-
         @PluginConfiguration
         private Configuration config;
 
@@ -159,8 +156,7 @@ public class DefaultRolloverStrategy extends 
AbstractRolloverStrategy {
                     nonNullStrSubstitutor,
                     customActions,
                     stopCustomActionsOnError,
-                    tempCompressedFilePattern,
-                    maxCompressionDelaySeconds);
+                    tempCompressedFilePattern);
         }
 
         public String getMax() {
@@ -363,18 +359,6 @@ public class DefaultRolloverStrategy extends 
AbstractRolloverStrategy {
             this.config = config;
             return this;
         }
-
-        /**
-         * Defines maximum delay in seconds before compression.
-         *
-         * @param maxCompressionDelaySeconds maximum delay in seconds before 
compression.
-         * @return This builder for chaining convenience
-         * @since 2.27.0
-         */
-        public Builder setMaxCompressionDelaySeconds(final int 
maxCompressionDelaySeconds) {
-            this.maxCompressionDelaySeconds = maxCompressionDelaySeconds;
-            return this;
-        }
     }
 
     @PluginBuilderFactory
@@ -438,7 +422,6 @@ public class DefaultRolloverStrategy extends 
AbstractRolloverStrategy {
     private final List<Action> customActions;
     private final boolean stopCustomActionsOnError;
     private final PatternProcessor tempCompressedFilePattern;
-    private final int maxCompressionDelaySeconds;
 
     /**
      * Constructs a new instance.
@@ -466,8 +449,7 @@ public class DefaultRolloverStrategy extends 
AbstractRolloverStrategy {
                 strSubstitutor,
                 customActions,
                 stopCustomActionsOnError,
-                null,
-                0);
+                null);
     }
 
     /**
@@ -489,40 +471,6 @@ public class DefaultRolloverStrategy extends 
AbstractRolloverStrategy {
             final Action[] customActions,
             final boolean stopCustomActionsOnError,
             final String tempCompressedFilePatternString) {
-        this(
-                minIndex,
-                maxIndex,
-                useMax,
-                compressionLevel,
-                strSubstitutor,
-                customActions,
-                stopCustomActionsOnError,
-                tempCompressedFilePatternString,
-                0);
-    }
-
-    /**
-     * Constructs a new instance.
-     *
-     * @param minIndex The minimum index.
-     * @param maxIndex The maximum index.
-     * @param customActions custom actions to perform asynchronously after 
rollover
-     * @param stopCustomActionsOnError whether to stop executing asynchronous 
actions if an error occurs
-     * @param tempCompressedFilePatternString File pattern of the working file
-     *                                     used during compression, if null no 
temporary file are used
-     * @param maxCompressionDelaySeconds maximum delay in seconds before 
compression.
-     * @since 2.27.0
-     */
-    protected DefaultRolloverStrategy(
-            final int minIndex,
-            final int maxIndex,
-            final boolean useMax,
-            final int compressionLevel,
-            final StrSubstitutor strSubstitutor,
-            final Action[] customActions,
-            final boolean stopCustomActionsOnError,
-            final String tempCompressedFilePatternString,
-            final int maxCompressionDelaySeconds) {
         super(strSubstitutor);
         this.minIndex = minIndex;
         this.maxIndex = maxIndex;
@@ -532,7 +480,6 @@ public class DefaultRolloverStrategy extends 
AbstractRolloverStrategy {
         this.customActions = customActions == null ? 
Collections.<Action>emptyList() : Arrays.asList(customActions);
         this.tempCompressedFilePattern =
                 tempCompressedFilePatternString != null ? new 
PatternProcessor(tempCompressedFilePatternString) : null;
-        this.maxCompressionDelaySeconds = maxCompressionDelaySeconds;
     }
 
     public int getCompressionLevel() {
@@ -563,16 +510,6 @@ public class DefaultRolloverStrategy extends 
AbstractRolloverStrategy {
         return tempCompressedFilePattern;
     }
 
-    /**
-     * Returns the maximum delay in seconds before compression.
-     *
-     * @return maximum delay in seconds before compression.
-     * @since 2.27.0
-     */
-    public int getMaxCompressionDelaySeconds() {
-        return maxCompressionDelaySeconds;
-    }
-
     private int purge(final int lowIndex, final int highIndex, final 
RollingFileManager manager) {
         return useMax ? purgeAscending(lowIndex, highIndex, manager) : 
purgeDescending(lowIndex, highIndex, manager);
     }
@@ -746,17 +683,11 @@ public class DefaultRolloverStrategy extends 
AbstractRolloverStrategy {
                 }
                 compressAction = new CompositeAction(
                         Arrays.asList(
-                                fileExtension.createCompressAction(
-                                        renameTo,
-                                        tmpCompressedName,
-                                        true,
-                                        compressionLevel,
-                                        maxCompressionDelaySeconds),
+                                fileExtension.createCompressAction(renameTo, 
tmpCompressedName, true, compressionLevel),
                                 new FileRenameAction(tmpCompressedNameFile, 
renameToFile, true)),
                         true);
             } else {
-                compressAction = fileExtension.createCompressAction(
-                        renameTo, compressedName, true, compressionLevel, 
maxCompressionDelaySeconds);
+                compressAction = fileExtension.createCompressAction(renameTo, 
compressedName, true, compressionLevel);
             }
         }
 
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/FileExtension.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/FileExtension.java
index eb453628e8..e62419b685 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/FileExtension.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/FileExtension.java
@@ -35,22 +35,7 @@ public enum FileExtension {
                 final String compressedName,
                 final boolean deleteSource,
                 final int compressionLevel) {
-            return createCompressAction(renameTo, compressedName, 
deleteSource, compressionLevel, 0);
-        }
-
-        @Override
-        public Action createCompressAction(
-                final String renameTo,
-                final String compressedName,
-                final boolean deleteSource,
-                final int compressionLevel,
-                final int maxCompressionDelaySeconds) {
-            return new ZipCompressAction(
-                    new File(renameTo),
-                    new File(compressedName),
-                    deleteSource,
-                    compressionLevel,
-                    maxCompressionDelaySeconds);
+            return new ZipCompressAction(source(renameTo), 
target(compressedName), deleteSource, compressionLevel);
         }
     },
     GZ(".gz") {
@@ -60,22 +45,7 @@ public enum FileExtension {
                 final String compressedName,
                 final boolean deleteSource,
                 final int compressionLevel) {
-            return createCompressAction(renameTo, compressedName, 
deleteSource, compressionLevel, 0);
-        }
-
-        @Override
-        public Action createCompressAction(
-                final String renameTo,
-                final String compressedName,
-                final boolean deleteSource,
-                final int compressionLevel,
-                final int maxCompressionDelaySeconds) {
-            return new GzCompressAction(
-                    new File(renameTo),
-                    new File(compressedName),
-                    deleteSource,
-                    compressionLevel,
-                    maxCompressionDelaySeconds);
+            return new GzCompressAction(source(renameTo), 
target(compressedName), deleteSource, compressionLevel);
         }
     },
     BZIP2(".bz2") {
@@ -85,19 +55,8 @@ public enum FileExtension {
                 final String compressedName,
                 final boolean deleteSource,
                 final int compressionLevel) {
-            return createCompressAction(renameTo, compressedName, 
deleteSource, compressionLevel, 0);
-        }
-
-        @Override
-        public Action createCompressAction(
-                final String renameTo,
-                final String compressedName,
-                final boolean deleteSource,
-                final int compressionLevel,
-                final int maxCompressionDelaySeconds) {
             // One of "gz", "bzip2", "xz", "zst", "pack200", or "deflate".
-            return new CommonsCompressAction(
-                    "bzip2", source(renameTo), target(compressedName), 
deleteSource, maxCompressionDelaySeconds);
+            return new CommonsCompressAction("bzip2", source(renameTo), 
target(compressedName), deleteSource);
         }
     },
     DEFLATE(".deflate") {
@@ -107,19 +66,8 @@ public enum FileExtension {
                 final String compressedName,
                 final boolean deleteSource,
                 final int compressionLevel) {
-            return createCompressAction(renameTo, compressedName, 
deleteSource, compressionLevel, 0);
-        }
-
-        @Override
-        public Action createCompressAction(
-                final String renameTo,
-                final String compressedName,
-                final boolean deleteSource,
-                final int compressionLevel,
-                final int maxCompressionDelaySeconds) {
             // One of "gz", "bzip2", "xz", "zst", "pack200", or "deflate".
-            return new CommonsCompressAction(
-                    "deflate", source(renameTo), target(compressedName), 
deleteSource, maxCompressionDelaySeconds);
+            return new CommonsCompressAction("deflate", source(renameTo), 
target(compressedName), deleteSource);
         }
     },
     PACK200(".pack200") {
@@ -129,19 +77,8 @@ public enum FileExtension {
                 final String compressedName,
                 final boolean deleteSource,
                 final int compressionLevel) {
-            return createCompressAction(renameTo, compressedName, 
deleteSource, compressionLevel, 0);
-        }
-
-        @Override
-        public Action createCompressAction(
-                final String renameTo,
-                final String compressedName,
-                final boolean deleteSource,
-                final int compressionLevel,
-                final int maxCompressionDelaySeconds) {
-            // One of "gz", "bzip2", "xz", "zstd", "pack200", or "deflate".
-            return new CommonsCompressAction(
-                    "pack200", source(renameTo), target(compressedName), 
deleteSource, maxCompressionDelaySeconds);
+            // One of "gz", "bzip2", "xz", "zst", "pack200", or "deflate".
+            return new CommonsCompressAction("pack200", source(renameTo), 
target(compressedName), deleteSource);
         }
     },
     XZ(".xz") {
@@ -151,19 +88,8 @@ public enum FileExtension {
                 final String compressedName,
                 final boolean deleteSource,
                 final int compressionLevel) {
-            return createCompressAction(renameTo, compressedName, 
deleteSource, compressionLevel, 0);
-        }
-
-        @Override
-        public Action createCompressAction(
-                final String renameTo,
-                final String compressedName,
-                final boolean deleteSource,
-                final int compressionLevel,
-                final int maxCompressionDelaySeconds) {
             // One of "gz", "bzip2", "xz", "zstd", "pack200", or "deflate".
-            return new CommonsCompressAction(
-                    "xz", source(renameTo), target(compressedName), 
deleteSource, maxCompressionDelaySeconds);
+            return new CommonsCompressAction("xz", source(renameTo), 
target(compressedName), deleteSource);
         }
     },
     ZSTD(".zst") {
@@ -173,19 +99,8 @@ public enum FileExtension {
                 final String compressedName,
                 final boolean deleteSource,
                 final int compressionLevel) {
-            return createCompressAction(renameTo, compressedName, 
deleteSource, compressionLevel, 0);
-        }
-
-        @Override
-        public Action createCompressAction(
-                final String renameTo,
-                final String compressedName,
-                final boolean deleteSource,
-                final int compressionLevel,
-                final int maxCompressionDelaySeconds) {
             // One of "gz", "bzip2", "xz", "zstd", "pack200", or "deflate".
-            return new CommonsCompressAction(
-                    "zstd", source(renameTo), target(compressedName), 
deleteSource, maxCompressionDelaySeconds);
+            return new CommonsCompressAction("zstd", source(renameTo), 
target(compressedName), deleteSource);
         }
     };
 
@@ -217,13 +132,6 @@ public enum FileExtension {
     public abstract Action createCompressAction(
             String renameTo, String compressedName, boolean deleteSource, int 
compressionLevel);
 
-    public abstract Action createCompressAction(
-            String renameTo,
-            String compressedName,
-            boolean deleteSource,
-            int compressionLevel,
-            int maxCompressionDelaySeconds);
-
     public String getExtension() {
         return extension;
     }
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingFileManager.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingFileManager.java
index 0c62bfac96..75b7df81cf 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingFileManager.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingFileManager.java
@@ -26,13 +26,12 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.attribute.BasicFileAttributes;
 import java.nio.file.attribute.FileTime;
-import java.util.Collection;
 import java.util.Date;
-import java.util.concurrent.ArrayBlockingQueue;
 import java.util.concurrent.CopyOnWriteArrayList;
-import java.util.concurrent.ExecutorService;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
 import java.util.concurrent.Semaphore;
-import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
 import org.apache.logging.log4j.core.Layout;
@@ -70,12 +69,12 @@ public class RollingFileManager extends FileManager {
     private volatile boolean initialized;
     private volatile String fileName;
     private final boolean directWrite;
+    private volatile int maxRandomDelay;
     private final CopyOnWriteArrayList<RolloverListener> rolloverListeners = 
new CopyOnWriteArrayList<>();
 
-    /* This executor pool will create a new Thread for every work async action 
to be performed. Using it allows
-    us to make sure all the Threads are completed when the Manager is stopped. 
*/
-    private final ExecutorService asyncExecutor =
-            new ThreadPoolExecutor(0, Integer.MAX_VALUE, 0, 
TimeUnit.MILLISECONDS, new EmptyQueue(), threadFactory);
+    /* This executor service schedules asynchronous rollover actions and 
ensures they are completed when the manager
+    is stopped. */
+    private final ScheduledExecutorService asyncExecutor = new 
ScheduledThreadPoolExecutor(1, threadFactory);
 
     private static final AtomicReferenceFieldUpdater<RollingFileManager, 
TriggeringPolicy> triggeringPolicyUpdater =
             AtomicReferenceFieldUpdater.newUpdater(
@@ -294,6 +293,60 @@ public class RollingFileManager extends FileManager {
             final String fileOwner,
             final String fileGroup,
             final Configuration configuration) {
+        return getFileManager(
+                fileName,
+                pattern,
+                append,
+                bufferedIO,
+                policy,
+                strategy,
+                advertiseURI,
+                layout,
+                bufferSize,
+                createOnDemand,
+                filePermissions,
+                fileOwner,
+                fileGroup,
+                0,
+                configuration);
+    }
+
+    /**
+     * Returns a RollingFileManager.
+     * @param fileName The file name.
+     * @param pattern The pattern for rolling file.
+     * @param append true if the file should be appended to.
+     * @param bufferedIO true if data should be buffered.
+     * @param policy The TriggeringPolicy.
+     * @param strategy The RolloverStrategy.
+     * @param advertiseURI the URI to use when advertising the file
+     * @param layout The Layout.
+     * @param bufferSize buffer size to use if bufferedIO is true
+     * @param createOnDemand true if you want to lazy-create the file (a.k.a. 
on-demand.)
+     * @param filePermissions File permissions
+     * @param fileOwner File owner
+     * @param fileGroup File group
+     * @param maxRandomDelay maximum random delay in seconds before executing 
the asynchronous rollover action chain
+     * @param configuration The configuration.
+     * @return A RollingFileManager.
+     * @since 2.27.0
+     */
+    public static RollingFileManager getFileManager(
+            final String fileName,
+            final String pattern,
+            final boolean append,
+            final boolean bufferedIO,
+            final TriggeringPolicy policy,
+            final RolloverStrategy strategy,
+            final String advertiseURI,
+            final Layout<? extends Serializable> layout,
+            final int bufferSize,
+            final boolean createOnDemand,
+            final String filePermissions,
+            final String fileOwner,
+            final String fileGroup,
+            final int maxRandomDelay,
+            final Configuration configuration) {
         if (strategy instanceof DirectWriteRolloverStrategy && fileName != 
null) {
             LOGGER.error("The fileName attribute must not be specified with 
the DirectWriteRolloverStrategy");
             return null;
@@ -351,6 +404,7 @@ public class RollingFileManager extends FileManager {
                                         fileGroup,
                                         writeHeader,
                                         buffer);
+                                rm.setMaxRandomDelay(data.getMaxRandomDelay());
                                 if (os != null && rm.isAttributeViewEnabled()) 
{
                                     rm.defineAttributeView(file.toPath());
                                 }
@@ -361,7 +415,7 @@ public class RollingFileManager extends FileManager {
                             }
                             return null;
                         },
-                        new FactoryData(pattern, policy, strategy, 
configuration)));
+                        new FactoryData(pattern, policy, strategy, 
maxRandomDelay, configuration)));
     }
 
     /**
@@ -426,6 +480,26 @@ public class RollingFileManager extends FileManager {
         return renameEmptyFiles;
     }
 
+    /**
+     * Returns the maximum random delay in seconds before the asynchronous 
rollover action chain is executed.
+     *
+     * @return the maximum random delay in seconds.
+     * @since 2.27.0
+     */
+    public int getMaxRandomDelay() {
+        return maxRandomDelay;
+    }
+
+    /**
+     * Sets the maximum random delay in seconds before the asynchronous 
rollover action chain is executed.
+     *
+     * @param maxRandomDelay the maximum random delay in seconds.
+     * @since 2.27.0
+     */
+    public void setMaxRandomDelay(final int maxRandomDelay) {
+        this.maxRandomDelay = Math.max(0, maxRandomDelay);
+    }
+
     public void setRenameEmptyFiles(final boolean renameEmptyFiles) {
         this.renameEmptyFiles = renameEmptyFiles;
     }
@@ -670,8 +744,13 @@ public class RollingFileManager extends FileManager {
                 }
 
                 if (syncActionSuccess && descriptor.getAsynchronous() != null) 
{
-                    LOGGER.debug("RollingFileManager executing async {}", 
descriptor.getAsynchronous());
-                    asyncExecutor.execute(new 
AsyncAction(descriptor.getAsynchronous(), this));
+                    final long delayMillis = getAsyncActionDelayMillis();
+                    LOGGER.debug(
+                            "RollingFileManager executing async {} with delay 
{} ms",
+                            descriptor.getAsynchronous(),
+                            delayMillis);
+                    asyncExecutor.schedule(
+                            new AsyncAction(descriptor.getAsynchronous(), 
this), delayMillis, TimeUnit.MILLISECONDS);
                     asyncActionStarted = false;
                 }
             }
@@ -683,6 +762,13 @@ public class RollingFileManager extends FileManager {
         }
     }
 
+    long getAsyncActionDelayMillis() {
+        final int maxRandomDelayCopy = maxRandomDelay;
+        return maxRandomDelayCopy > 0
+                ? 
ThreadLocalRandom.current().nextLong(TimeUnit.SECONDS.toMillis(maxRandomDelayCopy)
 + 1)
+                : 0;
+    }
+
     /**
      * Performs actions asynchronously.
      */
@@ -762,6 +848,7 @@ public class RollingFileManager extends FileManager {
         private final String pattern;
         private final TriggeringPolicy policy;
         private final RolloverStrategy strategy;
+        private final int maxRandomDelay;
 
         /**
          * Creates the data for the factory.
@@ -773,10 +860,20 @@ public class RollingFileManager extends FileManager {
                 final TriggeringPolicy policy,
                 final RolloverStrategy strategy,
                 final Configuration configuration) {
+            this(pattern, policy, strategy, 0, configuration);
+        }
+
+        public FactoryData(
+                final String pattern,
+                final TriggeringPolicy policy,
+                final RolloverStrategy strategy,
+                final int maxRandomDelay,
+                final Configuration configuration) {
             super(configuration);
             this.pattern = pattern;
             this.policy = policy;
             this.strategy = strategy;
+            this.maxRandomDelay = maxRandomDelay;
         }
 
         public TriggeringPolicy getTriggeringPolicy() {
@@ -790,6 +887,10 @@ public class RollingFileManager extends FileManager {
         public String getPattern() {
             return pattern;
         }
+
+        public int getMaxRandomDelay() {
+            return maxRandomDelay;
+        }
     }
 
     /**
@@ -804,6 +905,7 @@ public class RollingFileManager extends FileManager {
         setRolloverStrategy(factoryData.getRolloverStrategy());
         setPatternProcessor(new PatternProcessor(factoryData.getPattern(), 
getPatternProcessor()));
         setTriggeringPolicy(factoryData.getTriggeringPolicy());
+        setMaxRandomDelay(factoryData.getMaxRandomDelay());
     }
 
     static long initialFileTime(final File file) {
@@ -833,47 +935,4 @@ public class RollingFileManager extends FileManager {
     static long alignMillisToSecond(long millis) {
         return Math.round(millis / 1000d) * 1000;
     }
-
-    private static class EmptyQueue extends ArrayBlockingQueue<Runnable> {
-
-        /**
-         *
-         */
-        private static final long serialVersionUID = 1L;
-
-        EmptyQueue() {
-            super(1);
-        }
-
-        @Override
-        public int remainingCapacity() {
-            return 0;
-        }
-
-        @Override
-        public boolean add(final Runnable runnable) {
-            throw new IllegalStateException("Queue is full");
-        }
-
-        @Override
-        public void put(final Runnable runnable) throws InterruptedException {
-            /* No point in going into a permanent wait */
-            throw new InterruptedException("Unable to insert into queue");
-        }
-
-        @Override
-        public boolean offer(final Runnable runnable, final long timeout, 
final TimeUnit timeUnit)
-                throws InterruptedException {
-            Thread.sleep(timeUnit.toMillis(timeout));
-            return false;
-        }
-
-        @Override
-        public boolean addAll(final Collection<? extends Runnable> collection) 
{
-            if (collection.size() > 0) {
-                throw new IllegalArgumentException("Too many items in 
collection");
-            }
-            return false;
-        }
-    }
 }
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManager.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManager.java
index abb50f587f..66c54d2641 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManager.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/RollingRandomAccessFileManager.java
@@ -160,6 +160,41 @@ public class RollingRandomAccessFileManager extends 
RollingFileManager {
             final String fileOwner,
             final String fileGroup,
             final Configuration configuration) {
+        return getRollingRandomAccessFileManager(
+                fileName,
+                filePattern,
+                append,
+                immediateFlush,
+                bufferSize,
+                policy,
+                strategy,
+                advertiseURI,
+                layout,
+                filePermissions,
+                fileOwner,
+                fileGroup,
+                0,
+                configuration);
+    }
+
+    /**
+     * @since 2.27.0
+     */
+    public static RollingRandomAccessFileManager 
getRollingRandomAccessFileManager(
+            final String fileName,
+            final String filePattern,
+            final boolean append,
+            final boolean immediateFlush,
+            final int bufferSize,
+            final TriggeringPolicy policy,
+            final RolloverStrategy strategy,
+            final String advertiseURI,
+            final Layout<? extends Serializable> layout,
+            final String filePermissions,
+            final String fileOwner,
+            final String fileGroup,
+            final int maxRandomDelay,
+            final Configuration configuration) {
         if (strategy instanceof DirectWriteRolloverStrategy && fileName != 
null) {
             LOGGER.error("The fileName attribute must not be specified with 
the DirectWriteRolloverStrategy");
             return null;
@@ -230,12 +265,13 @@ public class RollingRandomAccessFileManager extends 
RollingFileManager {
                                     fileOwner,
                                     fileGroup,
                                     writeHeader);
+                            rrm.setMaxRandomDelay(data.getMaxRandomDelay());
                             if (rrm.isAttributeViewEnabled()) {
                                 rrm.defineAttributeView(file.toPath());
                             }
                             return rrm;
                         },
-                        new FactoryData(filePattern, policy, strategy, 
configuration)));
+                        new FactoryData(filePattern, policy, strategy, 
maxRandomDelay, configuration)));
     }
 
     /**
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/AbstractAction.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/AbstractAction.java
index 14258eec3f..2ca052835a 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/AbstractAction.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/AbstractAction.java
@@ -82,25 +82,6 @@ public abstract class AbstractAction implements Action {
         interrupted = true;
     }
 
-    /**
-     * Blocks the current thread for a random delay up to {@code 
maxDelaySeconds}.
-     *
-     * @param maxDelaySeconds maximum delay in seconds before returning.
-     * @since 2.27.0
-     */
-    static void blockThread(final int maxDelaySeconds) {
-        if (maxDelaySeconds > 0) {
-            int delay = 
java.util.concurrent.ThreadLocalRandom.current().nextInt(maxDelaySeconds + 1);
-            if (delay > 0) {
-                try {
-                    Thread.sleep(delay * 1000L);
-                } catch (InterruptedException e) {
-                    Thread.currentThread().interrupt();
-                }
-            }
-        }
-    }
-
     /**
      * Tests if the action is complete.
      *
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/CommonsCompressAction.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/CommonsCompressAction.java
index c01b2cb992..1688969578 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/CommonsCompressAction.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/CommonsCompressAction.java
@@ -54,11 +54,6 @@ public final class CommonsCompressAction extends 
AbstractAction {
      */
     private final boolean deleteSource;
 
-    /**
-     * Maximum delay in seconds before compression.
-     */
-    private final int maxDelaySeconds;
-
     /**
      * Creates new instance of Bzip2CompressAction.
      *
@@ -70,33 +65,12 @@ public final class CommonsCompressAction extends 
AbstractAction {
      */
     public CommonsCompressAction(
             final String name, final File source, final File destination, 
final boolean deleteSource) {
-        this(name, source, destination, deleteSource, 0);
-    }
-
-    /**
-     * Creates new instance of Bzip2CompressAction.
-     *
-     * @param name the compressor name. One of "gz", "bzip2", "xz", "zst", 
"pack200", or "deflate".
-     * @param source file to compress, may not be null.
-     * @param destination compressed file, may not be null.
-     * @param deleteSource if true, attempt to delete file on completion. 
Failure to delete does not cause an exception
-     *            to be thrown or affect return value.
-     * @param maxDelaySeconds maximum delay in seconds before compression.
-     * @since 2.27.0
-     */
-    public CommonsCompressAction(
-            final String name,
-            final File source,
-            final File destination,
-            final boolean deleteSource,
-            final int maxDelaySeconds) {
         Objects.requireNonNull(source, "source");
         Objects.requireNonNull(destination, "destination");
         this.name = name;
         this.source = source;
         this.destination = destination;
         this.deleteSource = deleteSource;
-        this.maxDelaySeconds = maxDelaySeconds;
     }
 
     /**
@@ -107,7 +81,6 @@ public final class CommonsCompressAction extends 
AbstractAction {
      */
     @Override
     public boolean execute() throws IOException {
-        blockThread(maxDelaySeconds);
         return execute(name, source, destination, deleteSource);
     }
 
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/GzCompressAction.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/GzCompressAction.java
index 5747c638cd..4166f7b12e 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/GzCompressAction.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/GzCompressAction.java
@@ -55,11 +55,6 @@ public final class GzCompressAction extends AbstractAction {
      */
     private final int compressionLevel;
 
-    /**
-     * Maximum delay in seconds before compression.
-     */
-    private final int maxDelaySeconds;
-
     private static int checkCompressionLevel(final int compressionLevel) {
         final int minCompressionLevel = Deflater.DEFAULT_COMPRESSION;
         final int maxCompressionLevel = Deflater.BEST_COMPRESSION;
@@ -76,24 +71,14 @@ public final class GzCompressAction extends AbstractAction {
     }
 
     /**
-     * Create new instance of GzCompressAction.
-     *
-     * @param source       file to compress, may not be null.
-     * @param destination  compressed file, may not be null.
-     * @param deleteSource if true, attempt to delete file on completion.  
Failure to delete
-     *                     does not cause an exception to be thrown or affect 
return value.
-     * @param compressionLevel
-     *                     Gzip deflater compression level.
-     * @since 2.27.0
-     * @param maxDelaySeconds
-     *                     Maximum delay in seconds before compression.
+     * Creates a new instance.
+     * @param source file to compress, may not be null.
+     * @param destination compressed file, may not be null.
+     * @param deleteSource if true, attempt to delete file on completion.
+     * @param compressionLevel Gzip deflater compression level.
      */
     public GzCompressAction(
-            final File source,
-            final File destination,
-            final boolean deleteSource,
-            final int compressionLevel,
-            final int maxDelaySeconds) {
+            final File source, final File destination, final boolean 
deleteSource, final int compressionLevel) {
         Objects.requireNonNull(source, "source");
         Objects.requireNonNull(destination, "destination");
 
@@ -101,29 +86,16 @@ public final class GzCompressAction extends AbstractAction 
{
         this.destination = destination;
         this.deleteSource = deleteSource;
         this.compressionLevel = checkCompressionLevel(compressionLevel);
-        this.maxDelaySeconds = maxDelaySeconds;
-    }
-
-    /**
-     * Creates a new instance.
-     * @param source file to compress, may not be null.
-     * @param destination compressed file, may not be null.
-     * @param deleteSource if true, attempt to delete file on completion.
-     * @param compressionLevel Gzip deflater compression level.
-     */
-    public GzCompressAction(
-            final File source, final File destination, final boolean 
deleteSource, final int compressionLevel) {
-        this(source, destination, deleteSource, compressionLevel, 0);
     }
 
     /**
      * Prefer the constructor with compression level.
      *
-     * @deprecated Prefer {@link GzCompressAction#GzCompressAction(File, File, 
boolean, int, int)}.
+     * @deprecated Prefer {@link GzCompressAction#GzCompressAction(File, File, 
boolean, int)}.
      */
     @Deprecated
     public GzCompressAction(final File source, final File destination, final 
boolean deleteSource) {
-        this(source, destination, deleteSource, Deflater.DEFAULT_COMPRESSION, 
0);
+        this(source, destination, deleteSource, Deflater.DEFAULT_COMPRESSION);
     }
 
     /**
@@ -134,7 +106,6 @@ public final class GzCompressAction extends AbstractAction {
      */
     @Override
     public boolean execute() throws IOException {
-        blockThread(maxDelaySeconds);
         return execute(source, destination, deleteSource, compressionLevel);
     }
 
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/ZipCompressAction.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/ZipCompressAction.java
index d8e7c39738..f3bd48e2ce 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/ZipCompressAction.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/ZipCompressAction.java
@@ -51,11 +51,6 @@ public final class ZipCompressAction extends AbstractAction {
      */
     private final int level;
 
-    /**
-     * Maximum delay in seconds before compression.
-     */
-    private final int maxDelaySeconds;
-
     /**
      * Validates that the compression level is in the valid range [-1, 9].
      *
@@ -71,22 +66,15 @@ public final class ZipCompressAction extends AbstractAction 
{
     }
 
     /**
-     * Creates new instance of GzCompressAction.
+     * Creates new instance.
      *
      * @param source file to compress, may not be null.
      * @param destination compressed file, may not be null.
      * @param deleteSource if true, attempt to delete file on completion. 
Failure to delete does not cause an exception
      *            to be thrown or affect return value.
      * @param level the compression level
-     * @param maxDelaySeconds maximum delay in seconds before compression.
-     * @since 2.27.0
      */
-    public ZipCompressAction(
-            final File source,
-            final File destination,
-            final boolean deleteSource,
-            final int level,
-            final int maxDelaySeconds) {
+    public ZipCompressAction(final File source, final File destination, final 
boolean deleteSource, final int level) {
         Objects.requireNonNull(source, "source");
         Objects.requireNonNull(destination, "destination");
 
@@ -94,20 +82,6 @@ public final class ZipCompressAction extends AbstractAction {
         this.destination = destination;
         this.deleteSource = deleteSource;
         this.level = checkLevel(level);
-        this.maxDelaySeconds = maxDelaySeconds;
-    }
-
-    /**
-     * Creates new instance.
-     *
-     * @param source file to compress, may not be null.
-     * @param destination compressed file, may not be null.
-     * @param deleteSource if true, attempt to delete file on completion. 
Failure to delete does not cause an exception
-     *            to be thrown or affect return value.
-     * @param level the compression level
-     */
-    public ZipCompressAction(final File source, final File destination, final 
boolean deleteSource, final int level) {
-        this(source, destination, deleteSource, level, 0);
     }
 
     /**
@@ -118,7 +92,6 @@ public final class ZipCompressAction extends AbstractAction {
      */
     @Override
     public boolean execute() throws IOException {
-        blockThread(maxDelaySeconds);
         return execute(source, destination, deleteSource, level);
     }
 
diff --git 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/package-info.java
 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/package-info.java
index 85222541ad..3737053030 100644
--- 
a/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/package-info.java
+++ 
b/log4j-core/src/main/java/org/apache/logging/log4j/core/appender/rolling/action/package-info.java
@@ -18,7 +18,7 @@
  * Support classes for the Rolling File Appender.
  */
 @Export
-@Version("2.27.0")
+@Version("2.26.0")
 package org.apache.logging.log4j.core.appender.rolling.action;
 
 import org.osgi.annotation.bundle.Export;
diff --git a/src/changelog/.2.x.x/4012_add_max_compression_delay.xml 
b/src/changelog/.2.x.x/4012_add_max_random_delay.xml
similarity index 74%
rename from src/changelog/.2.x.x/4012_add_max_compression_delay.xml
rename to src/changelog/.2.x.x/4012_add_max_random_delay.xml
index 39cc883a15..128a8472e1 100644
--- a/src/changelog/.2.x.x/4012_add_max_compression_delay.xml
+++ b/src/changelog/.2.x.x/4012_add_max_random_delay.xml
@@ -8,7 +8,7 @@
     <issue id="4012" 
link="https://github.com/apache/logging-log4j2/issues/4012"/>
     <issue id="4071" 
link="https://github.com/apache/logging-log4j2/pull/4071"/>
     <description format="asciidoc">
-        Added support for `maxCompressionDelaySeconds` in compression actions 
to proactively defer compression and reduce disk I/O pressure during rollover.
+        Added support for `maxRandomDelay` on rolling appenders to start the 
asynchronous rollover action chain with a random delay and reduce disk I/O 
pressure during compression.
     </description>
 </entry>
 
diff --git 
a/src/site/antora/modules/ROOT/pages/manual/appenders/rolling-file.adoc 
b/src/site/antora/modules/ROOT/pages/manual/appenders/rolling-file.adoc
index 8dd9c91c0c..deb0ff99a0 100644
--- a/src/site/antora/modules/ROOT/pages/manual/appenders/rolling-file.adoc
+++ b/src/site/antora/modules/ROOT/pages/manual/appenders/rolling-file.adoc
@@ -280,6 +280,14 @@ If `true`, Log4j will lock the log file at **each** log 
event.
 Note that the effects of this setting depend on the Operating System: some 
systems like most POSIX OSes do not offer mandatory locking, but only advisory 
file locking.
 
 This setting can also reduce the performance of the appender.
+
+| [[RollingFileAppender-attr-maxRandomDelay]]maxRandomDelay
+| `int`
+| `0`
+a|
+Maximum random delay, in seconds, before the asynchronous rollover action 
chain starts.
+This can spread compression workload if many applications roll over at the 
same time.
+A value of `0` starts asynchronous actions immediately.
 |===
 
 
xref:plugin-reference.adoc#org-apache-logging-log4j_log4j-core_org-apache-logging-log4j-core-appender-RollingFileAppender[{plugin-reference-marker}
 Plugin reference for `RollingFile`]
@@ -304,6 +312,14 @@ If `true`, the appender starts writing at the end of the 
file.
 This setting does not give the same atomicity guarantees as for the
 <<RollingFileAppender-attr-append,`RollingFile` Appender>>.
 The log file cannot be opened by multiple applications at the same time.
+
+| [[RollingRandomAccessFile-attr-maxRandomDelay]]maxRandomDelay
+| `int`
+| `0`
+a|
+Maximum random delay, in seconds, before the asynchronous rollover action 
chain starts.
+This can spread compression workload if many applications roll over at the 
same time.
+A value of `0` starts asynchronous actions immediately.
 |===
 
 
xref:plugin-reference.adoc#org-apache-logging-log4j_log4j-core_org-apache-logging-log4j-core-appender-RollingRandomAccessFileAppender[{plugin-reference-marker}
 Plugin reference for `RollingRandomAccessFile`]
@@ -714,14 +730,6 @@ Maximum value for the <<conversion-pattern-integer,`%i`>> 
conversion pattern.
 
 This attribute is **ignored** if 
<<DefaultRolloverStrategy-attr-fileIndex,`fileIndex`>> is set to `nomax`.
 
-| 
[[DefaultRolloverStrategy-attr-maxCompressionDelaySeconds]]maxCompressionDelaySeconds
-| `int`
-| `0`
-|
-Maximum random delay, in seconds, before compressing archived log files.
-
-Use this attribute to spread compression workload when many applications roll 
over at the same time.
-A value of `0` disables the delay and starts compression immediately.
 |===
 
 
xref:plugin-reference.adoc#org-apache-logging-log4j_log4j-core_org-apache-logging-log4j-core-appender-rolling-DefaultRolloverStrategy[{plugin-reference-marker}
 Plugin reference for `DefaultRolloverStrategy`]

Reply via email to