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

Local Feature Flag Evaluation #6

Merged
merged 25 commits into from
Aug 17, 2022
Merged

Local Feature Flag Evaluation #6

merged 25 commits into from
Aug 17, 2022

Conversation

EDsCODE
Copy link
Member

@EDsCODE EDsCODE commented Jul 28, 2022

posthog.go Outdated Show resolved Hide resolved
feature_flags_test.go Outdated Show resolved Hide resolved
@EDsCODE EDsCODE requested a review from neilkakkar August 8, 2022 14:31

var i float64
switch t := val.(type) {
case int:
Copy link
Contributor

Choose a reason for hiding this comment

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

just wondering is there any reason we can't consolidate these case statements since we're doing float64(t) to all of them?

Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure if this is different in go, but if different cases require the same code, you can put them together like so:

case int:
case int8:
case int16:
    i = float64(t)
    return i, nil

featureflags.go Show resolved Hide resolved
@EDsCODE
Copy link
Member Author

EDsCODE commented Aug 10, 2022

TODO:

  • default value tests
  • feature_flag_called instrumentation and testing
  • memory tests + sizelimiteddict
  • Make sure current tests have max coverage of options that match spec
  • onlyevaluatelocally and sendfeatureflag arguments
  • warn/debug logging
  • inactive flag test
  • personalapikey loading test/wrong key test

@EDsCODE EDsCODE changed the title WIP: Turbo mode Local Feature Flag Evaluation Aug 11, 2022
@EDsCODE EDsCODE marked this pull request as ready for review August 11, 2022 20:50
Copy link
Member Author

@EDsCODE EDsCODE left a comment

Choose a reason for hiding this comment

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

RE: error handling levels. I'm not a go expert but given how it's standard to emit errors back to the top to be handled, I've added a few error logs where it's implicitly handled but otherwise I send the error up the chain so that the featureflag functions will return them


}

func TestCaptureMultipleUsersDoesntOutOfMemory(t *testing.T) {
Copy link
Member Author

Choose a reason for hiding this comment

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

I'm unsure how to test this

Copy link
Contributor

Choose a reason for hiding this comment

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

Not aware of the intricacies here, but the test is testing that the size of the hash doesn't blow up arbitrarily with new users coming in. (As for long running server-side apps, there's a risk of this running out of memory).

In node, I just check that the distinctIdHasSentFeatureFlagCalls array never exceeds MAX_SIZE, when 100*MAX_SIZE new users are seen:

https://github.com/PostHog/posthog-js-lite/pull/14/files#diff-f8fbefb57e174bbb58200dfeab663d6b65aa93b671b7847f4f48a17bfdf5dd54R272

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm testing the sizelimitedmap separately as a unit test. Doesn't seem feasible to test as an integration test since there's no way to expose the map without making it part of the public interface on client which isn't good practice as it's unnecessary to the user

Copy link
Contributor

Choose a reason for hiding this comment

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

Works 👍

@@ -245,21 +259,28 @@ func (c *client) ReloadFeatureFlags() error {
return nil
}

func (c *client) GetFeatureFlag(flagKey string, distinctId string, defaultValue interface{}) (interface{}, error) {
func (c *client) GetFeatureFlag(flagConfig FeatureFlagPayload) (interface{}, error) {
if err := flagConfig.validate(); err != nil {
Copy link
Member Author

Choose a reason for hiding this comment

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

Major change is that we accept a featureflag payload and validate it on invocation. This way users don't need to fill in every argument everytime

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm fine either way, but in node the way I structured this was to have required parameters outside the payload, and everything else inside. So, it looks something like:

// getFeatureFlag(
//    key: string,
//    distinctId: string,
//    options?: {
//      groups?: Record<string, string>
//      personProperties?: Record<string, string>
//      groupProperties?: Record<string, Record<string, string>>
//      onlyEvaluateLocally?: boolean
//      sendFeatureFlagEvents?: boolean
//    }
//  ): Promise<string | boolean | undefined>

It's nice because people who don't care about local eval / overriding server properties don't have to worry about payload keys.

Upto you though, your library, your call 😂

feature_flags_test.go Outdated Show resolved Hide resolved
featureflags.go Outdated Show resolved Hide resolved
featureflags.go Outdated Show resolved Hide resolved
i = float64(t)
case uint64:
i = float64(t)
default:
Copy link
Contributor

Choose a reason for hiding this comment

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

just checking, not sure about go syntax: This doesn't always go to default right? (it would in other langs, because there's no break/return in the cases above)

Copy link
Member Author

Choose a reason for hiding this comment

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

nope!

posthog.go Outdated
@@ -37,17 +38,20 @@ type Client interface {
Enqueue(Message) error
//
// Method returns if a feature flag is on for a given user based on their distinct ID
IsFeatureEnabled(string, string, bool) (bool, error)
IsFeatureEnabled(FeatureFlagPayload) (bool, error)
Copy link
Contributor

Choose a reason for hiding this comment

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

remember to mention this breaking change in the changelog :)

posthog.go Show resolved Hide resolved
posthog.go Show resolved Hide resolved
posthog.go Outdated Show resolved Hide resolved
@EDsCODE EDsCODE merged commit 4286894 into master Aug 17, 2022
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