GraphQL Application Server

The Commerce GraphQL Application Server enables Adobe Commerce to maintain state among Commerce GraphQL API requests. GraphQL Application Server, which is built on the Swoole extension, operates as a process with worker threads that handle request processing. By preserving a bootstrapped application state among GraphQL API requests, GraphQL Application Server enhances request handling and overall product performance. API requests become significantly more efficient.

GraphQL Application Server is available for Adobe Commerce only. It is not available for Magento Open Source. You must submit an Adobe Commerce Support ticket to enable GraphQL Application Server on Pro projects.

NOTE
GraphQL Application Server is currently not compatible with Amazon Simple Storage Service (AWS S3). Adobe Commerce on cloud infrastructure customers currently using AWS S3 for remote storage cannot use GraphQL Application Server until Adobe releases a hotfix later in 2024.

Architecture

GraphQL Application Server maintains state between Commerce GraphQL API requests and eliminates the need for bootstrapping. By sharing application state across processes, GraphQL requests become significantly more efficient, decreasing response times by up to 30%.

The share-nothing PHP execution model provides a challenge from the perspective of latency because each request requires the bootstrapping of the framework. This bootstrapping process includes time-consuming tasks such as reading configuration, setting up the bootstrap process, and creating service class objects.

Transitioning request handling logic to an application-level event loop appears to address the challenge of streamlining request processing at an enterprise level. This approach eliminates the need for bootstrapping during the request execution lifecycle.

Advantages

GraphQL Application Server allows Adobe Commerce to sustain state between consecutive Commerce GraphQL API requests. Sharing application state across requests enhances API request efficiency by minimizing processing overhead and optimizing request handling. As a result, GraphQL request response time can be reduced up to 30%.

System requirements

Running GraphQL Application Server requires the following:

  • PHP 8.2 or higher
  • Swoole PHP extension v5+ installed
  • Adequate RAM and CPU based on the expected load

Enable and deploy on cloud infrastructure

The ApplicationServer module (Magento/ApplicationServer/) enables GraphQL Application Server.

Enable Pro projects

NOTE
The Application Server is an opt-in feature on Cloud Pro instances. To enable it, submit a support request.

