wp-graphql / wpgraphql-acf

Re-architecture of WPGraphQL for ACF
GNU General Public License v3.0
91 stars 13 forks source link

Slower Query Resolution with GraphQL Analyzer vs Previous Plugin #218

Closed rodrigo-arias closed 3 months ago

rodrigo-arias commented 5 months ago

Description

After migrating to the new plugin, I noticed that query resolution was slower than before. In a minimal reproduction scenario, the query time increased from 240 ms to 400 ms. However, in more complex scenarios involving dozens of fields and larger datasets, the uncached request times have increased from 1.2 seconds to 3.7 seconds, which is quite significant, and started overloading the server.

Steps to reproduce

Check the resolution time in the GraphiQL network tab.

WPGraphQL for Advanced Custom Fields (old):

query GET_TEST {
  post(id: "/test", idType: SLUG) {
    title
    personRelationship {
      toPerson {
        ... on Person {
          id
          title
        }
      }
    }
    pageRelationship {
      toPage {
        ... on Page {
          id
          title
        }
      }
    }
  }
}

WPGraphQL for ACF (new):

query GET_TEST {
  post(id: "/test", idType: SLUG) {
    title
    personRelationship {
      toPerson {
        nodes {
          ... on Person {
            id
            title
          }
        }
      }
    }
    pageRelationship {
      toPage {
        nodes {
          ... on Page {
            id
            title
          }
        }
      }
    }
  }
}

PHP or JSON export of the ACF Field Group(s)

acf-export-2024-06-19.json.zip

Additional context

CleanShot 2024-06-18 at 20 49 07 vs. CleanShot 2024-06-18 at 20 50 07

WPGraphQL Version

1.27.0

WPGraphQL For ACF Version

2.2.0

ACF (Advanced Custom Fields) Version. Free or Pro?

6.3.1.2 Pro

WordPress Version

6.5.4

PHP Version

8.2

Additional enviornment details

GraphQL Type Tracking enabled

Please confirm that you have searched existing issues in the repo.

Please confirm that you have disabled ALL plugins except for WPGraphQL, WPGraphQL For ACF, ACF, etc.

rodrigo-arias commented 5 months ago

After further testing, this issue appears to be related to GraphQL Type Tracking. When this option is disabled, the difference in performance is marginal. However, when enabled, the headers significantly slow down the queries in the new version of the plugin. You can see the difference even if no related items are returned.

WPGraphQL for Advanced Custom Fields 0.6.1

CleanShot 2024-06-19 at 21 49 12

https://github.com/wp-graphql/wpgraphql-acf/assets/11416255/1292a4ec-17af-47c0-bd27-93ea5231da5d

WPGraphQL for ACF 2.3.0

CleanShot 2024-06-19 at 21 48 51

https://github.com/wp-graphql/wpgraphql-acf/assets/11416255/fb0c2be9-c035-479c-926e-f90cf14ce85f

josephfusco commented 5 months ago

Hey @rodrigo-arias thanks for reporting this and for the thorough issue. This looks good we will try to follow your steps to reproduce and come up with next steps. 🙌🏼

rodrigo-arias commented 5 months ago

Thanks @josephfusco, if I can help with anything just let me know.

jasonbahl commented 5 months ago

@rodrigo-arias Thanks for the detailed issue.

I've created an environment with the ACF Field Group export that you have provided and have successfully reproduced, noticing a significant increase when using WPGraphQL v2.3.0 + GRAPHQL_DEBUG + X-GraphQL-Key Tracking enabled.

I'm not sure yet what the culprit is or but the fact that I can reproduce is promising in looking for a solution.

I expect there to be some overhead when using WPGraphQL for ACF, but not this much!

Thanks again for reporting!

WPGraphQL for Acf v0.6.1

Query, modified for v0.6.1 (toPerson and toPage fields are not connections)

query GET_TEST {
  post(id: "/test", idType: SLUG) {
    title
    personRelationship {
      toPerson {
        __typename
        ... on Post {
          id
          title
        }
      }
    }
    pageRelationship {
      toPage {
        ... on Page {
          id
          title
        }
      }
    }
  }
}

X-GraphQL-Key Tracking Disabled & WPGRAPHQL_DEBUG disabled:

X-GraphQL-Key Tracking enabled & WPGRAPHQL_DEBUG enabled:

(~50% increase)

WPGraphQL for ACF v2.3.0

Query, modified for v0.6.1 (toPerson and toPage fields ARE connections)

query GET_TEST {
  post(id: "/test", idType: SLUG) {
    title
    personRelationship {
      toPerson {
        nodes {
          __typename
          ... on Person {
            id
            title
          }
        }
      }
    }
    pageRelationship {
      toPage {
        nodes {
          ... on Page {
            id
            title
          }
        }
      }
    }
  }
}

X-GraphQL-Key Tracking Disabled & WPGRAPHQL_DEBUG disabled:

X-GraphQL-Key Tracking enabled & WPGRAPHQL_DEBUG enabled:

