Magento 2.3.4 GraphQL vs RestAPI vs Serverless Lambda API performance

Modern PWA/Headless application Magento needs extremely fast API to fetch data under load. A lot of Magento folks think GraphQL resolves all M2 performance issues! Let’s check this legend.

I will do a test on the real production project. I don’t have time to make test environments with all required infrastructure. We have an instance C5.2xlarge and database RDS, Local Redis with (‘disable_locking’ => ‘1’).

FPC disabled because Magento tries to hide real GraphQL performance behind Cache (FPC/VARNISH etc). FPC cache works for the GrpahQL request sent by GET method. If you are sending a request by classic POST request FPC doesn’t work:

https://devdocs.magento.com/guides/v2.3/graphql/caching.html

API entry points to get products.

REST API:

result with the data:

“items”: [{
“id”: 1,
“sku”: “00000000000”,
“name”: “XXXXXXXXXXXXXXXXXX”,
“attribute_set_id”: 4,
“price”: 12.99,
“status”: 2,
“visibility”: 1,
“type_id”: “simple”,
“created_at”: “2016–12–20 20:25:23”,
“updated_at”: “2017–04–17 18:32:55”,
“weight”: 1,
“extension_attributes”: {
“website_ids”: [1],“category_links”: [
{
“position”: 1,
“category_id”: “2”
},

GrapQL entry point:

this is equal to classic POST request:

{
products(filter:{ sku: { eq: “0000000” } }) {
total_count
items {
name
sku
url_rewrites {
url
parameters {
name
value
}}
description {
html
}
related_products {
id
name
}}}}

Result:

{
“data”: {
“products”: {
“total_count”: 1,
“items”: [{
“name”: “XXXXXXXXXXXXXXXXX”,
“sku”: “00000000000000000000”,
“url_rewrites”: [{
“url”: “url”,
“parameters”: [{
“name”: “id”,
“value”: “58”
}]},{
“url”: “url/url”,
“parameters”: [{
“name”: “id”,
“value”: “58”
},
{
“name”: “category”,
“value”: “6”
}]},{
“url”: “/url”,
“parameters”: [
{
“name”: “id”,
“value”: “58”
},
{
“name”: “category”,
“value”: “2”
}]}],
“description”: {
“html”: “<p>Some description there </p>”
},
“related_products”: []
}]}}}
Image for post
Image for post
Magento 2 Serverless Lambda

AWS Serverless magento Lambda SaaS API attached to the load ballancer :

With All product data (options,related products, media gallary, ALL atributes, everething possible and not possible) not just partial data like GraphQL , and Magento API have. This API we are using in the SaaS Magento platform – Magneto.shop

Image for post
Image for post
“time”: 12, 
“total”: 1,“result”: [{
“row_id”: 1,
“entity_id”: 1,
“created_in”: 1,
“updated_in”: 2147483647,
“attribute_set_id”: 4,
“type_id”: “simple”,
“sku”: “0000000000”,
“has_options”: 0,
“required_options”: 0,
“created_at”: “2016–12–20T20:25:23.000Z”,
“updated_at”: “2017–04–17T18:32:55.000Z”,
“attributes”: [],
“inventory”: [],
“stock”: [],
“options”: [],
“configurations”: [],
“links”: []
“relations”:[]
“urls”:[]

TEST Scenarios:

Ok, let’s Go Test!

Rest API 1 product:

Image for post
Image for post

Result: 87ms

GraphQL 1 product :

Image for post
Image for post

Result: 88ms

Lambda API:

Image for post
Image for post

Result: 32ms

However script execution time is 15ms by lambda profiler timer. Lambda adds ~20sec time overhead.

Lets test Magento under load, 5 concurrent requests…

Magento native API:

Image for post
Image for post

Result: 281ms

Magento Graph GL:

Image for post
Image for post

Result: 246ms

Magento SaaS Serve-less API:

Image for post
Image for post

Result: 33ms

Test with concurrency: 10

Rest API:

Image for post
Image for post

Result: 380

Magento Graph QL:

Image for post
Image for post

Result: 420

SaaS Magento API:

Image for post
Image for post

Result: 31ms

Then testing the only lambda. Magento performance is “bad” /*I have been asked not to use bad words in the posts about Magento and not tell truth about performance and legacy architecture*/ for concurrent requests.

50 Lambda API requests:

Image for post
Image for post

100 Lamda API requests:

Image for post
Image for post

200 Lamda API requests:

Image for post
Image for post

300 Concurrent API calls:

Image for post
Image for post

500 concurrent API calls:

Image for post
Image for post

Result: 37 ms

1000 concurrent lambda API request:

Image for post
Image for post

Result: 45ms

Control result check: 1 request 3682 bytes 2000 request = 7364000 bytes if somebody thinking API returns errors or empty pages so fast instead of product data. One error page = 138 bytes

Why I have written this post amazon encreased my Lambda concurrency limit to 10 000 and now we can test bigger concurrency.

After 1000 concurent requess RDS has some issue:

max_allowed_packet = {value}

max_connections = {value}

and I have changed instance to m5.2xlarge

2000 Concurent requests:

Image for post
Image for post

Absolute Magento 2 record: Record 2073.40 API Requests per/second

3000 Concurent requests:

Image for post
Image for post

4000 Concurent requests:

Image for post
Image for post

After this test reaced timout I have 3 seconds per lamda.

During the test, we even haven’t reached MySQL performance limits. Max CPU utilization was less than 12% with our 2 vCore T3.medium RDS instance. We can see that the main performance problem is the Magento Core PHP framework, not MySQL performance. Magento can’t reach even 10 requests per second without response-time degradation.

Image for post
Image for post
Magento 1000 request per second Mysql Resouce utilization

4000 requests m4.4xlarge instance only 2.5 CPU utilization:

Image for post
Image for post

Magento with concurrency 20 graph QL API increases response 10 times:

Image for post
Image for post

For example, famous HallyHansen https://www.hellyhansen.com/ (Magento Cloud) can handle only 15 requests without response time degradation,

Image for post
Image for post

With 50 concurrent requests it generates 36 requests per second:

Image for post
Image for post
Maybe it is not starter cloud plan

Fastly Cloud Varnish Cache can produce only 196.28 requests per second:

Image for post
Image for post

Lambda API without the cache faster than Varnish Cache ;)

Also with each new product added to Magento collection response time ingresses on ~10ms (N+1 problem).

Time per 1 product: M2 88 lambda 28

Time per 2 product: M2 93 lambda 30

Time per 3 product: M2 107 lambda 33

Time per 10 products: M2 186 lambda 41

Time per 20 products: M2 278 lambda 57

Time per 50 products: M2 689 lambda 86

Time per 100 products: M2 1281 lambda 102

Magento 2 works fast only for low concurrency load (1–5 concurrent requests) for a single simple product. If you are want to have a good Magento eCommerce performance feel free to contact me (yegorshytikov@gmail.com) or Linkedin (https://www.linkedin.com/in/yehor-shytikov-20a24438/) together we can make your project fast! It is only a small part of all optimizations possible.

My Lamda doesn’t have this issue, yes response time groves mostly because more data should be looped in the function to build proper response results as a graph. Also, Lambda can be optimized for big data fetching by dividing the ORM collection into async batches in the same request (Not THE Magento Async API when you should wait and check/fetch response data by cron — https://devdocs.magento.com/guides/v2.3/rest/operation-status-endpoints.html)

Response time doesn’t matter much in case of serverless computing because 1000 to 3000 Lambda function calls can be executed simultaneously without blocking the next request. AWS Lambda is capable of serving multiple requests by horizontally scaling for multiple containers. Lambda can support up to 1000 parallel container executions by default. Scaling speed is 500 instances per minute. Concurrency is the number of requests that your function is serving at any given time. When your function is invoked, Lambda allocates an instance of it to process the event. When the function code finishes running, it can handle another request. If the function is invoked again while a request is still being processed, another instance is allocated, which increases the function’s concurrency.

API response time very from 10 to 100 ms for a single magento product API requests. That’s means Lambda theoreticaly(see picture below) can handle 10,000–300,000 requests per second!

In the case of lambda’s main problem, not performance but a limitation of concurrency. 100 requests per second pretty enough even for a company like Walmart. To protect your function from being overloaded, consider putting an API layer in front of your function with Amazon API Gateway. Request rate is the first thing you should consider when designing REST APIs. By default, API Gateway allows for up to 10,000 requests per second. AWS tracks the number of requests over a short period of time, such as one second or one minute. If requests beat the limit, you’ll see error responses with the 429 status code. This includes the “Retry-After” header. The PWA app should retry 429 requests for up to 30 seconds, based on each specific API’s recommendations and best practices.

After This post VueStorefront will be moved to Lambda ;) It also has some scalability issues…

In the case of Magento 2 to handle big amounts of multiple requests simultaneously you need to have a tremendous Machine with around 90 CPUs with the price 5000$ per month ore have a fast auto-scaling solution like this: https://github.com/Genaker/TerraformMagentoCloud. Low-performance PHP Magento 2 Framework uses a lot of CPUs. Don’t use Magento Commerce Cloud it doesn’t have autoscaling and has critical performance issues.

GraphQL doesn’t increase performance API it just allows you to combine several API requests into one however because of PHP single-threaded nature you will have bigger performance problems when you querying multiple entities in one request (comments, relates a product, product option, bundles, reviews). Also, Graph QL and Magento API are not so slow for now as legacy frontend because 3-d party extensions don’t add garbage code. Now all garbage is in the Templates, Layouts, Blocks. When companies will use GraphQl and will add more data/resolvers to the base functionality performance will drop significantly.

Read Also:

Written by

Magento/APP Cloud Architect. Melting metal server infrastructure into cloud solutions.