# Performance ## Enabling the Builtin HTTP Cache Invalidation System Exposing a hypermedia API has [many advantages](http://blog.theamazingrando.com/in-band-vs-out-of-band.html). One of them is the ability to know exactly which resources are included in HTTP responses created by the API. We used this specificity to make API Platform apps blazing fast. When the cache mechanism [is enabled](configuration.md), API Platform collects identifiers of every resources included in a given HTTP response (including lists, embedded documents and subresources) and returns them in a special HTTP header called [Cache-Tags](https://support.cloudflare.com/hc/en-us/articles/206596608-How-to-Purge-Cache-Using-Cache-Tags-Enterprise-only-). A [cache reverse proxy](https://en.wikipedia.org/wiki/Web_accelerator) supporting cache tags (Varnish, CloudFlare, Fastly…) must be put in front of the web server and store all responses returned by the API with a high [TTL](https://en.wikipedia.org/wiki/Time_to_live). When a resource is modified, API Platform takes care of purging all responses containing it in the proxy’s cache. It means that after the first request, all subsequent requests will not touch the web server, and will be served instantly from the cache. It also means that the content served will always be fresh, because the cache is purged in real time. The support for most specific cases such as the invalidation of collections when a document is added or removed or for relationships and inverse relations is built-in. We also included [Varnish](https://varnish-cache.org/) in the [Docker setup](../deployment/docker.md) provided with the distribution of API Platform, so this feature works out of the box. Integration with Varnish and the Doctrine ORM is shipped with the core library. You can easily implement the support for any other proxy or persistence system. ## Enabling the Metadata Cache Computing metadata used by the bundle is a costly operation. Fortunately, metadata can be computed once and then cached. API Platform internally uses a [PSR-6](http://www.php-fig.org/psr/psr-6/) cache. If the Symfony Cache Component is available (the default in the official distribution), it automatically enables the support for the best cache adapter available. Best performance is achieved using [APCu](https://github.com/krakjoe/apcu). Be sure to have the APCu extension installed on your production server, API Platform will automatically use it. ## Using PPM (PHP-PM) Response time of the API can be improved up to 15x by using [PHP Process Manager](https://github.com/php-pm/php-pm). If you want to use it on your project, follow the documentation dedicated to Symfony on the PPM website. Keep in mind that PPM is still in an early stage of development and can cause issues in production. ## Doctrine Queries and Indexes ### Search Filter When using the `SearchFilter` and case insensivity, Doctrine will use the `LOWER` SQL function. Depending on your driver, you may want to carefully index it by using a [function-based index](http://use-the-index-luke.com/sql/where-clause/functions/case-insensitive-search) or it will impact performance with a huge collection. [Here are some examples to index LIKE filters](http://use-the-index-luke.com/sql/where-clause/searching-for-ranges/like-performance-tuning) depending on your database driver. ### Eager loading By default Doctrine comes with [lazy loading](http://doctrine-orm.readthedocs.io/en/latest/reference/working-with-objects.html#by-lazy-loading). Usually a killer time-saving feature and also a performance killer with large applications. Fortunately, Doctrine proposes another approach to remedy this problem: [eager loading](http://doctrine-orm.readthedocs.io/en/latest/reference/working-with-objects.html#by-eager-loading). This can easily be enabled for a relation: `@ORM\ManyToOne(fetch="EAGER")`. By default in API Platform, we made the choice to force eager loading for all relations, with or without the Doctrine `fetch` attribute. Thanks to the eager loading [extension](extensions.md). The `EagerLoadingExtension` will join every readable association according to the serialization context. If you want to fetch an association that is not serializable you've to bypass `readable` and `readableLink` by using the `fetchEager` attribute on the property declaration, for example: ```php /** * @ApiProperty(attributes={"fetchEager": true}) */ public $foo; ``` #### Max joins There is a default restriction with this feature. We allow up to 30 joins per query. Beyond, an `ApiPlatform\Core\Exception\RuntimeException` exception will be thrown but this value can easily be increased with a little of configuration: ```yaml # app/config/config.yaml api_platform: eager_loading: max_joins: 100 ``` Be careful when you exceed this limit, it's often caused by the result of a circular reference. [Serializer groups](serialization.md) can be a good solution to fix this issue. #### Force eager As mentioned above, by default we force eager loading for all relations. This behaviour can be modified with the configuration in order to apply it only on join relations having the `EAGER` fetch mode: ```yaml # app/config/config.yaml api_platform: eager_loading: force_eager: false ``` #### Override at resource and operation level When eager loading is enabled, whatever the status of the `force_eager` parameter, you can easily override it directly from the configuration of each resource. You can do this at the resource level, at the operations level, or both: ```php