fix: batch bulk subscriber creation to prevent network error (#158)#197
Conversation
The original onCreate loop fired all N axios.post() calls simultaneously
without awaiting, overwhelming the browser networking stack and causing
a Network Error for large subscriber counts (e.g. 10,000).
Additionally, navigation("/subscriber") was called inside every .then(),
so the component unmounted on the first success while remaining requests
continued updating dead state.
Fix:
- Process requests in controlled batches of 10 using Promise.allSettled
- Navigate exactly once after all batches complete
- Show a progress bar with live count during creation
- Show an error summary (instead of per-request alerts) when any fail
- Disable the CREATE button while operation is in progress
Fixes free5gc#158
|
Hi @ALIIQBAL786, This is a solid improvement to the creation procedure—nice work on that. I do have a question regarding how you managed to create such a large number of UEs. Did you use the frontend to add them, or did you rely on backend APIs or scripts? As for the issue itself, my understanding is that the reporter is pointing out that the subscriber page panics when handling around 10K UEs, rather than focusing on the creation procedure. Happy to discuss further! Cheers, |
The subscriber list page panicked when loaded with a large number of UEs (e.g. 10,000) due to three issues: 1. filteredData.map() rendered ALL rows as DOM nodes simultaneously. With 10K subscribers this created 10K table rows at once, overwhelming the browser's rendering engine and causing a panic. Fix: slice filteredData to the current page before rendering. 2. count() always returned 0, so TablePagination showed incorrect controls and navigating pages had no effect. Fix: return filteredData.length as the true record count. 3. onDeleteSelected joined all selected items into a single confirm string. With 10K items selected this produced a massive string that crashed the browser's native confirm dialog. Fix: cap the preview to 5 items with a "...and N more" suffix. 4. Search did not reset page to 0, causing a blank view when the filtered result set was smaller than the current page offset. Fix: reset page to 0 on every search input change. Fixes free5gc#158
Fixes all 6 points raised in PR review: 1. Virtualization (react-window v2 `List`) Replace slice-based pagination with react-window FixedSizeList so only visible rows (~10) exist in the DOM regardless of dataset size. Pagination was a stopgap — bumping page size or adding "show all" would re-introduce the DOM explosion. Virtual scroll bounds DOM nodes permanently. 2. useMemo for filteredData Wrap the filter in useMemo([data, searchTerm]) so the full-array scan only runs when data or search term changes, not on every checkbox tick or button hover. Previously every selection state update re-ran the filter. 3. Threshold-based bulk delete confirmation Single confirm for < 100 items (count + 5-item preview). Double confirm for >= 100 items: first warning, then final irreversible step. Caps the preview string to avoid crashing the native confirm dialog with 10K+ selected items joined into one string. 4. Consistent state resets Clear selection on search change to avoid operating on subscribers no longer visible in filtered results. Virtual scroll eliminates the "blank page" edge case that required page-index resets. 5. Selection keyed by stable ueId+plmnID Selection is stored globally by identifier, not by row index or current visible slice. Selections survive scroll, search, and sort changes. Bulk delete operates on the full selected set, not just the visible page. 6. Stress test coverage Mock server extended with ?seed=N to generate up to 100K synthetic subscribers. Tested: 50K dataset, rapid search typing, select all → delete, repeated filter changes.
…eate setLoading(true) was being called in onCreate, causing the component to return <div>Loading...</div> early and hide the form and progress bar entirely. Users saw a blank screen with no feedback until all requests completed (or indefinitely for large counts like 50,000). The loading state is only meant for edit mode (fetching an existing subscriber's data). Batch creation progress is already tracked separately via createProgress state, which drives the progress bar and button label.
Replace the single-payload DELETE request with batched requests (BULK_DELETE_BATCH_SIZE=100 items each) so large selections such as 50 K subscribers don't exceed implicit browser/server payload limits that cause the request to be dropped without surfacing an error. Changes: - onDeleteSelected is now async and iterates in batches via await - deleteProgress state drives a LinearProgress bar and disables the button while deletion is in progress (mirrors onCreate pattern) - Console logs added at each batch for browser-side diagnostics - ariaAttributes type widened to Record<string, string | number> to match react-window v2's aria prop signature - Unused MUI Table/TableCell/TableHead/TableRow imports removed Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Raises BATCH_SIZE from 10 to 50 so 5× more requests run concurrently per batch, reducing total round trips for large subscriber counts (e.g. 50 K: 5 000 batches → 1 000 batches). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Tested against the real free5GC Docker stack. Summary of all changes: Bulk create fix (Issue #158) Subscriber list virtualization Reviewer feedback Progress bar fix Bulk delete fix |
CI hardened mode requires the lockfile to already contain all installed packages. yarn.lock was missing the react-window and @types/react-window entries added by this PR. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The branch had yarn.lock in yarn v1 classic format while main uses yarn v4 (berry) format with __metadata and checksums. Regenerate using corepack yarn@4.1.0 so the lockfile format matches main and CI hardened-mode checks pass. Adds react-window@2.2.7 and @types/react-window@1.8.8 entries that were missing from main's lockfile. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
@ALIIQBAL786 One thing I'd like to flag — the current approach replaces MUI's semantic To keep things consistent and theme-friendly, could you consider one of the following?
Also, a minor housekeeping note — there are quite a few comments that read more like commit messages or PR descriptions rather than long-term code documentation. Could you clean these up and keep only comments that explain non-obvious decisions? Either approach would help keep the codebase consistent and easier to maintain going forward. Let me know what you think! |
Replace all hardcoded color literals in SubscriberList with MUI sx theme token strings so styles adapt automatically to theme changes: - rgba(224,224,224,1) → "divider" - #fafafa → "background.default" - rgba(25,118,210,…) → "action.selected" / "action.focus" - rgba(0,0,0,0.04) → "action.hover" Remove comments that described what the code does (readable from the code) or duplicated PR/commit message content. Retain only the react-window pixel height note, which explains a non-obvious technical constraint. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Thanks for the review! You're right about the hardcoded colors, I've replaced all of them with MUI theme tokens ("divider", "action.selected", "background.default", etc.) in the latest commit, so they'll adapt automatically with the theme. Comments have been cleaned up too, kept only the one explaining the react-window height constraint since that's genuinely non-obvious. On the structure, that one's unfortunately a hard constraint from react-window. It renders each row into an absolutely-positioned , so putting elements inside it produces invalid HTML and breaks the layout. It also prevents react-window from measuring the container height correctly, which causes it to render 0 rows. CSS Grid with a shared column template is the standard workaround for this, it keeps the visual alignment consistent with what would give you, just without the semantic wrapper.
Let me know if anything else needs adjusting! |
|
Thanks for the detailed review @solar224, really appreciate you going through it carefully! Since the PR is already merged, I'll pick these up in a follow-up. The plan:
Will open a follow-up PR once these are ready! |
|
@ALIIQBAL786 Thanks for the quick reply and for outlining this detailed follow-up plan! Just wanted to give you a quick heads-up that I've actually already started working on these subsequent modifications on my end. So, no need to spend your time opening a new PR for this—I'll take it from here. Really appreciate your contribution and all your work on the initial PR! BR, |
|
Thanks for the heads-up @solar224, and happy to hand it off! Looking forward to seeing your improvements. Appreciate the kind words too! |
Problem
When creating multiple subscribers with a large `userNumber`, the frontend crashes with a Network Error.
Root cause: The original `onCreate` loop fired all N `axios.post()` calls simultaneously without awaiting. For large counts (e.g. 10,000), this overwhelms the browser's networking stack. Additionally, `navigation("/subscriber")` was called inside every `.then()`, so the component unmounted on the first success while thousands of in-flight requests continued updating dead state.
Fix
Tested
Fixes #158