-
Notifications
You must be signed in to change notification settings - Fork 2k
Sites: improve memoization of the getSites selector #19760
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
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,7 +5,6 @@ | |
| */ | ||
|
|
||
| import { | ||
| assign, | ||
| compact, | ||
| every, | ||
| filter, | ||
|
|
@@ -68,36 +67,44 @@ export const getSiteBySlug = createSelector( | |
| ); | ||
|
|
||
| /** | ||
| * Returns a normalized site object by its ID. Intends to replicate | ||
| * the site object returned from the legacy `sites-list` module. | ||
| * | ||
| * Memoization cache for the `getSite` selector | ||
| */ | ||
| let getSiteCache = new WeakMap(); | ||
|
|
||
| /** | ||
| * Returns a normalized site object by its ID or site slug. | ||
| * | ||
| * @param {Object} state Global state tree | ||
| * @param {Number} siteId Site ID | ||
| * @param {Number|String} siteIdOrSlug Site ID or site slug | ||
| * @return {?Object} Site object | ||
| */ | ||
| export const getSite = createSelector( | ||
| ( state, siteId ) => { | ||
| let site = | ||
| getRawSite( state, siteId ) || | ||
| // Support for non-ID site retrieval | ||
| // Replaces SitesList#getSite | ||
| getSiteBySlug( state, siteId ); | ||
|
|
||
| if ( ! site ) { | ||
| return null; | ||
| } | ||
| export function getSite( state, siteIdOrSlug ) { | ||
| const rawSite = getRawSite( state, siteIdOrSlug ) || getSiteBySlug( state, siteIdOrSlug ); | ||
| if ( ! rawSite ) { | ||
| return null; | ||
| } | ||
|
|
||
| // To avoid mutating the original site object, create a shallow clone | ||
| // before assigning computed properties | ||
| site = { ...site }; | ||
| assign( site, getSiteComputedAttributes( state, siteId ) ); | ||
| assign( site, getJetpackComputedAttributes( state, siteId ) ); | ||
| // Use the rawSite object itself as a WeakMap key | ||
| const cachedSite = getSiteCache.get( rawSite ); | ||
| if ( cachedSite ) { | ||
| return cachedSite; | ||
| } | ||
|
|
||
| return site; | ||
| }, | ||
| state => [ state.sites.items, state.currentUser.capabilities ] | ||
| ); | ||
| const site = { | ||
| ...rawSite, | ||
| ...getSiteComputedAttributes( state, rawSite.ID ), | ||
| ...getJetpackComputedAttributes( state, rawSite.ID ), | ||
| }; | ||
|
|
||
| // Once the `rawSite` object becomes outdated, i.e., state gets updated with a newer version | ||
| // and no more references are held, the key will be automatically removed from the WeakMap. | ||
| getSiteCache.set( rawSite, site ); | ||
| return site; | ||
| } | ||
|
|
||
| getSite.clearCache = () => { | ||
| getSiteCache = new WeakMap(); | ||
|
||
| }; | ||
|
|
||
| export function getJetpackComputedAttributes( state, siteId ) { | ||
| if ( ! isJetpackSite( state, siteId ) ) { | ||
|
|
||
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
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So tracing this down the
state.sites.itemsarray holds a hard reference to ourrawSites. So in theory we should be able to GC weak refs in the map after state.sites.items is updated again, right?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the
rawSitewill be collected but the cache of it won't. as long as the actualrawSitereference is still in memory (such as instate.sites.items) then we'll be able to retrieve the value here. once it's no longer in use outside of this place it can be freed. at that point, we'll love our ability to retrieve the cached value; it's not clear to me if that value will leak or if the engine will free it as wellclearly I don't know what I'm doing here, but I tried to see if the weak map freed its local objects. in this ill-performed experiment i'm not sure what it indicates. but, the data is there for any more informed eyes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The
WeakMapentry holds a weak reference to the key and a strong reference to the value. If anyone is holding a strong reference to the key, they can use it to access the value in the map.If a reference to the key no longer exists, the entry becomes inaccessible and is removed from the map during the GC. During that GC, the strong reference to the value is removed. If it was the last reference to it, the value also becomes eligible for GC.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is what seems most expected to me, but I couldn't find the documentation for it; seems like it should be obvious.