Skip to content

fix(volume): preserve existing speed test directories - #3082

Open
OldFriendWenjianjian wants to merge 3 commits into
spacedriveapp:mainfrom
OldFriendWenjianjian:fix/volume-speed-test-cleanup
Open

fix(volume): preserve existing speed test directories#3082
OldFriendWenjianjian wants to merge 3 commits into
spacedriveapp:mainfrom
OldFriendWenjianjian:fix/volume-speed-test-cleanup

Conversation

@OldFriendWenjianjian

@OldFriendWenjianjian OldFriendWenjianjian commented Aug 3, 2026

Copy link
Copy Markdown

Summary

  • never create or delete root-level speed-test directories; only use an existing writable directory
  • use unique, create-only permission probes and speed-test files so existing files are never overwritten
  • clean up only files owned by the current test, including after failed or timed-out speed tests
  • log cleanup failures with structured path, error, and artifact context

Testing

  • RUSTFLAGS="--cap-lints warn" cargo test -p sd-core volume::speed::tests --lib (9 passed)
  • RUSTFLAGS="--cap-lints warn" cargo check -p sd-core --lib
  • the focused suite includes the full speed-test workflow plus existing empty/non-empty tmp, file collision, and concurrent-test coverage

The lint cap is needed on the current main branch because sd-task-system forbids a new Rust 1.99 future-deprecation warning for AtomicUsize::fetch_update.

Fixes #3056

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Speed tests now use UUID-named files, exclusive creation, and ownership tracking. Cleanup removes only files created by the test and preserves existing directories and concurrent contents. Writable-directory checks no longer create directories.

Changes

Speed-test cleanup safety

Layer / File(s) Summary
Track test-location ownership
core/src/volume/speed.rs
TestLocation records created files. The test runner performs ownership-aware cleanup and reports cleanup errors.
Create test files exclusively
core/src/volume/speed.rs
Write iterations use UUID-named files and exclusive creation. Existing files are not overwritten.
Preserve directory contents
core/src/volume/speed.rs
Writable-directory checks probe existing directories and remove probe files safely. Tests cover preservation, collisions, concurrency, fixture setup, and final cleanup.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SpeedTest
  participant TestLocation
  participant FileSystem
  SpeedTest->>TestLocation: run test
  TestLocation->>FileSystem: create unique file exclusively
  FileSystem-->>TestLocation: write result
  TestLocation->>FileSystem: remove owned file
  FileSystem-->>SpeedTest: cleanup result
Loading

Poem

A rabbit checks each test file twice,
Then writes with names both safe and nice.
Old folders stay where they belong,
Owned files leave before too long.
The volume keeps its quiet peace.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title clearly identifies preserving existing speed-test directories, which is the main change.
Description check ✅ Passed The description includes the required summary, testing details, and issue reference.
Linked Issues check ✅ Passed The description references issue #3056 with a valid Fixes directive.
Out of Scope Changes check ✅ Passed The summarized changes directly support the stated cleanup and preservation objectives.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Comment thread core/src/volume/speed.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (2)
core/src/volume/speed.rs (2)

124-124: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use structured fields in the cleanup warnings.

Both warn! calls embed the error in the message string instead of using named fields. Structured fields let log tooling filter on the failing path or error kind without parsing free text.

♻️ Proposed fix for structured logging
-				warn!("Failed to remove test file: {}", e);
+				warn!(error = %e, path = %self.test_file.display(), "Failed to remove test file");
-				warn!("Failed to remove test directory: {}", e);
+				warn!(error = %e, path = %dir.display(), "Failed to remove test directory");

As per coding guidelines, "Include relevant context fields in structured logging, e.g., debug!(job_id = %self.id, "message")."