After the Application Server feature is enabled on your Pro project, complete the following steps before deploying GraphQL Application Server:

  1. Deploy Adobe Commerce on cloud infrastructure using the cloud template from the 2.4.7-appserver branch.

  2. Ensure that all your Commerce customizations and extensions are compatible with GraphQL Application Server.

  3. Clone your Commerce Cloud project.

  4. Adjust settings in the ‘application-server/nginx.conf.sample’ file if necessary.

  5. Comment out the active ‘web’ section in project_root/.magento.app.yaml file entirely.

  6. Uncomment the following ‘web’ section configuration in the project_root/.magento.app.yaml file that includes the GraphQL Application Server start command.

    code language-yaml
    web:
        upstream:
            socket_family: tcp
            protocol: http
        commands:
            start: ./application-server/start.sh > var/log/application-server-status.log 2>&1
    
  7. Add updated files to the git index with this command:

    code language-bash
    git add -f .magento.app.yaml application-server/*
    
  8. Commit your changes with this command:

    code language-bash
    git commit -m "AppServer Enabled"
    

Deploy Pro projects

After completing the enablement steps, push changes to your git repository to deploy GraphQL Application Server:

git push

Enable Starter projects

Complete the following steps before deploying GraphQL Application Server on Starter projects:

  1. Deploy Adobe Commerce on cloud infrastructure using the cloud template from the 2.4.7-appserver branch.

  2. Ensure that all your Commerce customizations and extensions are compatible with GraphQL Application Server.

  3. Confirm that the CRYPT_KEY environment variable is set for your instance. You can check the status of this variable on the Cloud Project Portal (Onboarding UI).

  4. Clone your Commerce Cloud project.

  5. Rename application-server/.magento/.magento.app.yaml.sample to application-server/.magento/.magento.app.yaml and adjust settings in .magento.app.yaml if needed.

  6. Uncomment the following route’s configuration in the project_root/.magento/routes.yaml file to redirect /graphql traffic to GraphQL Application Server.

    code language-yaml
    "http://{all}/graphql":
        type: upstream
        upstream: "application-server:http"
    
  7. Add updated files to the git index:

    code language-bash
    git add -f .magento/routes.yaml application-server/.magento/*
    
  8. Commit your changes:

    code language-bash
    git commit -m "AppServer Enabled"
    
NOTE
Ensure that all custom settings in your root .magento.app.yaml file are appropriately migrated to the application-server/.magento/.magento.app.yaml file. After the application-server/.magento/.magento.app.yaml file is added to your project, you should maintain it in addition to the root .magento.app.yaml file. For example, if you need to configure rabbitmq or manage web properties you should add the same configuration to application-server/.magento/.magento.app.yaml as well.

Deploy Starter projects

After completing the enablement steps, push changes to your git repository to deploy GraphQL Application Server:

git push

Verify enablement on cloud projects

  1. Perform a GraphQL query or mutation against your instance to confirm that the graphql endpoint is accessible. For example:

    code language-none
    mutation {
     createEmptyCart
    }
    

    The expected response should resemble this example:

    code language-terminal
    {
     "data": {
         "createEmptyCart": "HLATPzcLw5ylDf76IC92nxdO2hXSXOrv"
         }
     }
    
  2. Use SSH to access your Cloud instance. The project_root/var/log/application-server.log should contain a new log record for every GraphQL request.

  3. You can also check if GraphQL Application Server is running by executing the following command:

    code language-bash
    ps aux|grep php
    

    You should see a bin/magento server:run process with multiple threads.

If these verification steps are successful, GraphQL Application Server is running and serving /graphql requests.

Enable on-premises projects

The ApplicationServer module (Magento/ApplicationServer/) enables GraphQL Application Server for GraphQL APIs.

Running GraphQL Application Server locally requires installation of the Swoole extension and a minor change to your deployment’s Nginx configuration file.

Prerequisites

Complete the following steps before enabling the ApplicationServer module:

  • Configure Nginx
  • Install and configure the Swoole v5+ extension

Configure Nginx

Your specific Commerce deployment determines how to configure Nginx. In general, the Nginx configuration file is by default named nginx.conf and is placed in one of these directories: /usr/local/nginx/conf, /etc/nginx, or /usr/local/etc/nginx. See Beginner’s Guide for more information on configuring Nginx.

Sample Nginx configuration:

location /graphql {
    proxy_set_header Host $http_host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

    proxy_pass http://127.0.0.1:9501/graphql;
}

Install and configure Swoole

To run the GraphQL Application Server locally, install the Swoole extension (v5.0 or higher). There are multiple ways to install this extension.

The following procedure describes how to install the Swoole extension for PHP 8.2 on OSX-based systems. It is one of several ways of installing the Swoole extension.

pecl install swoole

During installation, Adobe Commerce displays prompts to enable support for openssl, mysqlnd, sockets, http2, and postgres. Enter yes for all options except postgres.

Verify Swoole installation

Confirm that the extension has been successfully enabled:

php -m | grep swoole

Common errors with Swoole installation

Any errors that occur during Swoole installation typically occur during the pecl installation phase. Typical errors include missing openssl.h and pcre2.h files. To resolve these errors, ensure that these two packages are installed in your local system.

  • Check the location of openssl by running:
openssl version -d

This command shows the path where openssl is installed.

  • Check the location of pcre2 by running:
pcre2-config --prefix

Use Homebrew to install the missing packages if command output indicates that files are missing:

brew install openssl
brew install pcre2

Resolve issues with openssl

To resolve issues related to openssl, run:

export LDFLAGS="-L/opt/homebrew/etc/openssl@3/lib" export CPPFLAGS="-I/opt/homebrew/etc/openssl@3/include"

Confirm that you are using the path from your local dev environment.

You can run the following command again to check if openssl-related issues have been resolved:

pecl install swoole

Resolve issues with pcre2.h

To resolve issues related to pcre2.h, symlink the pcre2.h path to your installed PHP extension directory. Your specific installed version of PHP and pcr2.h determines the particular version of the command that you should use.

Run GraphQL Application Server

Start GraphQL Application Server:

bin/magento server:run

This command starts an HTTP port on 9501. Once GraphQL Application Server launches, port 9501 becomes an HTTP proxy server for all GraphQL queries.

To confirm that GraphQL Application Server is running in your deployment:

ps aux | grep php

Additional ways to confirm that GraphQL Application Server is running include:

  • Check the /var/log/application-server.log file for entries that are related to processed GraphQL requests.
  • Try to connect to the HTTP port that GraphQL Application Server runs on. For example: curl -g 'http://localhost:9501/graph.

Confirm that GraphQL requests are being processed

GraphQL Application Server adds the X-Backend response header with the value graphql_server to each request that it processes. To check if a request has been handled by GraphQL Application Server, check for this response header.

Confirm extension and customization compatibility

Extension developers and merchants should first verify that their extension and customization code adhere to the technical guidelines described in Technical guidelines.

Consider these guidelines during code evaluation:

  • Service classes (that is, classes that provide behavior but not data, such as EventManager) should not have mutable state.
  • Avoid temporal coupling.

Disable GraphQL Application Server

Procedures for disabling GraphQL Application Server vary depending upon whether the server is running in an on-premises or Cloud deployment.

Disable GraphQL Application Server (cloud)

  1. Remove any new files and any other code changes that were included in the AppServer Enabled commit during your preparations for deployment.

  2. Commit your changes using this command:

    code language-bash
    git commit -m "AppServer Disabled"
    
  3. Deploy these changes using this command:

    code language-bash
    git push
    

Disable GraphQL Application Server (on-premises)

  1. Comment out the /graphql section of nginx.conf file that you added when enabling GraphQL Application Server.
  2. Restart nginx.

This method of disabling GraphQL Application Server can be useful to quickly test or compare performance.

Confirm that GraphQL Application Server is disabled

To confirm that GraphQL requests are being processed by php-fpm instead of GraphQL Application Server, enter this command: ps aux | grep php.

After GraphQL Application Server has been disabled:

  • bin/magento server:run is inactive.
  • var/log/application-server.log contains no entries after GraphQL requests.

Integration and functional tests for GraphQL Application Server

Extension developers can run two integration tests to verify extension compatibility with GraphQL Application Server: GraphQlStateTest and ResetAfterRequestTest.

GraphQlStateTest

GraphQlStateTest detects state in shared objects that should not be reused for multiple requests.

This test is designed to detect state changes in service objects that are produced by the ObjectManager. The test executes identical GraphQL queries twice and compares service object state before and after the second query.

GraphQlStateTest failures and potential remediation

  • Cannot add, skip, or filter a list. If you encounter a failure that suggests it is not safe to add, skip, or filter a list, consider whether the class can be refactored in a backward-compatible way to use the factories of the service classes that have mutable state.

  • Class exhibits a mutable state. If the class itself exhibits a mutable state, try to rewrite your code to circumvent this state. If the mutable state is required for performance reasons, then implement ResetAfterRequestInterface and use _resetState() to reset the object to its initial constructed state.

  • Typed property $x must not be accessed before initialization message. Failures with this type of message suggest that the specified property has not been initialized by the constructor. This is a form of temporal coupling that occurs because the object cannot be used after it is initially constructed. This coupling occurs even if the property is private because the Collector that retrieves the data from the properties is using the PHP reflection feature. In this case, try refactoring the class to avoid temporal coupling and to avoid mutable state. If that refactoring does not resolve the failure, you can change the property type to a nullable type so it can be initialized to null.  If the property is an array, try initializing the property as an empty array.

Run GraphQlStateTest by executing vendor/bin/phpunit -c $(pwd)/dev/tests/integration/phpunit.xml dev/tests/integration/testsuite/Magento/GraphQl/App/GraphQlStateTest.php.

ResetAfterRequestTest

ResetAfterRequestTest looks for all classes that implement ResetAfterRequestInterface and verifies that the _resetState() method returns an object’s state to the same state it held after being constructed by ObjectManager.  This test creates a service object with ObjectManager, then clones that object, calls _resetState(), and then compares both objects. The test does not call any methods between object instantiation and _resetState(), so it does not confirm resetting any mutable state. It does find problems where a bug or typo in _resetState() may set the state to something different than what it was originally.

ResetAfterRequestTest failures and potential remediation

  • Class has inconsistent property values. If this test fails, check if a class has been changed with the result that the object after construction has different property values than it has after the _resetState() method is called. If the class that you are working on does not contain the _resetState() method itself, then check the class hierarchy for a superclass that implements it.

  • Typed property $x must not be accessed before initialization message. This issue also occurs with GraphQlStateTest.

    Run ResetAfterRequestTest by executing: vendor/bin/phpunit -c $(pwd)/dev/tests/integration/phpunit.xml dev/tests/integration/testsuite/Magento/Framework/ObjectManager/ResetAfterRequestTest.php.

Functional Testing

Extension developers should execute WebAPI functional tests for GraphQL, as well as any custom automated or manual functional tests for GraphQL, while deploying GraphQL Application Server. These functional tests help developers identify potential errors or compatibility issues.

State Monitor Mode

While running functional tests (or manual testing), the application server can run with --state-monitor mode enabled to help find classes where state is being unintentionally reused. Start the Application Server normally, except add the --state-monitor parameter.

bin/magento server:run --state-monitor

After each request is processed, a new file is added to the tmp directory, for example: var/tmp/StateMonitor-thread-output-50-6nmxiK. Once you are done testing, these files can be merged with the bin/magento server:state-monitor:aggregate-output command, which creates two merged files, one in XML and one in JSON.

Examples:

/var/workspace/var/tmp/StateMonitor-json-2024-04-10T18:50:39Z-hW0ucN.json
/var/workspace/var/tmp/StateMonitor-junit-2024-04-10T18:50:39Z-oreUco.xml

These files can be inspected with any tool you use to view XML or JSON, which will show the modified properties of service objects like GraphQlStateTest does. The --state-monitor mode uses the same skip list and filter list as GraphQlStateTest.

NOTE
Do not use the --state-monitor mode in production. It is only designed for development and testing. It creates many output files and will run slower than normally.
NOTE
--state-monitor is not compatible with PHP versions 8.3.0 - 8.3.4 due to a bug in the PHP garbage collector. If you are using PHP 8.3, you must upgrade to 8.3.5 or newer in order to use this feature.
recommendation-more-help
c0c5bbed-4957-4162-81bc-120c837a1894