You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
S3FS grep performance optimization: Addresses grep timeout issue with S3 backend in multi-subdirectory scenarios. Through three key optimizations - flat listing instead of recursive read_dir, concurrent chunked read instead of full download, and bypassing directory checks inread() - reduces grep latency from 60s+ to 1-3s for 1000-subdirectory scenarios.
Related Issue
Type of Change
Bug fix (non-breaking change that fixes an issue)
New feature (non-breaking change that adds functionality)
Breaking change (fix or feature that would cause existing functionality to not work as expected)
Documentation update
Refactoring (no functional changes)
Performance improvement
Test update
Changes Made
Only modifycrates/ragfs/src/plugins/s3fs/mod.rs, no impact on other plugins' default implementations
Entry handling - stat root path + regex compilation + single-file fast path
Flat file collection - use list_objects(delimiter=None) to get all files in one go (replaces O(n) recursive read_dir calls)
Concurrent chunked grep - 64KB chunked read + cross-chunk line boundary handling + 16MB long line protection + adaptive concurrency (16-100)
New helper functions: s3_is_excluded_path, s3_relative_match_file, s3_relative_depth (reproducing filesystem.rs's pub(crate) semantics)
Fix: Semantics of exclude_path == "/" to exclude all content (previously root directory exclusion was broken)
new unit tests: Covers complex scenarios like cross-chunk line stitching, EOF boundary, binary content, concurrent race conditions
Testing
I have added tests that prove my fix is effective or that my feature works
New and existing unit tests pass locally with my changes
I have tested this on the following platforms:
Linux
macOS
Windows
Performance Test Results (100 iterations):
Scenario
Before Optimization
After (native grep)
After (ripgrep)
1000-subdirectory grep
Timeout 60s+
~3s
~2.4s
Single-file grep
~0.5s
~0.3s
-
Checklist
My code follows the project's coding style
I have performed a self-review of my code
I have commented my code, particularly in hard-to-understand areas
I have made corresponding changes to the documentation
My changes generate no new warnings
Any dependent changes have been merged and published
The encode_path function now encodes spaces even when they are not in normalize_encoding_chars (as long as normalize_encoding_chars is non-empty). This is a behavioral change from the previous implementation which only encoded characters explicitly listed in normalize_encoding_chars. This may break existing code relying on the old encoding behavior.
Fix regression where spaces are now always encoded even when not included in normalize_encoding_chars. The new logic incorrectly adds space to the target character set unconditionally. Revert to only encoding characters explicitly present in target_chars.
-let is_target = |ch: char| ch != '/' && ch.is_ascii() && (ch == ' ' || target_chars.contains(&ch));+let is_target = |ch: char| ch != '/' && ch.is_ascii() && target_chars.contains(&ch);
if !path.chars().any(&is_target) {
return path.to_string();
}
let extra = path.chars().filter(|&ch| is_target(ch)).count() * 2;
let mut encoded = String::with_capacity(path.len() + extra);
for ch in path.chars() {
- if ch == '/' || !ch.is_ascii() || (ch != ' ' && !target_chars.contains(&ch)) {+ if ch == '/' || !ch.is_ascii() || !target_chars.contains(&ch) {
encoded.push(ch);
} else {
push_encoded_byte(&mut encoded, ch as u8);
}
}
Suggestion importance[1-10]: 9
__
Why: The PR introduced a regression where spaces are now encoded unconditionally, even when not in normalize_encoding_chars. This suggestion correctly reverts that logic, fixing a functional bug.
High
Fix atomic match count synchronization
Use Ordering::Acquire for loading matched_count and Ordering::Release for storing it to ensure proper cross-thread synchronization. This prevents stale reads of the match count and ensures updates are visible to other threads in a timely manner.
-let done = matched_count.load(Ordering::Relaxed);+let done = matched_count.load(Ordering::Acquire);
if done >= limit {
return Ok(());
}
let remaining = limit.saturating_sub(done);
let matches = self
.grep_one_file(base_path, &path, file_size, re, remaining)
.await?;
let mut r = result.lock().unwrap();
for m in matches {
if r.count >= limit {
break;
}
r.matches.push(m);
r.count += 1;
}
-matched_count.store(r.count, Ordering::Relaxed);+matched_count.store(r.count, Ordering::Release);
Suggestion importance[1-10]: 6
__
Why: Using Ordering::Relaxed for the atomic matched_count can lead to stale reads across threads. Switching to Acquire/Release improves synchronization, though the existing Mutex already protects the main result state.
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
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.
Description
S3FS grep performance optimization: Addresses grep timeout issue with S3 backend in multi-subdirectory scenarios. Through three key optimizations - flat listing instead of recursive
read_dir, concurrent chunked read instead of full download, and bypassing directory checks inread()- reduces grep latency from 60s+ to 1-3s for 1000-subdirectory scenarios.Related Issue
Type of Change
Changes Made
crates/ragfs/src/plugins/s3fs/mod.rs, no impact on other plugins' default implementationslist_objects(delimiter=None)to get all files in one go (replaces O(n) recursiveread_dircalls)s3_is_excluded_path,s3_relative_match_file,s3_relative_depth(reproducing filesystem.rs's pub(crate) semantics)exclude_path == "/"to exclude all content (previously root directory exclusion was broken)Testing
Performance Test Results (100 iterations):
Checklist
Screenshots (if applicable)
Additional Notes
Design Principles:
node_limit,exclude_path,level_limit,recursive,case_insensitivebehavior exactly matches default implementationclamp(available_parallelism()*2, 16, 100), 16MB partial buffer limit to prevent OOMKey Optimizations:
read(): Saves 2 S3 API calls per chunk read (directory_exists checks)