From 93fe9caf2598f7e297ea961cfb26d97718e9ae67 Mon Sep 17 00:00:00 2001 From: "Gaur, Amit" Date: Sun, 26 Apr 2026 01:23:07 +0530 Subject: [PATCH] fix: store rowCount per-file to prevent cross-file index corruption The static field 'rowCount' in FileServerExtended was shared across all CSV files. When a Thread Group contained multiple ExtendedCsvDataSetConfig elements with different row counts, whichever called calculateRowCount() last would overwrite the global value. readRandom() and readUnique() then used this incorrect global value, generating line indices beyond the actual file size, causing NoSuchElementException from Optional.get() in readIndexed(). Fix: store rowCount per-FileEntry and add getRowCount(alias) to look up the correct count. The global static is retained for backward compatibility but deprecated. --- .../di/jmeter/utils/FileServerExtended.java | 34 +++++++++++++++++-- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/di/jmeter/utils/FileServerExtended.java b/src/main/java/com/di/jmeter/utils/FileServerExtended.java index 8c85daf..22eaef2 100644 --- a/src/main/java/com/di/jmeter/utils/FileServerExtended.java +++ b/src/main/java/com/di/jmeter/utils/FileServerExtended.java @@ -62,6 +62,11 @@ public class FileServerExtended { BASE_PREFIX_DEFAULT); private File base; + /** + * @deprecated Use per-file row count via {@link #getRowCount(String)} instead. + * Retained only for backward compatibility with callers that do not pass an alias. + */ + @Deprecated private static int rowCount; private static final ThreadLocal endPos = new ThreadLocal<>(); private static final ThreadLocal startPos = new ThreadLocal<>(); @@ -534,6 +539,7 @@ private static class FileEntry{ private final File file; private Closeable inputOutputObject; private final String charSetEncoding; + private int rowCount; FileEntry(File f, Closeable o, String e) { file = f; @@ -594,7 +600,11 @@ public void calculateRowCount(String filename, boolean ignoreFirstLine) { log.error(e.toString()); } - this.setRowCount(ignoreFirstLine ? count-1 : count ); + int effectiveCount = ignoreFirstLine ? count - 1 : count; + // Store per-file row count + fileEntry.rowCount = effectiveCount; + // Also update global static for backward compatibility + this.setRowCount(effectiveCount); } /** @@ -628,7 +638,8 @@ public static void setReadPosition(String threadName, int blockSize, boolean ign public synchronized String readRandom(String filename, boolean ignoreFirstLine) throws IOException { Random rand = new Random(); int startPos = ignoreFirstLine ? 1 : 0; - int randPos = rand.nextInt(((rowCount -1) - startPos) + 1) + startPos; + int fileRowCount = getRowCount(filename); + int randPos = rand.nextInt(((fileRowCount - 1) - startPos) + 1) + startPos; return readIndexed(filename, randPos); } @@ -646,7 +657,8 @@ public synchronized String readRandom(String filename, boolean ignoreFirstLine) */ public synchronized String readUnique(String filename, boolean ignoreFirstLine, String ooValue, int currPos, int startPos, int endPos) throws IOException { String line = null; - if(currPos < getRowCount()){ + int fileRowCount = getRowCount(filename); + if(currPos < fileRowCount){ line = readIndexed(filename, currPos); } if(ooValue.equalsIgnoreCase("Continue Cyclic")){ @@ -799,6 +811,22 @@ public String[] csvReadLine(String line, char delimiter) throws IOException { public static int getRowCount() { return rowCount; } + + /** + * Get the row count for a specific file by its alias. + * Falls back to the global static rowCount if the file is not found. + * + * @param alias the file alias used when reserving the file + * @return the row count for that specific file + */ + public int getRowCount(String alias) { + FileEntry fileEntry = files.get(alias); + if (fileEntry != null && fileEntry.rowCount > 0) { + return fileEntry.rowCount; + } + return rowCount; + } + public static void setRowCount(int count) { FileServerExtended.rowCount = count; }