(>500% increase 😱)

rodrigo-arias commented 5 months ago

@jasonbahl It's good that you're seeing the same thing. Thanks for checking!

jasonbahl commented 5 months ago

However, in more complex scenarios involving dozens of fields and larger datasets, the uncached request times have increased from 1.2 seconds to 3.7 seconds

@rodrigo-arias can you share more information on this scenario? The more information I have to reproduce scenarios, the better understanding I can get around what issue(s) are occurring and how to address them.

jasonbahl commented 4 months ago

This PR might have a positive impact on this issue: https://github.com/wp-graphql/wp-graphql/pull/3172

rodrigo-arias commented 4 months ago

@jasonbahl Sorry for the late answer. This might be too much context, but you can determine what is relevant.

The websites I work on rely heavily on bidirectional relationship fields (using Post 2 Post for ACF and registered via PHP). Depending on the post type, queries can include dozens of these related posts, as well as their titles, categories, tags, other taxonomies, and custom fields.

A typical query structure includes:

We use WPGraphQL, WPGraphQL for ACF, WPGraphQL Tax Query, WPGraphQL Yoast SEO, WPGraphQL Gutenberg (planned to migrate to WPGraphQL Content Blocks), and WPGraphQL for Gravity Forms.

For caching, we use WPGraphQL Smart Cache and have Object Cache enabled. We retrieve the data using Apollo Client with APQ (GET requests) and InMemoryCache.

I have tried splitting the queries into two parts: one for common data and one for post data, hoping the cache would quickly resolve the first. Since it is not possible to use apollo-link-batch with AQP, I tried parallel queries with Promise.all(). However, a single larger query proved to be more efficient.

At the moment, I don't see a way to reduce the amount of data required or obtain it more optimally.

That said, the videos I shared did not include WPGraphQL Tax Query, WPGraphQL Yoast SEO, WPGraphQL Gutenberg, WPGraphQL for Gravity Forms, Apollo Client, or any frontend configuration, as they were made directly from GraphiQL. Post content was not included, and related posts were not returned because they were not assigned.

Let me know if any additional details would be helpful.

jasonbahl commented 4 months ago

In an effort to see how https://github.com/wp-graphql/wp-graphql/pull/3172 impacted this, I used k6 to run some performance tests.

The test simulates 10 concurrent users executing the same query over and over as many times as it can for 10 seconds.

K6 Test ```js import http from 'k6/http' import { check, sleep } from 'k6' export const options = { vus: 10, duration: '10s', }; export default function() { const url = 'http://uri-debugging.local/graphql' const query = ` query GET_TEST { post(id: "/test", idType: SLUG) { title personRelationship { toPerson { nodes { ... on Person { id title } } } } pageRelationship { toPage { nodes { ... on Page { id title } } } } } } `; // format the query as a query param and append to the url // const res = http.get(`${url}?query=${encodeURIComponent(query)}`) const headers = { 'Content-Type': 'application/json' } const res = http.post(url, JSON.stringify({ query: query }), { headers: headers }) sleep(1) check(res, { 'status was 200': r => r.status === 200, 'transaction time OK (less than 200ms)': r => r.timings.duration < 200 }); } ```

With the following ACF Field Group imported (acf-export-2024-06-19.json.zip), and the following plugins active:

http_req_duration..............: avg=134.79ms min=69.5ms med=81.82ms max=1.05s  p(90)=234.55ms p(95)=402.75ms

Then if I run the same tests with the code from https://github.com/wp-graphql/wp-graphql/pull/3172 in place:

http_req_duration..............: avg=97.83ms  min=69.87ms med=79.25ms max=415.55ms p(90)=109.16ms p(95)=235.5ms

It appears that there are some improvements due to the work in https://github.com/wp-graphql/wp-graphql/pull/3172

jasonbahl commented 4 months ago

@rodrigo-arias if you have a chance to test your environment agains the changes in https://github.com/wp-graphql/wp-graphql/pull/3172 and report back, that would be 👌

rodrigo-arias commented 3 months ago

@jasonbahl This works way better. In my limited testing scenario, the resolution time has dropped and is now similar to WPGraphQL for ACF v0.6.1. If you don't encounter any drawbacks, this looks like a great performance fix. Thank you!

WPGraphQL v1.27.2 + WPGraphQL for ACF v2.4.1

CleanShot 2024-08-07 at 21 42 01

WPGraphQL PR#3172 + WPGraphQL for ACF v2.4.1

CleanShot 2024-08-07 at 21 51 52

jasonbahl commented 3 months ago

@rodrigo-arias I'm going to close this now that WPGraphQL v1.28.0 has been released and you confirmed significant improvements.

I'm sure there's more we can do to further improve performance of core WPGraphQL and specific extensions like this. If we're able to identify specific actionable items I would like to see that opened as isolated issues we can chip away at.

rodrigo-arias commented 3 months ago

@jasonbahl That sounds good. I'll report back if I find any noticeable performance issues. Thanks for your help and for continuing to push the community forward.