Also applies to: 135-135

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@core/src/volume/speed.rs` at line 124, Update both cleanup warning calls
around the test-file removal and cleanup paths to use structured logging fields
for the error instead of interpolating it into the message; preserve the
existing warning context and include the error as a named field such as error =
%e.

Source: Coding guidelines


69-73: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Preserve the primary test result if cleanup ever becomes fallible.

cleanup() always returns Ok(()): every failure branch (Line 124, Line 135) only calls warn! and continues. Given that, test_location.cleanup().await?; at Line 72 can never actually fail today, so the ? is currently a no-op.

This is fragile. cleanup()'s signature promises VolumeResult<()>, so a future change that makes it genuinely propagate a remove_file/remove_dir error is a natural evolution. If that happens, the current ordering discards the true perform_speed_test outcome (for example a VolumeError::Timeout) in favor of a cleanup error, because test_location.cleanup().await?; runs before let result = result?;.

Restructure so a cleanup failure never shadows the primary result.

♻️ Proposed fix to prioritize the primary result
 	let mut test_location = TestLocation::new(&volume.mount_point, &volume.mount_type).await?;
 	let result = perform_speed_test(&mut test_location, &config).await;
-
-	test_location.cleanup().await?;
-	let result = result?;
+	if let Err(cleanup_err) = test_location.cleanup().await {
+		warn!("Failed to clean up speed test resources: {}", cleanup_err);
+	}
+	let result = result?;

Also applies to: 110-141

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@core/src/volume/speed.rs` around lines 69 - 73, Update the test flow around
perform_speed_test and TestLocation::cleanup so the primary test result is
resolved before propagating any cleanup error: preserve and return the
perform_speed_test error while still always attempting cleanup, and only
propagate cleanup failure when the test itself succeeded. Apply the same
ordering to the cleanup-related flow in the referenced TestLocation
implementation.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@core/src/volume/speed.rs`:
- Around line 277-294: Update the cleanup in the successful branch of the
permission probe around permission_file and remove_file: inspect the removal
result and log the failure with sufficient context instead of discarding it,
while preserving the existing writable result behavior.
- Around line 459-481: Update test_concurrent_locations_use_different_files and
the shared-directory cleanup flow so concurrent cleanup cannot leave the
temporary directory behind. Serialize cleanup for locations sharing a directory,
or retry remove_dir after sibling file removal, and make the test explicitly
assert that the shared directory is removed successfully instead of relying only
on warn!.

---

Nitpick comments:
In `@core/src/volume/speed.rs`:
- Line 124: Update both cleanup warning calls around the test-file removal and
cleanup paths to use structured logging fields for the error instead of
interpolating it into the message; preserve the existing warning context and
include the error as a named field such as error = %e.
- Around line 69-73: Update the test flow around perform_speed_test and
TestLocation::cleanup so the primary test result is resolved before propagating
any cleanup error: preserve and return the perform_speed_test error while still
always attempting cleanup, and only propagate cleanup failure when the test
itself succeeded. Apply the same ordering to the cleanup-related flow in the
referenced TestLocation implementation.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 86ff38c8-5b84-499f-9143-66871f357de7

📥 Commits

Reviewing files that changed from the base of the PR and between 6dfeccf and 4d299fc.

📒 Files selected for processing (1)
  • core/src/volume/speed.rs

Comment thread core/src/volume/speed.rs
Comment thread core/src/volume/speed.rs
@OldFriendWenjianjian

Copy link
Copy Markdown
Author

Follow-up commit 75b6fc8 also addresses the two review-summary nitpicks:

  • cleanup warnings now use structured error and path fields
  • TestLocation::cleanup is explicitly best-effort and returns (), so cleanup cannot shadow the primary speed-test error

Validation after the update:

  • RUSTFLAGS="--cap-lints warn" cargo test -p sd-core volume::speed::tests --lib (9 passed)
  • RUSTFLAGS="--cap-lints warn" cargo check -p sd-core --lib

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
core/src/volume/speed.rs (1)

86-128: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract a shared remove-and-log helper.

cleanup() (lines 111-125) and the permission-probe removal (lines 275-287) implement the same pattern: remove a file, treat Ok and NotFound as success, and warn! on any other error. Extract one helper to avoid divergence between the two call sites as this code evolves.

♻️ Proposed refactor
+async fn remove_file_best_effort(path: &std::path::Path, context: &str) -> bool {
+	match tokio::fs::remove_file(path).await {
+		Ok(()) => true,
+		Err(e) if e.kind() == std::io::ErrorKind::NotFound => true,
+		Err(e) => {
+			warn!(error = %e, path = %path.display(), "Failed to remove {}", context);
+			false
+		}
+	}
+}
+
 	/// Clean up the test file
 	async fn cleanup(&mut self) {
 		// Never remove a file unless this speed test successfully created it.
 		if self.test_file_created {
-			match tokio::fs::remove_file(&self.test_file).await {
-				Ok(()) => {
-					self.test_file_created = false;
-				}
-				Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
-					self.test_file_created = false;
-				}
-				Err(e) => {
-					warn!(
-						error = %e,
-						path = %self.test_file.display(),
-						"Failed to remove speed test file"
-					);
-				}
-			}
+			if remove_file_best_effort(&self.test_file, "speed test file").await {
+				self.test_file_created = false;
+			}
 		}
 	}
 					Ok(mut file) => {
-						let write_result = file.write_all(b"test").await;
+						let write_ok = file.write_all(b"test").await.is_ok();
 						drop(file);
-						let cleanup_succeeded = match tokio::fs::remove_file(&permission_file).await
-						{
-							Ok(()) => true,
-							Err(error) if error.kind() == std::io::ErrorKind::NotFound => true,
-							Err(error) => {
-								warn!(
-									error = %error,
-									path = %permission_file.display(),
-									"Failed to remove speed test permission probe"
-								);
-								false
-							}
-						};
-						write_result.is_ok() && cleanup_succeeded
+						write_ok
+							&& remove_file_best_effort(&permission_file, "speed test permission probe")
+								.await
 					}

Also applies to: 262-296

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@core/src/volume/speed.rs` around lines 86 - 128, Extract the shared
remove-and-log behavior from TestLocation::cleanup and the permission-probe
removal around the permission test flow into one helper. Have the helper remove
the provided path, treat successful removal and NotFound as success, and warn
with the error and path for other failures; update both call sites to use it
while preserving test_file_created state handling in cleanup.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@core/src/volume/speed.rs`:
- Around line 86-128: Extract the shared remove-and-log behavior from
TestLocation::cleanup and the permission-probe removal around the permission
test flow into one helper. Have the helper remove the provided path, treat
successful removal and NotFound as success, and warn with the error and path for
other failures; update both call sites to use it while preserving
test_file_created state handling in cleanup.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d41172ff-f14e-44e7-b677-465ad702ed05

📥 Commits

Reviewing files that changed from the base of the PR and between 4d299fc and 75b6fc8.

📒 Files selected for processing (1)
  • core/src/volume/speed.rs

@OldFriendWenjianjian

Copy link
Copy Markdown
Author

Addressed the follow-up maintainability suggestion in f286bbf by sharing the remove/NotFound/warn behavior between final test-file cleanup and permission-probe cleanup.

The probe path deliberately calls the helper before combining its result with the write result, so a failed probe write cannot short-circuit cleanup and leave the created probe file behind.

Validation remains green:

  • RUSTFLAGS="--cap-lints warn" cargo test -p sd-core volume::speed::tests --lib (9 passed)
  • RUSTFLAGS="--cap-lints warn" cargo check -p sd-core --lib

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Critical Bug] Spacedrive automatically deletes root-level folder named "tmp" on tracked volumes

1 participant