Skip to content

Fix/multiple file upload race condition sorting #15923

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
wants to merge 7 commits into
base: 3.x
Choose a base branch
from

Conversation

husseincak
Copy link

@husseincak husseincak commented Mar 23, 2025

Description

As described in #15308, when multiple files are uploaded in parallel, the order was determined by the upload completion time rather than the order selected by the user. This change retrieves the current file order from FilePond after all files finish processing and sends that order to the backend via reorderUploadedFilesUsing, ensuring the visible sort order is maintained.

Resolves the multiple file upload race condition.

Visual changes

Before (video): https://github.com/user-attachments/assets/c0c25759-42f2-4d52-a812-37a8a7688c0c

After (video): https://github.com/user-attachments/assets/993fd041-3812-4c9f-b7b7-86042931f527

Functional changes

  • Code style has been fixed by running the composer cs command.
  • Changes have been tested to not break existing functionality.

When multiple files are uploaded in parallel, the order was determined by
the upload completion time rather than the order selected by the user.
This change retrieves the current file order from FilePond after all files
finish processing and sends that order to the backend via
reorderUploadedFilesUsing, ensuring the visible sort order is maintained.

Resolves the multiple file upload race condition.
Extracted the duplicate code into updateFileOrder so both 'reorderfiles' and 'handleFileProcessing' use the same logic.
@danharrin
Copy link
Member

@angus-mcritchie what do you think of this, does it fix the problem for you?

@danharrin danharrin added the bug Something isn't working label Mar 23, 2025
@danharrin danharrin added this to the v3 milestone Mar 23, 2025
@angus-mcritchie
Copy link

@angus-mcritchie what do you think of this, does it fix the problem for you?

That fixes it @danharrin! Great work @husseincak 👏

@husseincak
Copy link
Author

husseincak commented Mar 24, 2025

I notice an issue. It only works when you put FileUpload::make('images')->reorderable()

If not, the files will not be reordered because in the BaseFileUpload.php - reorderUploadedFiles method there is an if statement that prevent the method from working. Im calling that method to keep the visual order of the files everytime a bunch of files are added.

     /**
     * @param  array<array-key>  $fileKeys
     */
    public function reorderUploadedFiles(array $fileKeys): void
    {
        if (! $this->isReorderable) {
            return;
        }

        $fileKeys = array_flip($fileKeys);

        $state = collect($this->getState())
            ->sortBy(static fn ($file, $fileKey) => $fileKeys[$fileKey] ?? null) // $fileKey may not be present in $fileKeys if it was added to the state during the reorder call
            ->all();

        $this->state($state);
    }

Now, im not sure if i have to touch this or delete that if, because it can have another consecuences.

@danharrin
Copy link
Member

You can probably remove that block

@@ -317,6 +306,9 @@ export default function fileUploadFormComponent({
return
}

// Get the current order as displayed in FilePond and pass it to the backend.
await this.updateFileOrder()
Copy link
Member

Choose a reason for hiding this comment

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

Can we skip this if there is only 1 file? Otherwise there is another network request for no reason.

Are there any other situations where it can be skipped?

Copy link
Author

@husseincak husseincak Apr 2, 2025

Choose a reason for hiding this comment

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

In principle, I see three easy cases to check:

  • No files: If there are no files, there's nothing to reorder.
  • Only one file: With just one file, no ordering is needed.
  • Order unchanged: If the new order is identical to the previous order (i.e., nothing has actually moved, like appending files at the end and is already correct), we can skip the request.

There's also a slightly more complex case that I'm not sure is worth pursuing—I haven't explored it thoroughly yet:

  • Debouncing rapid events: If multiple reorder events fire in quick succession, a debounce check might avoid redundant requests (though this may be overkill depending on how often it occurs).

I'll review this further, and I think I can handle the first three options. This is my first PR, so feel free to let me know if I have to follow any etiquette or convention or Im doing something wrong.

Copy link
Member

Choose a reason for hiding this comment

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

Just fixing those cases would be great, or we will get complaints that it is inefficient :)

@github-project-automation github-project-automation bot moved this from Todo to In Progress in Roadmap Apr 2, 2025
@danharrin
Copy link
Member

Hi, are you able to making the suggested changes so we can merge the PR?

@husseincak
Copy link
Author

husseincak commented May 22, 2025

Hi,

I managed to make the optimization changes (I have them another branch locally). The problem is that I realized the bug isn't completely fixed. In most cases, the file order is correct after uploads or edits, but there are still some situations where it fails. I have been trying to solve it with no luck.

It looks like FilePond’s internal ordering logic is still interfering in some edge cases, even with the current workaround.

Where the bug happens:
The issue occurs at this line: filament/packages/forms/resources/js/components/file-upload.js:252:

this.pond.files = await this.getFiles()

The array returned by await this.getFiles() is in the correct order (matching the backend and intended UI order). However, after assigning it to this.pond.files, FilePond reorders the files internally, resulting in the wrong visual order. In the server, the order is correct.

Example scenario:
Suppose you have these files in order:

  1. A.jpg
  2. B.jpg
  3. C.jpg

If you insert a two new files (1.jpg) at the start and (2.jpg) at the end the expected order after saving should be:

  1. 1.jpg
  2. A.jpg
  3. B.jpg
  4. C.jpg
  5. 2.jpg

But after the saving, the order becomes:

  1. 1.jpg
  2. A.jpg
  3. C.jpg
  4. 2.jpg <--- Wrong
  5. B.jpg

If you refresh the browser, the order is showed correctly, so it means that in the server are being save correctly:

  1. 1.jpg
  2. A.jpg
  3. B.jpg
  4. C.jpg
  5. 2.jpg

Summary:

  • The order in await this.getFiles() is correct.
  • After assigning to this.pond.files, the order of the files inside this.pond.files is wrong.
  • This seems to be an internal FilePond issue, possibly related to how it merges local and remote files or handles reordering.

Here is a video of what is happening exactly:

Screen.Recording.on.2025-05-23.at.01-11-53.mp4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working pending changes
Projects
Status: In Progress
Development

Successfully merging this pull request may close these issues.

3 participants