fix: stale QuadItem removal after ClusterItem position updates - #1730
Conversation
dkhawk
left a comment
There was a problem hiding this comment.
Thank you @Aparnamohan1312 for fixing #1729.
Review Summary
- Root Cause & Core Fix: When a mutable
ClusterItemchanges coordinates after insertion, constructing a newQuadItem(item)generates updated coordinates.PointQuadTree.remove(quadItem)then traverses down the tree using the new coordinates rather than where the item was originally indexed, leaving a stale entry trapped inmQuadTree. - Solution Assessment: Storing the original
QuadItemcreated inaddItem()insidemItemMap(HashMap<T, QuadItem<T>>) allowsO(1)retrieval of the original wrapper duringremoveItem()andremoveItems(). This guarantees thatPointQuadTree.remove()uses the original cached coordinates where the item is stored. - Memory & Lifecycle Safety: Verified that
clearItems(),removeItem(), andremoveItems()all properly purge entries frommItemMap, preventing memory leaks. - Subclass Safety Enhancement: Added a safe fallback (
?: QuadItem(item)) inremoveItem()andremoveItems()so that any subclasses bypassingaddItem()will fall back safely to standard wrapper construction.
Test Coverage & On-Device Demo Verification
- Unit Tests: Expanded
QuadItemTest.javawith 5 regression tests (testUpdateItemAfterPositionChange,testRemoveItemAfterPositionChange,testUpdateItemPreventsStaleQuadTreeEntries,testRemoveItemsAfterPositionChange,testClearItemsAfterPositionChange).- Verified tests fail against unpatched code (
AssertionError: expected <0> but was <1>due to stale entries remaining inmQuadTree) and pass cleanly with the fix.
- Verified tests fail against unpatched code (
- Demo App Verification: Enhanced
ClusteringDiffDemoActivityandPersonto mutateClusterItemcoordinates in-place (setPosition()) and included 60 filler markers to triggerPointQuadTreequadrant splitting (MAX_ELEMENTS = 50). Verified on-device that rotating marker locations cleanly updates without stale clusters.
LGTM.
…moval (googlemaps#1730) - Expand QuadItemTest with comprehensive unit tests for single removal, bulk removal, clearing, and coordinate-boundary updates after a mutable ClusterItem changes position. - Add fallback to QuadItem(item) in NonHierarchicalDistanceBasedAlgorithm.kt removeItem/removeItems for subclass robustness if mItemMap is bypassed. - Enhance ClusteringDiffDemoActivity to mutate ClusterItem coordinates in-place and include background filler markers to trigger PointQuadTree quadrant splitting, demonstrating the fix on-device.
13bfec2 to
3d411a7
Compare
dkhawk
left a comment
There was a problem hiding this comment.
Hi @Aparnamohan1312,
Thank you again for the contribution. Notice that the cla/google check is currently failing on this PR.
Why the CLA check is failing
The cla/google bot verified that your GitHub account (@Aparnamohan1312, the PR opener) has signed the CLA. However, commit 50c12dc6 was authored using the email address aparna.mohan@walmart.com, which does not appear to be linked to your GitHub account or registered on your CLA signature.
The following contributors were found for this pull request:
✅ PR Opener: @Aparnamohan1312
❌ Author: <aparna.mohan@walmart.com>
How to fix this
You can resolve this using any of the following methods:
- Link the email to your GitHub account: Add
aparna.mohan@walmart.comto your GitHub email settings (https://github.com/settings/emails). Once verified, click the "rescan" link on thecla/googlecheck details page. - Register the corporate email on your CLA: If
aparna.mohan@walmart.comis covered under a corporate CLA, ensure it is added to your authorized contributor list at https://cla.developers.google.com/. - Amend the commit author email: If you prefer to use your personal or GitHub-associated email, you can amend the author email on your commit locally and force-push to your branch:
git commit --amend --author="Aparna Mohan <YOUR_CLA_EMAIL@example.com>" --no-edit git push --force-with-lease
Please let us know once you have updated the CLA check so we can proceed with merging this PR.
…moval (googlemaps#1730) - Expand QuadItemTest with comprehensive unit tests for single removal, bulk removal, clearing, and coordinate-boundary updates after a mutable ClusterItem changes position. - Add fallback to QuadItem(item) in NonHierarchicalDistanceBasedAlgorithm.kt removeItem/removeItems for subclass robustness if mItemMap is bypassed. - Enhance ClusteringDiffDemoActivity to mutate ClusterItem coordinates in-place and include background filler markers to trigger PointQuadTree quadrant splitting, demonstrating the fix on-device.
3d411a7 to
fb83466
Compare
Hi @dkhawk, thank you for reviewing. I've amended my email as suggested for CLA check. |
- Sync with latest origin/main containing PR #1740 (Gradle 9.6.1 / CI fixes), PR #1739 (LatLonQuad GroundOverlays), PR #1730 (clustering updateItem fix), and PR #1741 (XML serialization hardening). - Preserve all backwards-compatible @deprecated com.google.maps.android.ktx typealiases and forwarding bridges for 6.0.0-rc01. - Add play-services-location and mockito-kotlin dependencies to libs.versions.toml for KTX location module tests.
Summary
Fixes an issue where updateItem() may fail to remove the previously indexed QuadItem when a mutable ClusterItem changes position before being updated.
Reproduction
The issue occurs when the same mutable ClusterItem instance is reused:
Because the item's position has already changed, removeItem() reconstructs a new QuadItem using the updated coordinates instead of the coordinates used during insertion.
PointQuadTree.remove() then traverses the tree using the updated location, while the original QuadItem is still indexed under its previous location.
Root Cause
QuadItem caches the projected point at construction time.
removeItem() currently creates a new QuadItem from the current state of the ClusterItem. If the position has changed, the reconstructed QuadItem no longer represents the object that was originally inserted into the PointQuadTree, causing removal to search the wrong branch.
Fix
Store the original QuadItem created during addItem() in an internal lookup map keyed by the corresponding ClusterItem.
removeItem() and removeItems() now retrieve and remove the original QuadItem instance rather than constructing a new wrapper. This ensures removal uses the same cached coordinates that were used during insertion while preserving the existing public API and behavior.
Validation
Before submitting your PR, there are a few things you can do to make sure it goes smoothly:
BREAKING CHANGEfooter so when this change is integrated a major version update is triggered. See: https://www.conventionalcommits.org/en/v1.0.0/Fixes #< #1729> 🦕