Replace O(n^2) array_merge accumulators with append#73
Open
dylancarruthers wants to merge 1 commit into
Open
Conversation
array_merge reallocates the accumulator on every call. Replacing the in-loop accumulator pattern with native append makes pagination and transform linear in the row count. - get(): array_merge($results, $page) -> array_push(..., ...$page) - splitRows(): array_merge($output, [$item]) -> $output[] = $item (x2) - transform(): same shape for $fnew, $mnew, and one helper output Inner per-row array_merge calls on small lists (tags, munge keys) are unchanged.
Member
|
By my count, the current main branch has 26 usages of |
Member
|
added new branch on my side from the current main, 72-large-imports-silently-truncate. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
get()andtransform()inlibrary/Netbox/Netbox.phpaccumulate results usingarray_merge($accum, [$item])(orarray_merge($accum, $page)) inside loops that run once per row. Becausearray_mergeallocates a fresh array and copies the entire accumulator on every call, the total work is O(n²) in the number of imported rows.For small imports this is invisible. For large ones it becomes catastrophic:
succeeded = y. Downstream sync rules then delete or under-modify hosts that were simply lost from the import.Diagnosis
Direct curl pagination of the same NetBox query returns all 26,074 rows cleanly in ~30s, confirming the truncation is module-side, not NetBox-side. Profiling showed the time is dominated by
array_mergecopying the growing accumulator on every iteration.There are three independent occurrences of the pattern, each in an outer per-row loop:
get()$results = array_merge($results, $response->results);transform()flatten loop$fnew = array_merge($fnew, [(object)$out]);transform()munge loop$mnew = array_merge($mnew, [(object)$row]);transform()tags loop$tnew = array_merge($tnew, [(object)$row]);The tags loop runs unconditionally on every import, so the bug fires for any import source, not just ones using flatten/munge.
Fix
Replace the four outer accumulators with native append (
$arr[] = $x) and the page-merge witharray_push($accum, ...$page). Behavior is identical, the arrays are numerically indexed andarray_mergedid not provide any semantic guarantee these calls were relying on, but the work becomes O(n).(Same shape for
$mnewand$tnew.)The remaining array_merge calls (inside per-row inner loops over tags and munge keys) operate on small lists and are left alone.
Measured impact
Same import source, same NetBox, same filter (~26,074 matching devices), unchanged module config:
Risk
array_push($a, ...$b)requires PHP 5.6+ (variadic spread). The module already uses type hints and class properties that require newer PHP, so this is well within the supported range.No semantic change: the order, types, and indices of the resulting array are identical to the previous code.