Skip to content
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

SearchableDropdownTrait::getSource doesn't respect lazy-loading #11272

Closed
2 tasks done
johannesx75 opened this issue Jun 11, 2024 · 5 comments
Closed
2 tasks done

SearchableDropdownTrait::getSource doesn't respect lazy-loading #11272

johannesx75 opened this issue Jun 11, 2024 · 5 comments

Comments

@johannesx75
Copy link
Contributor

johannesx75 commented Jun 11, 2024

Module version(s) affected

5.2.10

Description

Prior to Silverstripe 5.2 auto scaffolding of has_one relations would use a NumericField when there were over 100 items.
With the introduction of SearchableDropdownField the auto scaffolding now uses lazy-loading for large lists.

Unfortunately in the construction of the SearchableDropdownField getSource is called by getSourceEmpty. Even for lazy loaded SearchableDropdownFields.

SearchableDropdownTrait::getSource in turn calls SelectField::getListMap which tries to convert the large list into an array in memory.

As far as I can tell this array is never used, since the lazy-loading search will work on the Search Context to find elements.
But trying to create the array on large lists leads to the form failing to render with a timeout or memory exhaustion.

How to reproduce

Reproduction Steps:

  1. Create simple model with has_one relationship (ClassA has_one ClassB, ClassB has_many ClassA)
  2. Add many thousands of Dataobjects of ClassB (250.000 in our case)
  3. Try opening ModelAdmin Detailform for ClassA

Possible Solution

Don't call getSource on lazyloaded SearchableDropdownFields.

Additional Context

Workaround: In ClassA::getCMSFields replace SearchableDropdownField with NumericField.

Validations

  • Check that there isn't already an issue that reports the same bug
  • Double check that your reproduction steps work in a fresh installation of silverstripe/installer (with any code examples you've provided)

New issues created

PRs

AFTER merging PR

Reassign to Guy - I want to revert these changes and do the cleaner (API breaking) fix in CMS 6.

@emteknetnz emteknetnz self-assigned this Jun 11, 2024
@emteknetnz emteknetnz removed their assignment Jun 11, 2024
@GuySartorelli GuySartorelli self-assigned this Jun 11, 2024
@emteknetnz emteknetnz removed their assignment Jun 12, 2024
@GuySartorelli GuySartorelli self-assigned this Jun 12, 2024
@GuySartorelli GuySartorelli changed the title SearchableDropdownTrait::getSource causes issue with large lists SearchableDropdownTrait::getSource doesn't respect lazy-loading Jun 12, 2024
@GuySartorelli
Copy link
Member

There are two different code paths that lead to calling getSource().

One is in the Field() method, which in DropdownField::Field() calls getSourceEmpty() which calls getSource().
We can take inspiration from silverstripe/tagfield here and just re-implement the Field() method to do the bare minimum that it needs to do.

The other is only when rendering the field in a react form (e.g. in elemental blocks). The call to getSchemaStateDefaults() ends up at SelectField::getSchemaStateDefaults() which calls getSource(). If I can find a clean solution for this one I'll raise a PR which solves both scenarios.

@GuySartorelli
Copy link
Member

Ultimately the fact that SearchableDropdownTrait::getSource() returns an array should be considered a bug - it says it does so for compatibility with the parent class, but the parent class has a return type of array|ArrayAccess and DataList implements ArrayAccess, so it could have just returned that.

Regardless, the return type is strongly typed so we can't just change that in CMS 5. I'll submit a PR that fixes it for 5, and then submit another PR that fixes it better for 6.

@emteknetnz
Copy link
Member

Linked PR has been merged, it will be automatically tagged shortly

@GuySartorelli
Copy link
Member

Reopened and assigned to me as per instructions in issue description

@GuySartorelli
Copy link
Member

getSourceEmpty() wants to concat something to the start of the source array (despite saying it can be ArrayAccess so there's a breach of types there) - I can't see a super easy way to resolve that right now so putting it in the too hard basket.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants