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

feat: report prerequisite relations in AllFlagState #19

Merged
merged 7 commits into from
Oct 23, 2024

Conversation

cwaldren-ld
Copy link
Contributor

@cwaldren-ld cwaldren-ld commented Oct 22, 2024

This PR updates AllFlagsState to track prerequisite evaluations.

This didn't require modifying the Evaluator interface, as it already returns prerequisites as part of the EvalResult.

When the returned FlagState is marshaled to JSON, it will now contain the prerequisite info for each flag.

@@ -295,7 +332,10 @@ public override FeatureFlagsState Read(ref Utf8JsonReader reader, Type typeToCon
for (var flagsObj = RequireObject(ref reader); flagsObj.Next(ref reader);)
{
var subKey = flagsObj.Name;
var flag = flags.ContainsKey(subKey) ? flags[subKey] : new FlagState();
Copy link
Contributor Author

@cwaldren-ld cwaldren-ld Oct 22, 2024

Choose a reason for hiding this comment

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

Slightly awkward: have to new the prerequisite list here in the deserializer, and also on line 375. I'd rather just have this in the constructor so it's never a question, but looks like structs in our language version can't define constructors.

Another option is to make a null list be a possibility. Logically that'd be fine, but.. I'd rather the consumers never need to question if it's null or not. I could be convinced otherwise.

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with the constructor idea or something similar. Faced a similar issue in Java wrt to nullity, but luckily the array constructor with capacity 0 uses a static instance under the hood for performance.

Copy link
Member

Choose a reason for hiding this comment

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

Theoretically we should have struct constructors, but not parameter-less constructors. Ideally we would be creating a fully populated readonly struct instead of poking stuff into it. (containing an immutable list of prerequisites, or null)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

With the way we are deserializing JSON, we'd need to have some kind of builder pattern (or temp variables) to achieve that (creating the readonly struct in one go.)

Copy link
Member

Choose a reason for hiding this comment

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

If the temp variables are references, then they aren't really temps, the struct holds a reference. Though it maybe it problematic to adjust. It isn't at an assignment level though. Assign to a shared list and then assign to the populated list.

@@ -371,8 +372,7 @@ public FeatureFlagsState AllFlagsState(Context context, params FlagsStateOption[

var builder = new FeatureFlagsStateBuilder(options);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

These weren't used at all in this function. They are used internal to the FeatureFlagsStateBuilder, presumably they didn't get cleaned up when that was introduced.


var directPrerequisites = result.PrerequisiteEvals.Where(
e => e.FlagKey == flag.Key)
.Select(p => p.PrerequisiteFlag.Key).ToList();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Converting to a list here, rather than leaving as an IEnumerable, because the intent is that order is preserved.

@cwaldren-ld cwaldren-ld marked this pull request as ready for review October 22, 2024 22:02
@cwaldren-ld cwaldren-ld requested a review from a team as a code owner October 22, 2024 22:02
@@ -295,7 +332,10 @@ public override FeatureFlagsState Read(ref Utf8JsonReader reader, Type typeToCon
for (var flagsObj = RequireObject(ref reader); flagsObj.Next(ref reader);)
{
var subKey = flagsObj.Name;
var flag = flags.ContainsKey(subKey) ? flags[subKey] : new FlagState();
Copy link
Contributor

Choose a reason for hiding this comment

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

I agree with the constructor idea or something similar. Faced a similar issue in Java wrt to nullity, but luckily the array constructor with capacity 0 uses a static instance under the hood for performance.

};
_flags[flagKey] = flag;
return this;
}
}

internal struct FlagState
internal struct FlagState : IEquatable<FlagState>
Copy link
Member

Choose a reason for hiding this comment

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

Non-nullable prerequisites feel a bit strange to me.

Copy link
Contributor Author

@cwaldren-ld cwaldren-ld Oct 23, 2024

Choose a reason for hiding this comment

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

Fair. Is that from a "what a library consumer would expect from a iterate-able thing in C#" kind of way, or a "how structs work" way?

Copy link
Member

Choose a reason for hiding this comment

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

So, it feels strange in the language to me. We have null-state analysis, so we should be able to safely mark things null-able and use them. https://learn.microsoft.com/en-us/dotnet/csharp/nullable-references#null-state-analysis

From a struct perspective, ideally structs only contain other value types, but that isn't always possible. Next is for structs to only contain immutable reference types in addition to value types. Least favorable is this mix, but also not always avoidable. A struct with mostly not references, with value semantics, is better than a class here probably. But it doesn't behave like a struct. In that if you alias it and mutate the list, then both lists mutate.

Copy link
Member

Choose a reason for hiding this comment

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

If we make an immutable list, and then assign the entire list, then it at least makes it more toward the normal side. It could also be exposed as an IReadOnlyList<T>.

Copy link
Member

Choose a reason for hiding this comment

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

I don't think this is blocking though, as it isn't part of the external API.

@cwaldren-ld cwaldren-ld merged commit 43da95c into main Oct 23, 2024
8 checks passed
@cwaldren-ld cwaldren-ld deleted the cw/sdk-688-client-prereq-relations branch October 23, 2024 22:59
cwaldren-ld pushed a commit that referenced this pull request Oct 24, 2024
🤖 I have created a release *beep* *boop*
---


##
[8.6.0](LaunchDarkly.ServerSdk-v8.5.2...LaunchDarkly.ServerSdk-v8.6.0)
(2024-10-24)


### Features

* report prerequisite relations in AllFlagState
([#19](#19))
([43da95c](43da95c))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
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

Successfully merging this pull request may close these issues.

3 participants