openstack-uicore-foundation 5.0.64 → 5.0.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/plans/clickup-dropzone-cancel-chunk-slot-leak.md +58 -0
- package/lib/components/index.js +1 -1
- package/lib/components/index.js.map +1 -1
- package/lib/components/inputs/upload-input-v2.js +1 -1
- package/lib/components/inputs/upload-input-v2.js.map +1 -1
- package/lib/components/inputs/upload-input-v3.js +1 -1
- package/lib/components/inputs/upload-input-v3.js.map +1 -1
- package/lib/components/mui/formik-inputs/upload.js +1 -1
- package/lib/components/mui/formik-inputs/upload.js.map +1 -1
- package/lib/components/mui/upload-dialog.js +1 -1
- package/lib/components/mui/upload-dialog.js.map +1 -1
- package/lib/i18n.js +1 -1
- package/lib/i18n.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
TITLE
|
|
2
|
+
|
|
3
|
+
UploadInputV3: cancelling a chunked upload leaks concurrency slots and can stall the next upload
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
GOAL
|
|
7
|
+
|
|
8
|
+
When a user removes a file in the middle of a chunked upload, DropzoneJS aborts that file's in-flight chunk requests but never gives back their concurrency slots. The dialog stays mounted, so the leaked slots carry over to the next file the user picks in it. With parallelChunkUploads enabled, as sponsor-services has it, one cancel can use up every slot, and the next upload sits on "Uploading" with no error and no progress. A common way to hit this: the user picks the wrong file, deletes it while it is still uploading, then picks the right one.
|
|
9
|
+
|
|
10
|
+
Current state: xhr.abort() fires the XHR "abort" event, not "load", "error" or "timeout". DropzoneJS only releases a slot from its onload, onerror and ontimeout wrappers, so chunksInFlight is never decremented for aborted chunks. processChunkQueue also skips queued chunks only for files with status "error", not for cancelled ones, so leftover chunks of a removed file are still sent to the server. The counter only resets on unmount, for example when the dialog closes.
|
|
11
|
+
|
|
12
|
+
Target state: every chunk request that took a slot gives it back, however it ends (load, error, timeout or abort). Queued chunks of a cancelled file are dropped without being sent. A new file uploaded in the same dialog after a cancel starts and finishes normally.
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
TASKS
|
|
16
|
+
|
|
17
|
+
- Release the chunk slot when a throttled chunk request is aborted
|
|
18
|
+
- In the "sending" handler of src/components/inputs/dropzone/index.js, wrap xhr.onabort the same way xhr.ontimeout is wrapped: call onChunkComplete() when file._isThrottledChunk is set, then chain any existing handler
|
|
19
|
+
- Stop sending queued chunks that belong to a cancelled file
|
|
20
|
+
- In processChunkQueue, skip entries whose file has _canceled set or status "canceled", as well as "error"; a skipped entry does not take a slot
|
|
21
|
+
- In the removedfile handler, drop the removed file's entries from chunkQueue
|
|
22
|
+
- Add regression tests in src/components/inputs/dropzone/__tests__/dropzone.test.js
|
|
23
|
+
- Cancel with every slot in use, then queue a chunk for a new file: the new chunk is sent right away
|
|
24
|
+
- Chunks queued for a removed file are never passed to the original _uploadData
|
|
25
|
+
- Unmount while chunks are in flight: aborts do not push chunksInFlight below 0 and do not send anything
|
|
26
|
+
- Update the Upload Gotchas section of .claude/rules/openstack-uicore-foundation-components.md so it describes the abort-based slot release that is actually implemented
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
ACCEPTANCE CRITERIA
|
|
30
|
+
|
|
31
|
+
- With maxConcurrentChunks=2, two chunks of file A in flight, and file A removed, a chunk queued afterwards for file B goes out at once: chunkQueue.length is 0 and chunksInFlight is 1
|
|
32
|
+
- After file A is removed, none of its chunks still in chunkQueue reach the original _uploadData
|
|
33
|
+
- After componentWillUnmount runs with chunks in flight, chunksInFlight is 0 and no chunk is sent
|
|
34
|
+
- npx jest src/components/inputs/dropzone src/components/inputs/upload-input-v3 passes: the 81 existing tests (as of PR 330 head d4f53a5) plus the new ones, with 0 failures
|
|
35
|
+
- Manual check in sponsor-services, using a build that includes this change and a file large enough to fill every parallel chunk slot: start the upload, delete it mid-upload, pick another file in the same dialog without closing it. The second upload reaches Complete
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
DEVELOPMENT NOTES
|
|
39
|
+
|
|
40
|
+
Key files:
|
|
41
|
+
- src/components/inputs/dropzone/index.js: the sending handler wrappers, processChunkQueue, the removedfile handler
|
|
42
|
+
- src/components/inputs/dropzone/__tests__/dropzone.test.js: the regression tests. The Dropzone mock needs an _uploadData jest.fn so setupChunkThrottle installs the wrapper
|
|
43
|
+
- .claude/rules/openstack-uicore-foundation-components.md: the Upload Gotchas section
|
|
44
|
+
|
|
45
|
+
Gotchas:
|
|
46
|
+
- Release the slot in onabort inside "sending", not in removedfile. Dropzone's removeFile calls cancelUpload first, which already runs file.xhr.abort() (dropzone 5.7.2 dist/dropzone.js:2199-2201). By the time removedfile runs, that xhr's readyState is DONE, so the handler's readyState !== DONE check skips it, and a release placed there would miss that chunk
|
|
47
|
+
- Aborting an xhr that has already finished fires no event, so a chunk that already released its slot through onload cannot be released twice
|
|
48
|
+
- componentWillUnmount sets chunksInFlight to 0 before aborting. The later onabort calls are harmless because onChunkComplete floors the counter at 0 and the queue is already empty
|
|
49
|
+
- Only UploadInputV3 consumers that use parallelChunkUploads can reach a full stall. With sequential chunks, only one slot leaks per cancel, which cuts concurrency but does not block
|
|
50
|
+
- MUI Dialog unmounts its children when closed (keepMounted defaults to false), so the leak lasts only for one open dialog. This is why closing and reopening "fixes" it
|
|
51
|
+
|
|
52
|
+
Risk: skipping entries in processChunkQueue could leave a live file's chunk behind if the cancel check is too broad
|
|
53
|
+
Mitigation: skip only on _canceled, status "canceled" or status "error", and cover the live-file path with the existing chunk-throttle tests
|
|
54
|
+
|
|
55
|
+
Out of scope:
|
|
56
|
+
- The status-poll request timeout, which is a separate follow-up
|
|
57
|
+
- Moving UploadInputV3 row identity from name + size to a per-upload id
|
|
58
|
+
- Upload V1/V2 components, beyond sharing the DropzoneJS fix
|