-
Notifications
You must be signed in to change notification settings - Fork 1
feat: Add fractional evaluation for segments #45
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
Changes from 18 commits
e0a08c9
549f5fb
785e444
9aef084
ceb14bc
9133f77
c241eba
4dc8e58
99c165a
51f91f7
4cb0733
8bcc64f
aa94272
ed05e0c
40c5663
a47b127
614e38f
964a3cf
2f1482d
842a037
a1d0119
ca67a04
27dfc2a
7faef02
f49164e
2f247b8
365fa0c
c5a3be5
0eb73d4
48b98de
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
liamhughes marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| namespace System.Runtime.CompilerServices; | ||
|
|
||
| internal static class IsExternalInit; | ||
liamhughes marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,8 @@ | ||
| using System.Text.RegularExpressions; | ||
| using System.Buffers.Binary; | ||
| using System.Text; | ||
| using System.Text.RegularExpressions; | ||
| using Microsoft.Extensions.Logging; | ||
| using Murmur; | ||
| using OpenFeature.Constant; | ||
| using OpenFeature.Model; | ||
|
|
||
|
|
@@ -41,6 +44,12 @@ | |
| "The slug provided did not match any of your Octopus Feature Toggles. Please double check your slug and try again."); | ||
| } | ||
|
|
||
| if (MissingRequiredPropertiesForClientSideEvaluation(feature)) | ||
| { | ||
| return new ResolutionDetails<bool>(slug, defaultValue, ErrorType.ParseError, | ||
| $"Feature toggle {slug} is missing necessary information for client-side evaluation."); | ||
| } | ||
|
|
||
| return new ResolutionDetails<bool>(slug, Evaluate(feature, context)); | ||
| } | ||
|
|
||
|
|
@@ -63,7 +72,52 @@ | |
|
|
||
| bool Evaluate(FeatureToggleEvaluation evaluation, EvaluationContext? context = null) | ||
| { | ||
| return evaluation.IsEnabled && | ||
| (evaluation.Segments.Length == 0 || MatchesSegment(context, evaluation.Segments)); | ||
| if (!evaluation.IsEnabled) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| var targetingKey = context?.TargetingKey; | ||
| if (targetingKey == null || targetingKey == "") | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I swapped that out for this as I was getting a null reference warning on |
||
| { | ||
| if (evaluation.ClientRolloutPercentage < 100) | ||
| { | ||
| return false; | ||
| } | ||
| } | ||
| else | ||
| { | ||
| if (GetNormalizedNumber(evaluation.EvaluationKey, targetingKey) > evaluation.ClientRolloutPercentage) | ||
|
Check warning on line 90 in src/Octopus.OpenFeature.Provider/OctopusFeatureContext.cs
|
||
| { | ||
| return false; // return false if hash number is larger than rollout percentage | ||
| } | ||
| } | ||
dylanlerch marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I considered placing the rollout percentage evaluation logic below the call to MatchesSegment, but I thought that MatchesSegment might actually be the most time intensive of the two 👇
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We may end up switching this around once we add reasons. For example, we need the reason that it does (or doesn't) get a toggle to be consistent between all libraries. For now we don't see this, so it's fine. |
||
| return evaluation.Segments.Length == 0 || MatchesSegment(context, evaluation.Segments); | ||
|
Check warning on line 96 in src/Octopus.OpenFeature.Provider/OctopusFeatureContext.cs
|
||
| } | ||
|
|
||
| /// <summary> | ||
| /// Computes a deterministic integer bucket in the inclusive range 1–100 for the given evaluation and targeting keys. | ||
| /// </summary> | ||
| private int GetNormalizedNumber(string evaluationKey, string targetingKey) | ||
| { | ||
| var bytes = Encoding.UTF8.GetBytes(string.Concat(evaluationKey, ":", targetingKey)); | ||
|
|
||
| using var algorithm = MurmurHash.Create32(); | ||
| var hash = algorithm.ComputeHash(bytes); | ||
|
Comment on lines
+111
to
+112
|
||
| // Explicitly little-endian to ensure consistent int values across all client libraries. | ||
| var value = BinaryPrimitives.ReadUInt32LittleEndian(hash); | ||
| return (int)(value % 100 + 1); | ||
| } | ||
|
|
||
|
|
||
| private static bool MissingRequiredPropertiesForClientSideEvaluation(FeatureToggleEvaluation evaluation) | ||
| { | ||
| if (!evaluation.IsEnabled) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| return evaluation.ClientRolloutPercentage is null || evaluation.EvaluationKey is null || evaluation.Segments is null; | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was having trouble running the tests locally, but that may have been because Caitlyn and I were editing the submodule files at the same time?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fine with me 👍