Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Significant memory spike in plugin with each() #2038

Closed
cjheppell-ravio opened this issue May 1, 2024 · 3 comments
Closed

Significant memory spike in plugin with each() #2038

cjheppell-ravio opened this issue May 1, 2024 · 3 comments
Labels

Comments

@cjheppell-ravio
Copy link

Summary

Javascript heap memory usage jumps significantly when using a plugin with loadOne, . This occurs for relatively "small" rows. ~100 rows will cause a jump from ~40mb on startup to ~250mb. Larger numbers cause higher memory usage.

For our usage of Postgraphile V5 in production, this is causing our server to crash due to out of memory errors.

Steps to reproduce

See this repository for a full reproduction:

https://github.com/cjheppell/grafast-memory-spike-repro/tree/main

Expected results

Memory usage does not jump significantly.

Actual results

Memory usage jumps significantly.

Additional context

Postgres 15, Node v20, MacOS M1 32GB, latest Postgraphile v5.

Schema, data and code is available in the reproduction repo above.

Possible Solution

Unknown. I'm too unfamiliar with the internals of Grafast/Postgraphile to suggest how this might be resolved.

@cjheppell-ravio
Copy link
Author

Update: This appears to not be related to applyTransform or loadOne at all. There's a simpler repro here with just each: https://github.com/scottravio/grafast-memory-spike-repro

@benjie benjie changed the title Significant memory spike in plugin with loadOne Significant memory spike in plugin with each() May 1, 2024
@benjie
Copy link
Member

benjie commented May 1, 2024

This is due to the each() creating a subroutine (since it's not used as the return result of a plan resolver, but instead as input to another step), and subroutines are currently not hoisted (see #2041); as such we're having to run the each transform with a batch size of 1000 (because you're fetching 1000 users, and this is a field in the User type), and for each of those 1,000 user we're then mapping over the 1000 languages ultimately resulting in 1000*1000 = 1,000,000 values.

Since your step here is not dependent on $user, theoretically we should be able to hoist it (see #2041) but we currently do not. Instead, try and ensure that the steps that you use can all be hoisted (see the plan diagram to see which bucket the steps exist in); for example:

    plans: {
      User: {
        totalLanguages() {
          const $allLangugages = withPgClient(
            executor,
            constant(null),
            async (pgClient) => {
              const { rows } = await pgClient.query({
                text: `select language from languages`,
              });
              return rows;
            },
          );
          $allLangugages.hasSideEffects = false;
          return lambda(
            $allLangugages,
            (allLangugages) => allLangugages.length,
          );
        },
      },
    },

However, it would be better to directly address your need, which in this case seems to be counting the number of languages on a per-user basis, so perhaps something like:

    plans: {
      User: {
        totalLanguages($user) {
          const $agg = user_config.find({ user_id: $user.get("id") }).clone("aggregate");
          return $agg.single().select(sql`count(*)`, TYPES.bigint);
        },
      },
    },

Note that .clone is currently marked as an internal API, but that is resolved in #2039.

@benjie
Copy link
Member

benjie commented May 1, 2024

Closing this issue in favour of the related issues #2039, #2040 and #2041.

@benjie benjie closed this as completed May 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Archived in project
Development

No branches or pull requests

2 participants