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

pvillard31 pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git


The following commit(s) were added to refs/heads/main by this push:
     new 1a57774cab4 NIFI-16357: Use 12 sections per container for 
FileSystemRepository in unit tests instead of 1024; this reduced run time from 
2+ minutes to 24 seconds. Additionally, made a few minor tweaks to improve 
timing-based reliability of tests. (#11691)
1a57774cab4 is described below

commit 1a57774cab455b04fac9f030b0ce8c26f4d0160c
Author: Mark Payne <[email protected]>
AuthorDate: Thu Sep 17 15:08:19 2026 -0400

    NIFI-16357: Use 12 sections per container for FileSystemRepository in unit 
tests instead of 1024; this reduced run time from 2+ minutes to 24 seconds. 
Additionally, made a few minor tweaks to improve timing-based reliability of 
tests. (#11691)
---
 .../repository/FileSystemRepository.java           | 35 +++++++++-----
 .../repository/TestFileSystemRepository.java       | 53 ++++++++++++----------
 2 files changed, 54 insertions(+), 34 deletions(-)

diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/repository/FileSystemRepository.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/repository/FileSystemRepository.java
index 7a9fc1eda6f..031734d7f52 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/repository/FileSystemRepository.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/main/java/org/apache/nifi/controller/repository/FileSystemRepository.java
@@ -84,8 +84,7 @@ import java.util.regex.Pattern;
  * Is thread safe
  */
 public class FileSystemRepository implements ContentRepository {
-
-    public static final int SECTIONS_PER_CONTAINER = 1024;
+    public static final int DEFAULT_SECTIONS_PER_CONTAINER = 1024;
     public static final long MIN_CLEANUP_INTERVAL_MILLIS = 
TimeUnit.SECONDS.toMillis(1L);
     public static final long DEFAULT_CLEANUP_INTERVAL_MILLIS = 
TimeUnit.MINUTES.toMillis(1L);
     public static final String ARCHIVE_DIR_NAME = "archive";
@@ -138,9 +137,17 @@ public class FileSystemRepository implements 
ContentRepository {
     private final Map<String, BlockingQueue<ArchiveInfo>> archivedFiles = new 
HashMap<>();
 
     private final NiFiProperties nifiProperties;
+    private final AtomicLong claimTruncationRuns = new AtomicLong(0L);
+    private final int sectionsPerContainer;
 
     public FileSystemRepository(final NiFiProperties nifiProperties) throws 
IOException {
+        this(nifiProperties, DEFAULT_SECTIONS_PER_CONTAINER);
+    }
+
+    FileSystemRepository(final NiFiProperties nifiProperties, final int 
sectionsPerContainer) throws IOException {
         this.nifiProperties = nifiProperties;
+        this.sectionsPerContainer = sectionsPerContainer;
+
         // determine the file repository paths and ensure they exist
         final Map<String, Path> fileRepositoryPaths = 
nifiProperties.getContentRepositoryPaths();
         for (final Path path : fileRepositoryPaths.values()) {
@@ -256,7 +263,7 @@ public class FileSystemRepository implements 
ContentRepository {
             executor.scheduleWithFixedDelay(new 
ArchiveOrDestroyDestructableClaims(), 1, 1, TimeUnit.SECONDS);
         }
 
-        final long cleanupMillis = 
this.determineCleanupInterval(nifiProperties);
+        final long cleanupMillis = determineCleanupInterval(nifiProperties);
         if (truncationEnabled) {
             executor.scheduleWithFixedDelay(new TruncateClaims(), 
cleanupMillis, cleanupMillis, TimeUnit.MILLISECONDS);
         }
@@ -317,7 +324,7 @@ public class FileSystemRepository implements 
ContentRepository {
             }
 
             // Ensure that the directory exists for the section, including the 
archive directory, if configured to archive data.
-            for (int i = 0; i < SECTIONS_PER_CONTAINER; i++) {
+            for (int i = 0; i < sectionsPerContainer; i++) {
                 final Path sectionPath = realPath.resolve(String.valueOf(i));
                 final Path toCreate = archiveData ? 
sectionPath.resolve(ARCHIVE_DIR_NAME) : sectionPath;
                 Files.createDirectories(toCreate);
@@ -352,7 +359,7 @@ public class FileSystemRepository implements 
ContentRepository {
 
     private void scanArchiveDirectories(final String containerName, final File 
containerDir, final ContainerState containerState) {
         long archivedFilesFound = 0L;
-        for (int i = 0; i < SECTIONS_PER_CONTAINER; i++) {
+        for (int i = 0; i < sectionsPerContainer; i++) {
             final File sectionDir = new File(containerDir, String.valueOf(i));
             final File archiveDir = new File(sectionDir, ARCHIVE_DIR_NAME);
             if (!archiveDir.exists()) {
@@ -368,12 +375,12 @@ public class FileSystemRepository implements 
ContentRepository {
             archivedFilesFound += filenames.length;
 
             final int sectionsScanned = i + 1;
-            if (sectionsScanned % ARCHIVE_SCAN_SECTION_LOG_INTERVAL == 0 && 
sectionsScanned < SECTIONS_PER_CONTAINER) {
+            if (sectionsScanned % ARCHIVE_SCAN_SECTION_LOG_INTERVAL == 0 && 
sectionsScanned < sectionsPerContainer) {
                 LOG.info("Scanned {} Sections for [{}] found {} Archived 
Files", sectionsScanned, containerName, archivedFilesFound);
             }
         }
 
-        LOG.info("Finished scanning {} Sections for [{}] found {} Archived 
Files", SECTIONS_PER_CONTAINER, containerName, archivedFilesFound);
+        LOG.info("Finished scanning {} Sections for [{}] found {} Archived 
Files", sectionsPerContainer, containerName, archivedFilesFound);
     }
 
     // Visible for testing
@@ -551,7 +558,7 @@ public class FileSystemRepository implements 
ContentRepository {
     public Set<ResourceClaim> getActiveResourceClaims(final File containerDir, 
final String containerName) {
         final Set<ResourceClaim> activeResourceClaims = new HashSet<>();
 
-        for (int i = 0; i < SECTIONS_PER_CONTAINER; i++) {
+        for (int i = 0; i < sectionsPerContainer; i++) {
             final String sectionName = String.valueOf(i);
             final File sectionDir = new File(containerDir, sectionName);
             if (!sectionDir.exists()) {
@@ -681,7 +688,7 @@ public class FileSystemRepository implements 
ContentRepository {
                 containerState.waitForArchiveExpiration();
             }
 
-            final long modulatedSectionIndex = currentIndex % 
SECTIONS_PER_CONTAINER;
+            final long modulatedSectionIndex = currentIndex % 
sectionsPerContainer;
             final String section = 
String.valueOf(modulatedSectionIndex).intern();
             final String claimId = System.currentTimeMillis() + "-" + 
currentIndex;
 
@@ -1065,6 +1072,10 @@ public class FileSystemRepository implements 
ContentRepository {
         resourceClaimManager.purge();
     }
 
+    long getClaimTruncationRuns() {
+        return claimTruncationRuns.get();
+    }
+
     private class TruncateClaims implements Runnable {
 
         @Override
@@ -1094,11 +1105,13 @@ public class FileSystemRepository implements 
ContentRepository {
                 final List<ContentClaim> toTruncate = new ArrayList<>();
                 resourceClaimManager.drainTruncatableClaims(toTruncate, 
10_000);
                 if (toTruncate.isEmpty()) {
-                    return;
+                    break;
                 }
 
                 truncateClaims(toTruncate, truncationActivationCache);
             }
+
+            claimTruncationRuns.incrementAndGet();
         }
 
         private void truncateClaims(final List<ContentClaim> toTruncate, final 
Map<String, Boolean> truncationActivationCache) {
@@ -1460,7 +1473,7 @@ public class FileSystemRepository implements 
ContentRepository {
         final StopWatch stopWatch = new StopWatch(true);
         final AtomicLong expiredFilesDeleted = new AtomicLong(0L);
         final AtomicLong expiredBytesDeleted = new AtomicLong(0L);
-        for (int i = 0; i < SECTIONS_PER_CONTAINER; i++) {
+        for (int i = 0; i < sectionsPerContainer; i++) {
             final Path sectionContainer = container.resolve(String.valueOf(i));
             final Path archive = sectionContainer.resolve("archive");
             if (!Files.exists(archive)) {
diff --git 
a/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/repository/TestFileSystemRepository.java
 
b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/repository/TestFileSystemRepository.java
index ba1a1ba4b60..490384a8472 100644
--- 
a/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/repository/TestFileSystemRepository.java
+++ 
b/nifi-framework-bundle/nifi-framework/nifi-framework-core/src/test/java/org/apache/nifi/controller/repository/TestFileSystemRepository.java
@@ -71,14 +71,10 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.fail;
 
 public class TestFileSystemRepository {
-
+    public static final int SECTIONS_PER_CONTAINER = 12;
     public static final File helloWorldFile = new 
File("src/test/resources/hello.txt");
     private static final Logger logger = 
LoggerFactory.getLogger(TestFileSystemRepository.class);
 
-    // The tests configure CONTENT_ARCHIVE_CLEANUP_FREQUENCY to "1 sec", which 
is the minimum allowed interval for
-    // the background TruncateClaims task. Waiting twice that interval ensures 
at least one cycle has executed.
-    private static final long BACKGROUND_TASK_WAIT_MILLIS = 2_000L;
-
     @TempDir
     private Path tempDir;
 
@@ -100,7 +96,7 @@ public class TestFileSystemRepository {
         );
         nifiProperties = 
NiFiProperties.createBasicNiFiProperties(originalNifiPropertiesFile.toString(), 
additionalProperties);
         maxClaimLength = 
DataUnit.parseDataSize(nifiProperties.getMaxAppendableClaimSize(), 
DataUnit.B).longValue();
-        repository = new FileSystemRepository(nifiProperties);
+        repository = new FileSystemRepository(nifiProperties, 
SECTIONS_PER_CONTAINER);
         claimManager = new StandardResourceClaimManager();
         repository.initialize(new 
StandardContentRepositoryContext(claimManager, EventReporter.NO_OP));
         repository.purge();
@@ -160,7 +156,7 @@ public class TestFileSystemRepository {
         // the freshly archived claim before this test observes it, leaving 
the archive count oscillating at 0.
         shutdown();
 
-        final FileSystemRepository localRepository = new 
FileSystemRepository(nifiProperties) {
+        final FileSystemRepository localRepository = new 
FileSystemRepository(nifiProperties, SECTIONS_PER_CONTAINER) {
             @Override
             public long getContainerUsableSpace(final String containerName) {
                 return Long.MAX_VALUE;
@@ -215,8 +211,8 @@ public class TestFileSystemRepository {
     public void testArchivedClaimRemovedDueToAge() throws IOException, 
InterruptedException {
         // Recreate Repository with specific properties
         final Map<String, String> propertyOverrides = new HashMap<>();
-        
propertyOverrides.put(NiFiProperties.CONTENT_ARCHIVE_MAX_RETENTION_PERIOD, "2 
sec");
-        
propertyOverrides.put(NiFiProperties.CONTENT_ARCHIVE_CLEANUP_FREQUENCY, "1 
sec");
+        
propertyOverrides.put(NiFiProperties.CONTENT_ARCHIVE_MAX_RETENTION_PERIOD, "20 
millis");
+        
propertyOverrides.put(NiFiProperties.CONTENT_ARCHIVE_CLEANUP_FREQUENCY, "10 
millis");
         
propertyOverrides.put(NiFiProperties.CONTENT_ARCHIVE_MAX_USAGE_PERCENTAGE, 
"99%");
         recreateRepositoryWithPropertyOverrides(propertyOverrides);
 
@@ -272,7 +268,7 @@ public class TestFileSystemRepository {
         repository.shutdown();
         nifiProperties = 
NiFiProperties.createBasicNiFiProperties(TestFileSystemRepository.class.getResource("/conf/nifi.properties").getFile(),
 propertyOverrides);
 
-        repository = new FileSystemRepository(nifiProperties);
+        repository = new FileSystemRepository(nifiProperties, 
SECTIONS_PER_CONTAINER);
         claimManager = new StandardResourceClaimManager();
         repository.initialize(new 
StandardContentRepositoryContext(claimManager, EventReporter.NO_OP));
         repository.purge();
@@ -319,7 +315,7 @@ public class TestFileSystemRepository {
             }
         }
 
-        repository = new FileSystemRepository(nifiProperties);
+        repository = new FileSystemRepository(nifiProperties, 
SECTIONS_PER_CONTAINER);
 
         for (final String containerName : containerPaths.keySet()) {
             assertEquals(3, repository.getArchiveCount(containerName));
@@ -378,7 +374,7 @@ public class TestFileSystemRepository {
             Files.createDirectories(bogus);
             bogus.toFile().setReadable(false);
 
-            repository = new FileSystemRepository(nifiProperties);
+            repository = new FileSystemRepository(nifiProperties, 
SECTIONS_PER_CONTAINER);
             repository.initialize(new StandardContentRepositoryContext(new 
StandardResourceClaimManager(), EventReporter.NO_OP));
         } finally {
             bogus.toFile().setReadable(true);
@@ -464,7 +460,7 @@ public class TestFileSystemRepository {
         repository.shutdown();
         Thread.sleep(1000L);
 
-        repository = new FileSystemRepository(nifiProperties);
+        repository = new FileSystemRepository(nifiProperties, 
SECTIONS_PER_CONTAINER);
         repository.initialize(new StandardContentRepositoryContext(new 
StandardResourceClaimManager(), EventReporter.NO_OP));
         repository.purge();
 
@@ -760,7 +756,7 @@ public class TestFileSystemRepository {
             // We are creating our own 'local' repository in this test so shut 
down the one created in the setup() method
             shutdown();
 
-            repository = new FileSystemRepository(nifiProperties) {
+            repository = new FileSystemRepository(nifiProperties, 
SECTIONS_PER_CONTAINER) {
                 @Override
                 protected boolean archive(Path curPath) {
                     archivedPaths.add(curPath);
@@ -811,7 +807,7 @@ public class TestFileSystemRepository {
             // We are creating our own 'local' repository in this test so shut 
down the one created in the setup() method
             shutdown();
 
-            repository = new FileSystemRepository(nifiProperties) {
+            repository = new FileSystemRepository(nifiProperties, 
SECTIONS_PER_CONTAINER) {
                 @Override
                 protected boolean archive(Path curPath) {
                     if (getOpenStreamCount() > 0) {
@@ -889,7 +885,7 @@ public class TestFileSystemRepository {
             // We are creating our own 'local' repository in this test so shut 
down the one created in the setup() method
             shutdown();
 
-            repository = new FileSystemRepository(nifiProperties) {
+            repository = new FileSystemRepository(nifiProperties, 
SECTIONS_PER_CONTAINER) {
                 @Override
                 protected boolean archive(Path curPath) {
                     if (getOpenStreamCount() > 0) {
@@ -1012,7 +1008,7 @@ public class TestFileSystemRepository {
         // We need to create our own repository that overrides 
getContainerUsableSpace to simulate disk pressure
         shutdown();
 
-        final FileSystemRepository localRepository = new 
FileSystemRepository(nifiProperties) {
+        final FileSystemRepository localRepository = new 
FileSystemRepository(nifiProperties, SECTIONS_PER_CONTAINER) {
             @Override
             public long getContainerUsableSpace(final String containerName) {
                 return 0; // Extreme disk pressure
@@ -1081,7 +1077,7 @@ public class TestFileSystemRepository {
     public void testTruncateNotActiveWhenDiskNotPressured() throws 
IOException, InterruptedException {
         shutdown();
 
-        final FileSystemRepository localRepository = new 
FileSystemRepository(nifiProperties) {
+        final FileSystemRepository localRepository = new 
FileSystemRepository(nifiProperties, SECTIONS_PER_CONTAINER) {
             @Override
             public long getContainerUsableSpace(final String containerName) {
                 return Long.MAX_VALUE;
@@ -1116,7 +1112,11 @@ public class TestFileSystemRepository {
             
localClaimManager.decrementClaimantCount(largeClaim.getResourceClaim());
             localClaimManager.markTruncatable(largeClaim);
 
-            Thread.sleep(BACKGROUND_TASK_WAIT_MILLIS);
+            final long initialRunCount = 
localRepository.getClaimTruncationRuns();
+            while (localRepository.getClaimTruncationRuns() == 
initialRunCount) {
+                Thread.sleep(50L);
+            }
+
             assertEquals(originalSize, Files.size(filePath));
         } finally {
             localRepository.shutdown();
@@ -1129,7 +1129,7 @@ public class TestFileSystemRepository {
         shutdown();
 
         final AtomicLong usableSpace = new AtomicLong(Long.MAX_VALUE);
-        final FileSystemRepository localRepository = new 
FileSystemRepository(nifiProperties) {
+        final FileSystemRepository localRepository = new 
FileSystemRepository(nifiProperties, SECTIONS_PER_CONTAINER) {
             @Override
             public long getContainerUsableSpace(final String containerName) {
                 return usableSpace.get();
@@ -1167,7 +1167,11 @@ public class TestFileSystemRepository {
             localClaimManager.markTruncatable(largeClaim);
 
             // Wait for at least one run of the background task with no disk 
pressure. File should not be truncated.
-            Thread.sleep(BACKGROUND_TASK_WAIT_MILLIS);
+            final long initialRunCount = 
localRepository.getClaimTruncationRuns();
+            while (localRepository.getClaimTruncationRuns() == 
initialRunCount) {
+                Thread.sleep(50L);
+            }
+
             assertEquals(originalSize, Files.size(filePath));
 
             // Now turn on disk pressure
@@ -1204,7 +1208,7 @@ public class TestFileSystemRepository {
     public void 
testTruncateClaimsSkipsClaimWithPositiveTruncationReferenceCount() throws 
IOException, InterruptedException {
         shutdown();
 
-        final FileSystemRepository localRepository = new 
FileSystemRepository(nifiProperties) {
+        final FileSystemRepository localRepository = new 
FileSystemRepository(nifiProperties, SECTIONS_PER_CONTAINER) {
             @Override
             public long getContainerUsableSpace(final String containerName) {
                 return 0;
@@ -1260,8 +1264,11 @@ public class TestFileSystemRepository {
             localClaimManager.markTruncatable(largeClaim);
             localClaimManager.incrementTruncationReferenceCount(largeClaim);
 
+            final long initialRunCount = repository.getClaimTruncationRuns();
             // Wait long enough for the TruncateClaims background task to have 
executed at least once
-            Thread.sleep(BACKGROUND_TASK_WAIT_MILLIS);
+            while (localRepository.getClaimTruncationRuns() == 
initialRunCount) {
+                Thread.sleep(50L);
+            }
 
             assertEquals(originalSize, Files.size(filePath),
                     "File should not be truncated because TruncateClaims 
should skip claims with positive truncation reference count");

Reply via email to