Problem/Motivation

Back in Symfony 4.1 there was a new interface introduced. CachableSupportsMethodInterface: see this blogpost.

Applications can define lots of normalizers/denormalizers and Symfony must call the supportsNormalization() method for each of them whenever a new object is normalized/denormalized. In theory the result of supportsNormalization() can depend on multiple factors. In practice most normalizers only depend on the type and format and that information is easily cacheable.

That's the trick used to improve the Serializer performance. We've introduced a new CacheableSupportsMethodInterface for those normalizers/denormalizers that only use the type and the format in their supports*() methods:

namespace Symfony\Component\Serializer\Normalizer;

interface CacheableSupportsMethodInterface
{
    public function hasCacheableSupportsMethod(): bool;
}

We've already implemented this interface in all the built-in normalizers, so you don't have to change your code. If you have created your own normalizers, check if they can be cached in the same way and implement the interface if needed.

According to our own benchmarks, this change can make simple apps which use few normalizers up to 10% faster. Complex applications with lots of normalizers, such as API Platform apps, can be up to 40% faster.

All normalizer, at lease in JSON:API only check for support in the protected $supportedInterfaceOrClass property. This means we can speed up normalizers theoretically.

The reason we wouldn't want this, if there are normalizers that find out if they support normalization of a value based on something else than the type and format it might break. This is not the case in JSON:API and it is VERY hard to extend those normalizers.

Proposed resolution

Add and implement CacheableSupportsMethodInterface in Drupal\jsonapi\Normalizer\NormalizerBase.

Remaining tasks

Review code change and performance test.

User interface changes

None

API changes

None

Data model changes

None

Release notes snippet

No needed i think?

Performance tests

To test performance i used JsonApiDocumentTopLevelNormalizerTest. I changed the code so that every test runs its body 1000 times. I've run this 13 times with the hasCacheableSupportsMethod returning TRUE and 13 times when it returns FALSE. I've timed how long the test bodies ran for the tests.

Result overview:

td>

td>

td>

No Change: hasCacheableSupportsMethod(): false AVG (s) Performance %
JsonApiDocumentTopLevelNormalizerTest::testNormalize 20,49640742 100
JsonApiDocumentTopLevelNormalizerTest::testNormalizeUuid 18,34350703 100
JsonApiDocumentTopLevelNormalizerTest::testNormalizeException' 0,127467706 100
JsonApiDocumentTopLevelNormalizerTest::testNormalizeConfig 0,569442566 100
JsonApiDocumentTopLevelNormalizerTest::testDenormalizeUuid 7,924503657 100
JsonApiDocumentTopLevelNormalizerTest::testDenormalizeInvalidTypeAndNoType 0,825388615 100
JsonApiDocumentTopLevelNormalizerTest::testCacheableMetadata 7,338037656 100
55,62475465 100
Changed: hasCacheableSupportsMethod(): true AVG (s) Compared to no change
JsonApiDocumentTopLevelNormalizerTest::testNormalize 19,45793317 105,3370224
JsonApiDocumentTopLevelNormalizerTest::testNormalizeUuid 16,89642024 108,5644579
JsonApiDocumentTopLevelNormalizerTest::testNormalizeException' 0,081131733 157,1120206
JsonApiDocumentTopLevelNormalizerTest::testNormalizeConfig 0,49955832 113,9892065
JsonApiDocumentTopLevelNormalizerTest::testDenormalizeUuid 7,747466546 102,2850968
JsonApiDocumentTopLevelNormalizerTest::testDenormalizeInvalidTypeAndNoType 0,835800519 98,75425964
JsonApiDocumentTopLevelNormalizerTest::testCacheableMetadata 6,94360722 105,6804831
52,46191775 106,0288244

Issue fork drupal-3252872

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

bbrala created an issue. See original summary.

bbrala’s picture

Title: [IGNORE] bbrala test issue » Use CacheableSupportsMethodInterface for performance improvement in jsonapi normlizers
Issue summary: View changes
Priority: Minor » Normal
Issue tags: +Performance
StatusFileSize
new34.5 KB
new38.18 KB
bbrala’s picture

