Skip to content

Fix deletion of partially-uploaded (pending) files#11655

Open
Copilot wants to merge 4 commits into1.9.xfrom
copilot/add-test-for-deleting-partially-uploaded-file
Open

Fix deletion of partially-uploaded (pending) files#11655
Copilot wants to merge 4 commits into1.9.xfrom
copilot/add-test-for-deleting-partially-uploaded-file

Conversation

Copy link
Copy Markdown
Contributor

Copilot AI commented Mar 26, 2026

Fixes: SER-1179

  • Fix abort() exception propagation for partially-uploaded files in Delete.php
  • Inject Log $log and capture exception as $e to surface unexpected abort() failures via $log->addTag('abortException', $e->getMessage())
  • Add test for deleting partially-uploaded file in StorageBase.php
  • Remove @ error-suppression operators from fopen/fread/fclose; add explicit assertion that file handle is valid
  • Add bucket cleanup at end of testDeletePartiallyUploadedFile to avoid leftover state across test runs

📍 Connect Copilot coding agent with Jira, Azure Boards or Linear to delegate work to Copilot in one click without leaving your project management tool.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 26, 2026

🔄 PHP-Retry Summary

Flaky tests detected across commits:

Commit 898e8e2 - 3 flaky tests
Test Retries Total Time Details
UsageTest::testVectorsDBStats 1 10.14s Logs
LegacyCustomClientTest::testListIndexes 1 240.48s Logs
LegacyCustomServerTest::testAttributeResponseModels 1 242.87s Logs
Commit b2b187c - 6 flaky tests
Test Retries Total Time Details
UsageTest::testPrepareStorageStats 1 1.71s Logs
DocumentsDBConsoleClientTest::testTimeout 1 120.05s Logs
DocumentsDBCustomServerTest::testTimeout 1 120.59s Logs
LegacyConsoleClientTest::testAttributeResponseModels 1 240.85s Logs
LegacyCustomServerTest::testOneToOneRelationship 1 240.11s Logs
LegacyCustomServerTest::testTimeout 1 123.25s Logs
Commit 71e714c - 4 flaky tests
Test Retries Total Time Details
LegacyCustomClientTest::testManyToManyRelationship 1 1.36s Logs
LegacyCustomServerTest::testListDocumentsWithCache 1 713ms Logs
LegacyTransactionsConsoleClientTest::testMixedSingleOperations 1 240.75s Logs
UsageTest::testVectorsDBStats 1 10.02s Logs

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 26, 2026

Greptile Summary

This PR fixes a server error that occurred when deleting a file whose chunked upload was cancelled before any bytes were written to disk. The root cause was that abort() on the Local storage device throws \Exception when the temporary chunk directory doesn't exist, and that exception propagated uncaught — leaving the pending DB document behind.

The fix wraps the abort() call in a try/catch (\Exception) so that a missing-chunks-on-disk scenario is treated as a clean deletion, allowing the DB document to be removed normally. A dedicated e2e test (testDeletePartiallyUploadedFile) validates the happy path by uploading only the first chunk of a multi-chunk file and asserting that the resulting pending file can be deleted successfully.

Key points:

  • The Local storage abort() implementation only throws \Exception for a missing directory, so the catch is correct for that adapter.
  • The catch (\Exception) is broad and will also swallow exceptions from cloud storage adapters (S3, B2, etc.) that may throw for transient reasons (network, credentials), potentially orphaning partial upload data on those providers without any log trace.
  • The new test does not clean up the bucket it creates, and uses error-suppressing @ operators for file I/O that could produce confusing failures if the test resource is ever missing.

Confidence Score: 4/5

Safe to merge for Local storage deployments; the broad exception catch warrants a follow-up for cloud storage adapters but is not a blocker.

The core fix is correct and well-targeted for the described bug. The exception type thrown by the Local device (\Exception) matches what is caught. The e2e test exercises the fixed path end-to-end. The one concrete concern — that catch (\Exception) silently swallows unexpected errors from cloud storage adapters — is a real edge case but not likely to cause problems in normal usage for the majority of deployments. P2 test hygiene issues do not affect merge safety.

src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Delete.php — review the exception catch breadth if cloud storage adapters are a primary concern.

Important Files Changed

Filename Overview
src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Delete.php Wraps abort() in a try/catch to gracefully handle cases where chunk data was never written to disk, allowing the pending DB document to be cleaned up; exception catch is correct but broad.
tests/e2e/Services/Storage/StorageBase.php New testDeletePartiallyUploadedFile test correctly exercises the fix by uploading only the first chunk and asserting the pending file can be deleted; minor: test bucket is never cleaned up, and file I/O uses suppressed-error @ operators.

Reviews (1): Last reviewed commit: "fix: allow deletion of partially-uploade..." | Re-trigger Greptile

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 26, 2026

✨ Benchmark results

  • Requests per second: 1,461
  • Requests with 200 status code: 263,067
  • P99 latency: 0.121484195

⚡ Benchmark Comparison

Metric This PR Latest version
RPS 1,461 1,067
200 263,067 192,024
P99 0.121484195 0.208676359

Copilot AI requested a review from Meldiron March 26, 2026 18:24
@blacksmith-sh

This comment has been minimized.

@Meldiron
Copy link
Copy Markdown
Contributor

@copilot Address two reviews above, I pinged you in both

Copy link
Copy Markdown
Contributor Author

Copilot AI commented Mar 26, 2026

@copilot Address two reviews above, I pinged you in both

Done in ecc76c7:

  • Delete.php: Inject Log $log, capture exception as $e, and call $log->addTag('abortException', $e->getMessage()) so unexpected failures surface in structured logs.
  • StorageBase.php: Removed @ error-suppression operators (added assertNotFalse on the file handle), and added bucket deletion at the end of testDeletePartiallyUploadedFile for proper cleanup.

// If the partial upload chunks are already gone from the device
// (e.g. the upload never wrote anything to disk), treat it as deleted
// so the pending file document can still be removed from the database.
$log->addTag('abortException', $e->getMessage());
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Not sure if we really want to add this...

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Since we catch all exceptions, we could be catching unwanted ones too, very hard to tell right now.

I want to keep this, but agree this maybe shouldnt be in log itself. moving it to span instead.

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.

4 participants