-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Security] Cache voters that will always abstain #43066
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
Conversation
Should we explore dedicated PHP8 class attributes? IIUC eg. |
1f4e591
to
829fe63
Compare
I don't know if we need an attribute for that, but what about a method that exposes what a voter cares about @jderusse, instead of the current approach? |
@jderusse what about voters that vote only on |
@nicolas-grekas i generally like attributes enforcing static/cachable rules to us :) but runtime api also fits yes. As an end user taking care of such issues, an attribute looks elegant IMHO. |
An attribute might make the architecture of the AccessDecisionManager harder, if we want to avoid calling Reflection on each |
Understood. I meant levaraging DI at compile time. Writing another set of support method implementations already bugs/confuses me somewhat. But it'll be the edge case... WFM. |
Yeah, I hesitated with this approach: but it can not work for voters that use a pattern for attributes: ie RoleVoter =>
This will work: the current approach is to let voters reporting "I will always abstain for this attribute" and/or "I will always abstain for this object", everything else will always be called. If voters don't care about subjects (like RoleVoter) they can return The code will still be optimized because the voter will not be called when the attribute does not start with note: to be accurate RoleVoter does not vote "only on null subject", they vote on "whatever the content of the subject". |
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.
LGTM, except the naming :)
What about CacheableVoterInterface
with supportsAttribute()
and supportsType()
methods?
src/Symfony/Component/Security/Core/Authorization/Voter/RoleVoter.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Security/Core/Authorization/Voter/RoleVoter.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Security/Core/Authorization/Voter/CacheableAbstainVotesInterface.php
Outdated
Show resolved
Hide resolved
Cmments addressed, and tests added |
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.
2 comments + fabbot and good to me!
src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Security/Core/Authorization/Voter/RoleVoter.php
Outdated
Show resolved
Hide resolved
2d557cc
to
6508fbb
Compare
src/Symfony/Component/Security/Core/Authorization/Voter/CacheableVoterInterface.php
Outdated
Show resolved
Hide resolved
33390fe
to
ef8f125
Compare
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.
found some typos, but still 👍 on my side
/cc @symfony/mergers
src/Symfony/Component/Security/Core/Authorization/Voter/CacheableVoterInterface.php
Outdated
Show resolved
Hide resolved
src/Symfony/Component/Security/Core/Authorization/Voter/CacheableVoterInterface.php
Outdated
Show resolved
Hide resolved
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.
One minor comment, otherwise 👍🏼 thanks !
src/Symfony/Component/Security/Core/Authorization/AccessDecisionManager.php
Show resolved
Hide resolved
Thank you @jderusse. |
With the renaming of the interface and methods, the semantics seem reversed when you first look at it. Since the interface is now called |
|
@stof in that case the interface is misnamed, IMO, the original names seem more appropriate. |
The original names were confusing to me. Feels like double negation: I'm not against renaming |
It depends who gets to define "better". As I said, the weird part here is returning |
thing is, the feature is about improving performance when something IS NOT able to process the payload. Having a negation will always be confusing. One could use In my opinion If we need to change something it should be the name of the interface. Would be happy to make a PR if somebody have a suggestion. |
I'm on a phone so I can't explain on detail, but to me the current implementation is very intuitive because I understand it as follows: Symfony: Hey voter, do you support ARTICLE_EDIT attribute? |
This means the voter cacheability is a side-effect of this detection, it's inferred and not directly stated. Your votes is not becoming "cacheable", it's becoming "more capable of explaining what it knows how to work with".. which is the information then used to make it cacheable. It basically has "extended / detailed supports" support. |
I must say that I quite liked the That said, this little nuance can be fulfilled easily by documentation. |
The current implementation of the AccessDecisionManager is to iterate over all the Voters, and stops when the strategy is met.
When an application performs a lot of checks (ie. an Admin shows a list of "blog post" and shows buttons to users that are allowed to "edit", "delete", "publish", ... ie
{% if is_granted('blog_delete', item) %}DELETE{% endif %}
). In that case, the number of calls to theVoterInterface::vote
methods grows exponentially. At some point, adding a new Voter to handle a new case (maybe not related to our blog posts) will have a performance impactMoreover in most of the time, a voter looks like:
We could leverage this, to cache the list of voters that need to be called or can be ignored
In the same way
symfony/serializer
provides aCacheableSupportsMethodInterface
I suggest to add a newCacheableAbstainVotesInterface
that will remember voters that will always abstain vote on a given attribute or a given object's typeWhen the Voter does not implements the interface OR returns
false
will provide the current behavior: The voter is always called.How to use this PR:
Results:
For 40 Voters and calling 500 times
isGranted(variousAttributes, variousObjects)
it get -40% perf gain !Opinionated choices in that PR:
TODO