-
Notifications
You must be signed in to change notification settings - Fork 31
feat(server): Add local evaluation #299
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
base: main
Are you sure you want to change the base?
Conversation
4c65dc3
to
320f16a
Compare
posthog-server/src/main/java/com/posthog/server/internal/FlagDefinitionModels.kt
Outdated
Show resolved
Hide resolved
salt: String = "", | ||
): Double { | ||
val hashKey = "$key.$distinctId$salt" | ||
val digest = MessageDigest.getInstance("SHA-1") |
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.
should this be a global var or do we need a fresh instance every 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.
getInstance
is a bit of a misnomer here - it's actually creating a new, stateful digest instance. It's possible we could use a single instance, but then would need to be synchronized. In this case, I don't think it's necessarily warranted.
posthog-server/src/main/java/com/posthog/server/internal/FlagEvaluator.kt
Outdated
Show resolved
Hide resolved
posthog-server/src/main/java/com/posthog/server/internal/FlagEvaluator.kt
Outdated
Show resolved
Hide resolved
posthog-server/src/main/java/com/posthog/server/internal/FlagEvaluator.kt
Outdated
Show resolved
Hide resolved
val parsedDate = | ||
try { | ||
parseDateValue(value.toString()) | ||
} catch (e: Exception) { |
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'd always use Throwable
since it catches more than just Exception
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 typically try to avoid catching Throwable
because it includes JVM exceptions that are generally unrecoverable like out of memory, thread death, JVM errors, etc.
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.
you do catch a Throwable when executing scheduleAtFixedRate
tho.
I usually prefer it because it's a lower level that captures many more errors, but of course, it depends on the throw signature of the methods you call.
I usually prefer to swallow SDK exceptions rather than let them bubble up, and if the caller does not handle the exception. It crashes the app
Sometimes you can get an OOM exception and its not even because the SDK is using a lot of memory, but it used the last bit, but people would still complain its the SDK doing the wrong thing :(
I will let you decide on this since its more server side and might be less of an issue
posthog-server/src/main/java/com/posthog/server/internal/FlagEvaluator.kt
Outdated
Show resolved
Hide resolved
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.
huge PR pheww... :D
i left a few comments, but i'd love to get eyes from the flags team as well, i just checked kotlin-ish code and if stuff made sense but i dont know the logic behind local eval
posthog-server/src/main/java/com/posthog/server/internal/PostHogFeatureFlags.kt
Show resolved
Hide resolved
Here's the current code coverage summary as of now
|
438981a
to
5d7a9b1
Compare
This cleans things up for future `onlyEvaluateLocally` config and sending feature flags on capture
5d7a9b1
to
8479f0f
Compare
Deserialization is handled here, paired closely with the Api. This simplifies deserialization in the server SDK.
This brings method coverage to 92% overall, line 85% overall.
It'll automatically clean up in case the developer forgets to call posthog.close()
It's not necessary considering the first request to trigger local evaluation will synchronously retrieve flag definitions if they're not yet loaded.
8479f0f
to
4e410f0
Compare
@IgnoreJRERequirement | ||
@PostHogInternal | ||
public data class FlagDefinition( | ||
val id: Int, | ||
val name: String, | ||
val key: String, | ||
val active: Boolean, | ||
val filters: FlagFilters, | ||
val version: Int, | ||
) |
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.
@marandaneto Is this okay to annotate with @IgnoreJRERequirement
to suppress these Animal Sniffer errors?
[Undefined reference | android-api-level-21-5.0.1_r2] com.posthog.internal.(PostHogLocalEvaluationModels.kt:1)
>> int Integer.hashCode(int)
[Undefined reference | android-api-level-21-5.0.1_r2] com.posthog.internal.(PostHogLocalEvaluationModels.kt:1)
>> int Double.hashCode(double)
According to gummy-bears docs, this is incorrectly flagged as being added in API 24 despite working in API 19
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.
nope not okay because of
posthog-android/posthog/build.gradle.kts
Lines 86 to 88 in 6adf18d
// we don't use these methods, so ignore them, they are only available on Android >= 24 | |
// [Undefined reference | android-api-level-21-5.0.1_r2] com.posthog.internal.(RREvent.kt:1) | |
// >> int Integer.hashCode(int) |
also here
// not a data class to avoid (int Integer.hashCode(int)) |
desugaring would work but desugaring is an opt in feature and its not guaranteed that its enabled in the hosting app
https://developer.android.com/studio/write/java8-support-table
💡 Motivation and Context
This PR adds local evaluation capabilities to feature flags. See the updated USAGE.md for details on how to use it.
If you're reviewing this PR, skimming
FlagEvaluator.kt
,LocalEvaluationPoller.kt
, and changes toPostHogFeatureFlags.kt
would be the best places to start.This change is pretty massive. I'm sure there's still a couple of bugs lurking, but test coverage is pretty good. This PR doesn't really change the public API, so we can always follow up with smaller bug fixes.
Resolves #307
💚 How did you test it?
Lots of unit testing and updating the Java sample app to use local eval
📝 Checklist