What can you do to get the best performance out of the commercetools Composable Commerce?
Everyone wants to be fast, everyone is afraid of the site getting slow. Although Composable Commerce is built for maximum scalability and reliable response times, there are many things a solution implementer can do in the code to keep response times low.
The following list of performance tips is not ordered by priority since the impact highly depends on the use case.
One final word: these are optimizations. It is OK if you can't follow them to implement a desired user experience. In other words: do not bother your UX person with the tips presented here to squeeze out some bits or milliseconds unless the performance impact becomes mission-critical.
API Request Planning
Please plan your API requests according to the following guidelines (ordered by importance):
- Avoid sequential API requests wherever you can.
- Use Reference Expansion instead of parallel or sequential requests (nowadays, network roundtrip time is more crucial than the number of bytes transferred).
- Do parallel requests. All the Composable Commerce SDKs support these and most good generic HTTP clients, too. You will need to plan ahead concerning by which identifiers to query, because you need them in the client request or session data to do parallel requests.
- Avoid Reference Expansion if you do not necessarily need it (as it takes time to perform the expansion internally). It's better to not include "expand" Parameters in your request defaults, but to add them on a case-by-case basis.
In any case, if you have use cases where the querying and reference expansion options force you into sequential requests, please post a suggestion on the Support Portal so that we can polish the API to avoid such cases.
Querying and Showing Data
- For a user-facing application, always go through the Product Projections endpoint. Use the Products endpoint only to implement administration, import/export etc. The API responses will be only half as large because the projections do only contain the
stagedversion of the data (depending on how you set the
stagedparameter), whereas the Products endpoint always returns the
stagedversion as full copies.
- Use the Product Projection Search endpoint wherever you can (even for fetching single products by some ID). Internally, the Product Projection Search endpoint is based on a different technology stack that delivers faster response times due to its read-only nature.
- When querying objects:
- Try to set a sensible
limitto the number of objects returned where you can. This depends on the cost of the query and on the size of the objects retrieved. As a rule of thumb, start with a small value and increase it in small steps until performance starts to drop off.
- Deactivate the field
totalwhen you are not interested in the total number of results matching the query. Refer to PagedQueryResult for more details.
- Avoid expensive query predicate patterns and understand the indexing behavior. This and specific tips are documented in the Query Predicate Documentation
- Try to set a sensible
When you create, update, or delete a product, it needs to be indexed before the change is available in the product projection search endpoint, which happens after a delay. This delay is not the same for all updates. Products with many variants, locales, prices, or searchable attributes take more time to get indexed than others, and thus, have a longer delay.
Additionally, when changing the configuration of your project that affects the products (for example, adding locales, currencies, countries, customer groups, or adding attributes to a product type), it will recreate the index for all products. Any following changes are put on hold until the process is completed and then applied later.
In the following list, you can find some thresholds below which a project should have a good indexing performance. Exceeding any of these values does not render the search unusable, but it may require the solution implementation to carefully design the frequency and timing of configuration changes that affect products. In that regard, we recommend you to apply all the configuration changes together before continuing with updating your products. This way, you optimize the effort for recreating the index of your project.
- 1 000 000 products per project
- 3 000 000 variants per project
- 15 locales per project
- 100 channels per project
- 20 suggestion keywords per product
- 25 searchable attributes across all product types (depending on the types of attributes and attribute values, for example, localizable texts have a bigger impact on indexing than boolean values)
Keep in mind that these values represent recommendations that you keep under control and not limits that commercetools Composable Commerce imposes.
Filtering, faceting and sorting
- Consider reducing the number of faceting attributes on the search request if you want to decrease the response time.
- When possible, use
filter.queryfilters to reduce the number of items to be faceted and/or sorted, as larger collections cause faceting and sorting to take proportionally longer.
- Use the filter by category subtree functionality whenever you want to retrieve products assigned to a specified category plus its subcategories.
For each search request you can enable the marked-matching-variants functionality that leads to marking those variants within the returned products which are matching the search criteria.
As matching variants can be complex to calculate, this feature is deactivated by default.
Since this feature may have an impact on the search performance, we let you control for which requests you want to enable it and when you don't need it.
Fetching a large amount of data from the product projection search endpoint results in longer response times. We recommend targeting response sizes of less than 1 MB, but we are aware this can sometimes be difficult. Here are some hints that may help you optimize your queries:
- For multi-language projects, use
localeProjectionparameter to limit the number of locales present in the search response.
- Consider setting up stores and using
storeProjectionin the search query to reduce the number of locales and prices in the response.
- To reduce the number of products returned, and therefore, the response size, try using lower
limitvalues in the search query.
Multiple Update Actions:
- It is possible to add a large number of update actions into one update request as long as they change the same Object (for example Update Product.
- If you want to publish the updated product right away, you can include the respective publish update action into the list of update actions. It must be the last action on the list though.
Bear in mind that all update actions in one update request are treated as one transaction. That means that the whole list of update actions will fail as soon as one update action in the list fails. Thus, please think carefully about which update actions you'll put into the list of actions and which actions you better spread across different update requests.
Caching Parts of the Model
Composable Commerce has been built for stateless frontend implementations, it is meant to pass every user request without caching in the frontend implementation. We recommend you to really do this until you have serious and specific performance problems. You may waste a large amount of time debugging cache staleness and its invalidation you probably not get paid for.
Nevertheless, many UI scenarios aren't doable without caching some data. This refers for example to the page header, especially category-driven menu trees and metadata like product types, channels, etc.
- Although you could query the whole category tree every time, it makes sense to cache the menu as a final menu object across users. We suggest to auto-refresh it very often to avoid inconsistencies, for example, every five seconds or so.
- Concerning User and Cart data, it depends on how much information is directly visible in the UIs initial state. In most cases, the information can be stored in an encrypted User Cookie (or a classic stateful server-side session) and the full User and/or Cart Object is pulled from the API on demand.
A notable exception to the general recommendation to avoid caching is that in some development frameworks (esp. in the content management system space) caching is built into the basic assumptions on a low level; In these cases it's not worth fighting against the framework's assumptions.
Import / Export / Sync Integrations
Most real-world projects have a number of backend system integrations. In addition to the general advice above, one efficient way to implement sync jobs is by querying the change events from the Messages API endpoint. This approach helps when integration with another system, because it enables you to create a job that polls the messages and remembers the "last message ID" (for example, in a CustomObject). This way your system integrations can provide "near-time" updates and the load is reduced on both your systems as well as on the Composable Commerce APIs.
To be on the safe side you can do full sync additionally every now and then.
Fetch only the fields that you need. This will result in smaller payloads, better for the network and the serialization/deserialization process.
<fieldName>Ref fields where the expanded resource is not needed.
Optimize your queries towards low Query complexity.