Issue summary: View changes
bbrala’s picture

Issue summary: View changes
bbrala’s picture

Issue summary: View changes
bbrala’s picture

Issue summary: View changes
bbrala’s picture

Issue summary: View changes
bbrala’s picture

Update issue summary and added all files.

bbrala’s picture

Status: Active » Needs review
gabesullice’s picture

Status: Needs review » Reviewed & tested by the community
Related issues: +#3031214: Introduce "deterministic" normalizers, +#3022583: [META] Normalization System: clean up/speed up/provide schema

Nice! I remembered this interface from a while back and found the issue where it was mentioned: #3031214-7: Introduce "deterministic" normalizers

Back then, we concluded that we couldn't use the Symfony interface until Drupal 9... well, D9 is here! Let's do this. I think we can close the original issue as outdated.

I had one nitpick about a missing comment. Other than that, let's mark this RTBC.

bbrala’s picture

Added related issue, didn't notice it in the queue. But guess the IS is a bit more up to date here.

bbrala’s picture

Title: Use CacheableSupportsMethodInterface for performance improvement in jsonapi normlizers » Use CacheableSupportsMethodInterface for performance improvement in jsonapi normalizers
bradjones1’s picture

While the jsonapi PHP API is internal... there are some, ahem, commonly used modules like jsonapi_extras that do things you're "not supposed to do." Is it worth a gander at those we know are out there, and see if there's any implication there?

bbrala’s picture

jsonapi_extras wont return TRUE for that method and therefor isnt affected, tests also run fune with this patch.

I've also checked schemata, which also doesn't seem to be affected, that module only uses the classes from core/serialisation which arn't affected by ths patch.

Should this change also end up in core serialisation it probably needs some more consideration on other extensions.

bbrala’s picture

Assigned: bbrala » Unassigned
alexpott’s picture

Status: Reviewed & tested by the community » Needs work
Issue tags: +Needs change record

I think we need a change record for this one.

It's also tempting to implement a BC layer by checking the class namespace - if it starts with on of Drupal core's namespace - return TRUE. If not trigger a deprecation and return FALSE.

bbrala’s picture

Issue tags: -Needs change record

Ive added the change record.

In regards to BC I'm not sure how we would go about that. What would we deprecate on, the fact the method is called but outside core? What would be the advise for the developers; implement the method? What if it should just return TRUE for the other class, should they overwrite eventhough the method is the same as the abstract class to remove the deprecation notice?

bbrala’s picture

Status: Needs work » Reviewed & tested by the community

Decided with alexpott that implenting the method in the concrete classes is better for BC. This means classes extending the abstract classes will keep working the same as before.

I've updated the change record and went through al implementations to double checked if they should support caching. Classes that extend other classes i have not changed. For example:

Drupal\jsonapi\Normalizer\EntityReferenceFieldNormalizer extends FieldNormalizer, there i only changed Drupal\jsonapi\Normalizer\FieldNormalizer to allow caching and have not implemented the method in the child classes.

alexpott’s picture

Status: Reviewed & tested by the community » Fixed

Committed and pushed 9df13e03446 to 10.0.x and bd858c23131 to 9.4.x. Thanks!

  • alexpott committed 9df13e0 on 10.0.x
    Issue #3252872 by bbrala, gabesullice, bradjones1, alexpott: Use...

  • alexpott committed bd858c2 on 9.4.x
    Issue #3252872 by bbrala, gabesullice, bradjones1, alexpott: Use...
wim leers’s picture

🥳

Tried to connect all loose ends here, to ensure we maximally follow through now that this step has been taken (thanks, @bbrala!!!):

bbrala’s picture

The issue title is wrong oops. Check out the last commit, al normalizers have been updated, also those in hal, serialization and layout_builder modules also.

bbrala’s picture

Title: Use CacheableSupportsMethodInterface for performance improvement in jsonapi normalizers » Use CacheableSupportsMethodInterface for performance improvement in normalizers

Updated title to reflect the change

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.