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

fix(3741): refactor remote-feature-flag controller with generateDeterministicRandomNumber and getMetaMetricsId #5097

Closed
wants to merge 3 commits into from

Conversation

DDDDDanica
Copy link
Contributor

@DDDDDanica DDDDDanica commented Dec 24, 2024

Explanation

Changes introduced in this PR including:

  • remove fallback of metaMetricsId and rely on client side always returning a value
  • change logic of generateDeterministicRandomNumber to handle both mobile(uuidv4) and extension(hex) side
  • refactor getMetaMetricsId to only handle synchronous (extension) function

References

Address feedback about

Changelog

@metamask/remote-feature-flag-controller

  • ADDED: Expand signature of getMetaMetricsId to handle both synchronous function
  • CHANGED: Modify generateDeterministicRandomNumber to handle both mobile(uuidv4) and extension(hex) side
  • CHANGED: Remove fallback of metaMetricsId

Checklist

  • I've updated the test suite for new or updated code as appropriate
  • I've updated documentation (JSDoc, Markdown, etc.) for new or updated code as appropriate
  • I've highlighted breaking changes using the "BREAKING" category above as appropriate
  • I've prepared draft pull requests for clients and consumer packages to resolve any breaking changes

@DDDDDanica DDDDDanica self-assigned this Dec 24, 2024
@DDDDDanica DDDDDanica force-pushed the fix/3742-threshold branch 3 times, most recently from 41f11dc to 24571f9 Compare December 24, 2024 19:54
@DDDDDanica DDDDDanica marked this pull request as ready for review January 6, 2025 14:57
Comment on lines 13 to 14
MOBILE_MIN: '00000000-0000-0000-0000-000000000000',
MOBILE_MAX: 'ffffffff-ffff-ffff-ffff-ffffffffffff',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
MOBILE_MIN: '00000000-0000-0000-0000-000000000000',
MOBILE_MAX: 'ffffffff-ffff-ffff-ffff-ffffffffffff',
MOBILE_MIN: '00000000-0000-4000-0000-000000000000',
MOBILE_MAX: 'ffffffff-ffff-4fff-ffff-ffffffffffff',

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch ! resolved: 035745f

Comment on lines 211 to 221
const metaMetricsIdResult = this.#getMetaMetricsId();
const metaMetricsId =
(await this.#getMetaMetricsId) || generateFallbackMetaMetricsId();
const thresholdValue = generateDeterministicRandomNumber(metaMetricsId);
metaMetricsIdResult instanceof Promise
? await metaMetricsIdResult
: metaMetricsIdResult;

const clientType = this.#clientConfigApiService.getClient();
const thresholdValue = generateDeterministicRandomNumber(
clientType,
metaMetricsId,
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: i don't really understand what you did here.
Can you elaborate more on this part please? Is it only to force the return type not to be optional or undefined as the MetaMetrics getMetaMetricsId always return a string even if the signature says it can return undefined?
What would you think about modifying the signature of MetaMetrics.getMetaMetricsId to drop undefined?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes definitely! We no longer handle undefined values - the signature doesn't actually include undefined. It's about handling both synchronous and asynchronous returns in a type-safe way.
About modifying MetaMetrics.getMetaMetricsId - it is defined in client side while mobile is async and extension is sync, and we will rely on client side to always give a value back 🙏🏻

@@ -59,7 +59,7 @@ function createController(
state: Partial<RemoteFeatureFlagControllerState>;
clientConfigApiService: AbstractClientConfigApiService;
disabled: boolean;
getMetaMetricsId: Promise<string | undefined>;
getMetaMetricsId: () => Promise<string> | string;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note that this function is synchronous on both extension and mobile. It's typed as asynchronous on mobile by mistake, but it's not.

Copy link
Contributor Author

@DDDDDanica DDDDDanica Jan 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh that's good to know.

@joaoloureirop could you make adjustments in your side of implementation when initializing the controller to adapt to synchronous pattern?

changes can be found: 15428da

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Mark & Danica. I'll update mobile PR

* Gets the client type configured for this service.
* @returns The configured ClientType
*/
public getClient(): ClientType {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this method for?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this one is to get the client type we passed in the service configuration. Then when we can decide which algorithm we use inside generateDeterministicRandomNumber

const chr = char.charCodeAt(0);
return ((acc << BITS_PER_CHAR) - acc + chr) & maxValue;
}, 0);
return hash / maxValue;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "max value" here should be the maximum possible hash value, not the maximum possible integer size. If the max here doesn't match the maximum hash size, then our distribution will be skewed.

Copy link
Member

@Gudahtt Gudahtt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's really important that we guarantee a uniform distribution, so that a 1% rollout actually results in 1% of users having a feature enabled (rather than 10% or 99% or 0.001%). I'm requesting changes mainly because this doesn't yet seem guaranteed.

Perhaps we can split this PR in two though? The getMetraMetricsId related changes are totally distinct, and much simpler.

@DDDDDanica
Copy link
Contributor Author

@DDDDDanica DDDDDanica closed this Jan 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

4 participants