braintree / braintree_ios

Braintree SDK for iOS
https://developer.paypal.com/braintree/docs/start/hello-client/ios/v5
MIT License
556 stars 290 forks source link

Analytics - only fetch config before flushing events cache #1337

Closed scannillo closed 2 weeks ago

scannillo commented 3 weeks ago

Background

Screenshot 2024-06-05 at 11 12 54 AM

This PR reduces the number of calls to BTAPIClient.fetchConfiguration() in our Analytics layer. I don't know if this will solve 100% of the issue, but it should help.

Changes

Checklist

Authors

@scannillo

scannillo commented 2 weeks ago

We could add a unit test like below, but I don't think it brings that much value. I think we should make a ticket for a holistic integration test for analytics, the batch POST, & the config GET. Open to folks opinions though.

func testSendAnalyticsEvent_doesNotMakeGETRequestForConfig() {
    let stubAPIClient: MockAPIClient = stubbedAPIClientWithAnalyticsURL("test://do-not-send.url")
    let analyticsService = BTAnalyticsService(apiClient: stubAPIClient)

    analyticsService.sendAnalyticsEvent("any.analytics.event1")

    XCTAssertFalse(stubAPIClient.fetchOrReturnRemoteConfigCalled)
}
KunJeongPark commented 2 weeks ago

We could add a unit test like below, but I don't think it brings that much value. I think we should make a ticket for a holistic integration test for analytics, the batch POST, & the config GET. Open to folks opinions though.

func testSendAnalyticsEvent_doesNotMakeGETRequestForConfig() {
    let stubAPIClient: MockAPIClient = stubbedAPIClientWithAnalyticsURL("test://do-not-send.url")
    let analyticsService = BTAnalyticsService(apiClient: stubAPIClient)

    analyticsService.sendAnalyticsEvent("any.analytics.event1")

    XCTAssertFalse(stubAPIClient.fetchOrReturnRemoteConfigCalled)
}

I think in some functions, we call sendAnalytics event before the fetch function. So if tokenize is called immediately after BTAPIClient instantiation, we would have prefetch call from BTAPIClient instantiation and possibly fetch call from analytics. Correct me if I am wrong. We definitely want to move the first sendAnalyticsEvent call in tokenize functions in the fetch call completion handler. I know that batched call would introduce a delay, but it's hard to predict timing of these events especially with cache expiration. And definitely really tough to write tests for different scenarios.

scannillo commented 2 weeks ago

I think in some functions, we call sendAnalytics event before the fetch function. So if tokenize is called immediately after BTAPIClient instantiation, we would have prefetch call from BTAPIClient instantiation and possibly fetch call from analytics. Correct me if I am wrong.

I'm not following this. This PR is really just a small change/fix. We never actually needed to fetch the config as early as we were before this PR (on each call to sendAnalyticsEvent). We only need the config value when we construct the batch of events to send to FPTI.

This PR now only retrieves the config (ideally it's cached by now) before we're ready to batch upload our queue of analytic events. This batch happens on a 20 second timer.

KunJeongPark commented 2 weeks ago

I think in some functions, we call sendAnalytics event before the fetch function. So if tokenize is called immediately after BTAPIClient instantiation, we would have prefetch call from BTAPIClient instantiation and possibly fetch call from analytics. Correct me if I am wrong.

I'm not following this. This PR is really just a small change/fix. We never actually needed to fetch the config as early as we were before this PR (on each call to sendAnalyticsEvent). We only need the config value when we construct the batch of events to send to FPTI.

This PR now only retrieves the config (ideally it's cached by now) before we're ready to batch upload our queue of analytic events. This batch happens on a 20 second timer.

Yes, I understand that. I was just trying to understand the timeline of whether we are trying to make these calls less frequent or injecting config value into BTAnalyticsService as we discussed as a possibility for scenario in this PR: https://github.com/braintree/braintree_ios/pull/1308

scannillo commented 2 weeks ago

Yes, I understand that. I was just trying to understand the timeline of whether we are trying to make these calls less frequent or injecting config value into BTAnalyticsService as we discussed as a possibility for scenario in this PR: https://github.com/braintree/braintree_ios/pull/1308

Like Rich called out, there is a problem with injecting it, since it could be expired.

This PR is a simple fix that could help things. It doesn't change any public APIs within the SDK.