8000 feat(flags): adding support for GeoIP and person property overrides to rust flag service by dmarticus · Pull Request #24536 · PostHog/posthog · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

feat(flags): adding support for GeoIP and person property overrides to rust flag service #24536

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

Merged
merged 36 commits into from
Aug 27, 2024

Conversation

dmarticus
Copy link
Contributor
@dmarticus dmarticus commented Aug 22, 2024

Changes

See this TODO list for features I'm burning down

  • support for MaxMind GeoIP lookup
  • refactored the flag_request, request_handler, and v0_endpoints a bit to separate concerns and make testing easier
  • support for person property overrides when matching flag
  • tests for all new methods and a few old ones

Does this work well for both Cloud and self-hosted?

No impact yet, no one is using this service.

How did you test this code?

Manual tests demonstrated here: https://posthog.slack.com/archives/C034XD440RK/p1724364084555299

wrote a bunch of new unit tests, too.

@dmarticus dmarticus marked this pull request as ready for review August 22, 2024 23:03
@dmarticus dmarticus requested a review from a team August 22, 2024 23:05
@dmarticus dmarticus requested a review from neilkakkar August 27, 2024 00:45
Copy link
Contributor
@Phanatic Phanatic left a comment

Choose a reason for hiding this comment

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

A few questions for my own edification, this looks great!

@@ -165,6 +165,12 @@ jobs:
DATABASE_URL: 'postgres://posthog:posthog@localhost:5432/posthog'
run: cd ../ && python manage.py setup_test_environment --only-postgres

- name: Download MaxMind Database
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there versioning for this database? I dunno how the database gets into our self-hosted mmdbcdn.posthog.net URL

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

yes its updated weekly, we use latest, https://github.com/PostHog/http-mmdb

assert_eq!(redis_flag.deleted, pg_flag.deleted);
assert_eq!(redis_flag.key, pg_flag.key, "Flag key mismatch");
assert_eq!(
redis_flag.name, pg_flag.name,
Copy link
Contributor

Choose a reason for hiding this comment

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

👍🏾 Good call on adding more descriptive assert messages here.

if let Some(person_overrides) = &self.person_property_overrides {
// Check if all required properties are present in the overrides
// and none of them are of type "cohort"
let should_prefer_overrides = properties
Copy link
Contributor

Choose a reason for hiding this comment

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

Interesting, to confirm, this checks that all the person properties from the request payload match the spec for a feature flag and short-circuits the database call?

Can we add a metric here? I'm curious how many requests submit all properties needed to evaluate a feature flag.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

metrics are coming in a later PR, but I'll add a TODO to add a counter here, it's a good place for it.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Interesting, to confirm, this checks that all the person properties from the request payload match the spec for a feature flag and short-circuits the database call?

Yes, that's how the functionality works.

Copy link
Contributor

Choose a reason for hiding this comment

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

We automatically do this for any call from the SDKs where props have been added. It's also the only way to subvert the delay with ingesting properties problem.

Copy link
Contributor

Choose a reason for hiding this comment

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

also note that since conditions are OR'ed, as long as even one property is sent where the condition matches, you can short circuit flag evaluation without needing all properties.

@@ -243,6 +299,15 @@ impl FeatureFlagMatcher {

Ok(props)
}

// async fn get_group_properties_from_cache_or_db(
Copy link
Contributor

Choose a reason for hiding this comment

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

I would probably stash these plan for group_properties locally and remove them from the PR.

Copy link
Contributor Author
@dmarticus dmarticus Aug 27, 2024

Choose a reason for hiding this comment

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

yeah that PR is ready now too, so I don't think it's worth doing since it'll be a fast follow

async fn del(&self, k: String) -> Result<(), CustomRedisError> {
let mut conn = self.client.get_async_connection().await?;

let results = conn.del(k);
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 not very familiar with Rust's redis library, do we need to check here if the key exists or does this client library do that by itself?

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 wraps Redis' API, in which DEL is noop if there's no key.

info!("Attempting to open GeoIP database at: {:?}", geoip_path);

let reader = Reader::open_readfile(&geoip_path)?;
info!("Successfully opened GeoIP database");
Copy link
Contributor

Choose a reason for hiding this comment

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

I can't tell from the calling code, and that might be my own ignorance showing 😄

Do we read this database on every call to the GeoIPService? and do we need to close the reader somewhere or does the caller of this service close the handle to the geoIp file somewhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we create this connection once, on startup. See here:

#[derive(Clone)]
pub struct State {
    // TODO add writers when ready
    pub redis: Arc<dyn RedisClient + Send + Sync>,
    pub postgres: Arc<dyn DatabaseClient + Send + Sync>,
    pub geoip: Arc<GeoIpService>,
}

pub fn router<R, D>(redis: Arc<R>, postgres: Arc<D>, geoip: Arc<GeoIpService>) -> Router
where
    R: RedisClient + Send + Sync + 'static,
    D: DatabaseClient + Send + Sync + 'static,
{
    let state = State {
        redis,
        postgres,
        geoip,
    };

    Router::new()
        .route("/flags", post(v0_endpoint::flags).get(v0_endpoint::flags))
        .with_state(state)
}

(from router.rs)

Every subsequent call of get_geoip_properties hits that same in-memory database.

Copy link
Contributor Author
@dmarticus dmarticus Aug 27, 2024

Choose a reason for hiding this comment

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

(nit: maybe I should call it a GeoIpClient instead of a GeoIpService)

@dmarticus dmarticus enabled auto-merge (squash) August 27, 2024 19:55
@dmarticus dmarticus merged commit 8dde90a into master Aug 27, 2024
84 of 85 checks passed
@dmarticus dmarticus deleted the feat/add-geoip-targeting branch August 27, 2024 19:58
pauldambra pushed a commit that referenced this pull request Aug 29, 2024
…o rust flag service (#24536)

Co-authored-by: Phani Raj <phani@posthog.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
0