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

Performance of useFlags hook severely degraded by Proxy implementation #177

Open
coreylight opened this issue Jan 5, 2023 · 4 comments
Open

Comments

@coreylight
Copy link

With the introduction of this change in 2.27.0

flags object (that is either injected via props using LDConsumer or returned from the useFlags hook) will generate a flag evaluation event on flag read (using a JavaScript proxy). This can be disabled by setting reactOptions.sendEventsOnFlagRead: false.

Our application was unknowingly heavily impacted at runtime.
We have hundreds of flags and have expected a "plain object" implementation found in previous versions that caused no issues.

Example code that was ok before:

const useMyFlag = (name) => {
  const allFlags = useFlags();
  const newFlagsWithOverrides = {
    ...allFlags,
    ...overrides
  }
  return newFlagsWithOverrides[name];
}

While this code may not apply to everyone, it's very simple JS code that no developer would expect to cause performance implications.

By introducing a Proxy that does work based on key access, performance plummeted for us.

I'm raising an issue mostly to warn other developers to make certain they know of this change. To that effect, a major version bump should (and still can) be considered to this library.
OR the flag evaluation proxy should be opt-in.

@yusinto
Copy link
Contributor

yusinto commented Jan 9, 2023

Thanks for reporting this. Are you able to share the number of flags involved when you start noticing the performance degradation please? If possible also please provide some metrics of the performance degradation in terms of rendering time for example comparing the performance before and after this issue.

@coreylight
Copy link
Author

@yusinto We had about 520+ flags at time of the issue.
I've created a sample application that shows off the issue.
You are welcome to replace the sleep function with any function that does anything. But you can see through traces that the work adds up quickly, making the UI very slow to render.

@tarqd
Copy link

tarqd commented Jan 23, 2023

Hey @coreylight,

I think that this implementation would improve performance and ensure that flag events work as expected:

const useMyFlag = (name, fallback) => {
  const ldClient = useLDClient();
  if (overrides.hasOwnProperty(name)) {
    return overrides[name]
  }
  return ldClient.variation(name, fallback)
}

This will avoid any deoptimization caused by the use of Proxy and superfluous object creation. My hunch is that a majority of the performance loss is from deoptimization rather than the call to variation itself.

Although I highly recommend using variation to ensure platform features that rely on events funciton correctly, you may be able to use allFlags as a workaround:

const useMyFlag = (name) => {
  const ldClient = useLDClient();
  // directly access the flag set without the proxy
  const allFlags = ldClient.allFlags()
  //..... etc
}

@coreylight
Copy link
Author

@tarqd Thanks for the suggestions. However, you're asking me to change my access patterns, which I've already done since we've experienced the pain of this change.
If LD doesn't want to make this opt-in, so be it. I've stated the regression that was caused by our team, using the methods that are outlined in the React Getting Started docs. If ldClient.variation is the most important method to use, I'd suggest the LD docs get updated to reflect that. And potentially adding a useFlagVariation hook for ease (if not already available).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants