forked from apache/hadoop
-
Notifications
You must be signed in to change notification settings - Fork 0
feat: Improve slive stress tests with path scanning and better file h… #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
zczhao0809
wants to merge
1
commit into
trunk
Choose a base branch
from
improve-slive-tests
base: trunk
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,16 +18,26 @@ | |
|
|
||
| package org.apache.hadoop.fs.slive; | ||
|
|
||
| import java.io.IOException; | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
| import java.util.Random; | ||
| import java.util.UUID; | ||
|
|
||
| import org.apache.hadoop.fs.FileStatus; | ||
| import org.apache.hadoop.fs.FileSystem; | ||
| import org.apache.hadoop.fs.Path; | ||
| import org.slf4j.Logger; | ||
| import org.slf4j.LoggerFactory; | ||
|
|
||
| /** | ||
| * Class which generates a file or directory path using a simple random | ||
| * generation algorithm stated in http://issues.apache.org/jira/browse/HDFS-708 | ||
| */ | ||
| class PathFinder { | ||
|
|
||
| private static final Logger LOG = LoggerFactory.getLogger(PathFinder.class); | ||
|
|
||
| private enum Type { | ||
| FILE, DIRECTORY | ||
| } | ||
|
|
@@ -38,11 +48,96 @@ private enum Type { | |
| private Path basePath; | ||
| private ConfigExtractor config; | ||
| private Random rnd; | ||
|
|
||
| // Used to store scanned existing paths | ||
| private List<Path> existingFiles; | ||
| private List<Path> existingDirs; | ||
|
|
||
| PathFinder(ConfigExtractor cfg, Random rnd) { | ||
| this.basePath = cfg.getDataPath(); | ||
| this.config = cfg; | ||
| this.rnd = rnd; | ||
| this.existingFiles = new ArrayList<>(); | ||
| this.existingDirs = new ArrayList<>(); | ||
| } | ||
|
|
||
| /** | ||
| * Scan all paths under base_dir and record existing files and directories | ||
| */ | ||
| private void scanBaseDirectory() { | ||
| try { | ||
| FileSystem fs = basePath.getFileSystem(config.getConfig()); | ||
| LOG.info("Starting to scan base_dir: " + basePath); | ||
| // Clear existing lists | ||
| clearExistingPaths(); | ||
|
|
||
| // Recursively scan directories | ||
| scanDirectoryRecursively(fs, basePath); | ||
|
|
||
| // Print summary only (avoid huge log output) | ||
| LOG.info("Scan complete: found " + existingFiles.size() + " files, " | ||
| + existingDirs.size() + " directories"); | ||
|
|
||
| } catch (IOException e) { | ||
| LOG.error("Error scanning base_dir: " + e.getMessage(), e); | ||
| clearExistingPaths(); | ||
| } | ||
| } | ||
|
|
||
| private void clearExistingPaths() { | ||
| existingFiles.clear(); | ||
| existingDirs.clear(); | ||
| } | ||
|
|
||
| /** | ||
| * Recursively scan directories | ||
| */ | ||
| private void scanDirectoryRecursively(FileSystem fs, Path dir) throws IOException { | ||
| if (!fs.exists(dir)) { | ||
| return; | ||
| } | ||
|
|
||
| FileStatus[] statuses = fs.listStatus(dir); | ||
| if (statuses == null || statuses.length == 0) { | ||
| return; | ||
| } | ||
|
|
||
| for (FileStatus status : statuses) { | ||
| Path path = status.getPath(); | ||
| if (status.isFile()) { | ||
| existingFiles.add(path); | ||
| } else if (status.isDirectory()) { | ||
| existingDirs.add(path); | ||
| // Recursively scan subdirectories | ||
| scanDirectoryRecursively(fs, path); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * Randomly select one from existing files | ||
| */ | ||
| private Path getExistingFile() { | ||
| if (existingFiles.isEmpty()) { | ||
| throw new RuntimeException("No files found in base_dir, cannot perform read/delete operations"); | ||
| } | ||
| int index = rnd.nextInt(existingFiles.size()); | ||
| Path selectedFile = existingFiles.get(index); | ||
| LOG.info("Selected from existing files: " + selectedFile); | ||
| return selectedFile; | ||
| } | ||
|
|
||
| /** | ||
| * Randomly select one from existing directories | ||
| */ | ||
| private Path getExistingDirectory() { | ||
| if (existingDirs.isEmpty()) { | ||
| throw new RuntimeException("No directories found in base_dir, cannot perform ls operations"); | ||
| } | ||
| int index = rnd.nextInt(existingDirs.size()); | ||
| Path selectedDir = existingDirs.get(index); | ||
| LOG.info("Selected from existing directories: " + selectedDir); | ||
| return selectedDir; | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -62,6 +157,10 @@ private enum Type { | |
| * @return Path | ||
| */ | ||
| private Path getPath(int curId, int limitPerDir, Type type) { | ||
| return getPath(curId, limitPerDir, type, null); | ||
| } | ||
|
|
||
| private Path getPath(int curId, int limitPerDir, Type type, String suffix) { | ||
| if (curId <= 0) { | ||
| return basePath; | ||
| } | ||
|
|
@@ -74,6 +173,9 @@ private Path getPath(int curId, int limitPerDir, Type type) { | |
| name = DIR_PREFIX + new Integer(curId % limitPerDir).toString(); | ||
| break; | ||
| } | ||
| if (suffix != null) { | ||
| name += "_" + suffix; | ||
| } | ||
| Path base = getPath((curId / limitPerDir), limitPerDir, Type.DIRECTORY); | ||
| return new Path(base, name); | ||
| } | ||
|
|
@@ -85,6 +187,57 @@ private Path getPath(int curId, int limitPerDir, Type type) { | |
| * @return path | ||
| */ | ||
| Path getFile() { | ||
| return getFile(null); | ||
| } | ||
|
|
||
| /** | ||
| * Gets a file path based on operation type and configuration | ||
| * | ||
| * @param operationType the type of operation (can be null for backward compatibility) | ||
| * @return path | ||
| */ | ||
| Path getFile(String operationType) { | ||
| boolean useNewAlgorithm = config.shouldUseNewAlgorithm(); | ||
|
|
||
| // Handle operations that need existing files | ||
| if (isExistingFileOperation(operationType)) { | ||
| if (useNewAlgorithm) { | ||
| LOG.info("Use new algorithm mode: scanning base_dir for " + operationType + " operation"); | ||
| scanBaseDirectory(); | ||
| return getExistingFile(); | ||
| } | ||
| // Fall through to original algorithm for normal mode | ||
| } | ||
|
|
||
| // Handle CREATE operation | ||
| if ("CREATE".equals(operationType)) { | ||
| if (useNewAlgorithm) { | ||
| LOG.info("Generating unique path for CREATE operation"); | ||
| return generateUniquePath(); | ||
| } | ||
| // Fall through to original algorithm for normal mode | ||
| } | ||
|
|
||
| // Use original algorithm for all other cases | ||
| LOG.info("Using original algorithm for " + (operationType != null ? operationType : "default") + " operation"); | ||
| return generateOriginalPath(); | ||
| } | ||
|
|
||
| private boolean isExistingFileOperation(String operationType) { | ||
| return "READ".equals(operationType) || "DELETE".equals(operationType) || | ||
| "TRUNCATE".equals(operationType) || "APPEND".equals(operationType) || | ||
| "RENAME_SRC".equals(operationType); | ||
| } | ||
|
|
||
| private Path generateUniquePath() { | ||
| int fileLimit = config.getTotalFiles(); | ||
| int dirLimit = config.getDirSize(); | ||
| int startPoint = 1 + rnd.nextInt(fileLimit); | ||
| String uniqueId = UUID.randomUUID().toString().replace("-", "").substring(0, 10); | ||
| return getPath(startPoint, dirLimit, Type.FILE, uniqueId); | ||
| } | ||
|
|
||
| private Path generateOriginalPath() { | ||
| int fileLimit = config.getTotalFiles(); | ||
| int dirLimit = config.getDirSize(); | ||
| int startPoint = 1 + rnd.nextInt(fileLimit); | ||
|
|
@@ -98,6 +251,30 @@ Path getFile() { | |
| * @return path | ||
| */ | ||
| Path getDirectory() { | ||
| return getDirectory(null); | ||
| } | ||
|
|
||
| /** | ||
| * Gets a directory path based on operation type | ||
| * For CREATE/MKDIR operations: use original algorithm (write to base_dir) | ||
| * For LS operations: scan base_dir and select from existing directories | ||
| * | ||
| * @param operationType the type of operation (can be null for backward compatibility) | ||
| * @return path | ||
| */ | ||
| Path getDirectory(String operationType) { | ||
| boolean useNewAlgorithm = config.shouldUseNewAlgorithm(); | ||
| // For LS operation, scan base_dir and select existing directories each time | ||
| if ("LS".equals(operationType)) { | ||
| if (useNewAlgorithm) { | ||
| LOG.info("Starting to scan base_dir and select existing directories for LS operation"); | ||
| scanBaseDirectory(); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ditto |
||
| return getExistingDirectory(); | ||
| } | ||
| // Fall through to original algorithm for normal mode | ||
| } | ||
|
|
||
| // Use original algorithm by default | ||
| int fileLimit = config.getTotalFiles(); | ||
| int dirLimit = config.getDirSize(); | ||
| int startPoint = rnd.nextInt(fileLimit); | ||
|
|
||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A full path scan for each getFile operation will slow down the test
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, I also tried the following methods:
Short-term caching of directory scan results: for example, using the previous scan results for 5 seconds + error-driven refresh: if a file_not_found error is encountered three times in a row, a refresh is forced; however, this method still has a high failure rate.
Using the new algorithm, 5000 operations took approximately 16 minutes, with most operations successfully executed. Using the original algorithm, 5000 operations took 3 minutes, with approximately 50% of the operations failing. This seems acceptable if accuracy is the only concern, not performance.
Do you have any suggestions for avoiding this problem?