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: add iOS background request keep-alive #68

Closed
wants to merge 1 commit into from
Closed

feat: add iOS background request keep-alive #68

wants to merge 1 commit into from

Conversation

tcldr
Copy link

@tcldr tcldr commented Aug 8, 2023

Summary

When an iOS app is backgrounded, any network requests which aren't specifically marked as background tasks will fail to complete. With the current implementation, this means that up to 30 seconds of events will be delayed until the next time the app is opened, or lost completely.

iOS does include an affordance to extend the time after an app loses foreground focus in which it can perform additional tasks, but these tasks need to be marked explicitly with calls to UIApplication.beginBackgroundTask and UIApplication.endBackgroundTask.

Unfortunately, this can't be achieved with an event callback alone as a background task should ideally be limited to around 5s, with a maximum deadline of 30s. This deadline is frequently exceeded with an event callback as the duration between when an event is tracked and when it is finally flushed through the queue frequently exceeds 30s and results in warnings from the system.

The PR works around this issue by calling the system's UIApplication.beginBackgroundTask and UIApplication.endBackgroundTask methods as part of the EventPipeline mechanism, indicating to the system that it should keep the app alive until the point that all pending network requests have completed. It also includes some changes to the setup of URLSession to increase the chances of a successful network request being made, and to avoid background tasks running over deadline.

An alternative way to achieve this might be to include an additional callback in the options that gets fired just prior to dispatch, but on balance this seems a more pragmatic approach that doesn't require the addition of any public API.

Checklist

  • Does your PR title have the correct title format?
  • Does your PR have a breaking change?: No

@justin-fiedler
Copy link
Collaborator

Thank you @tcldr for the PR. I will try to review it this week.

Copy link
Collaborator

@justin-fiedler justin-fiedler left a comment

Choose a reason for hiding this comment

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

Thanks @tcldr. See comments/questions.

Also maybe the beginBackgroundTask logic should be applied only for background flush() (invoked in exitForeground) not for all uploads. What do you think?

Sources/Amplitude/Utilities/HttpClient.swift Show resolved Hide resolved
Sources/Amplitude/Utilities/HttpClient.swift Show resolved Hide resolved
// add some throughput
sessionConfiguration.httpMaximumConnectionsPerHost = 2
// avoid keeping app background task alive for too long
sessionConfiguration.timeoutIntervalForRequest = 20
Copy link
Collaborator

Choose a reason for hiding this comment

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

This will limit all requests to 20s, vs default of 60s which could increase timeouts in low latency situations. I would prefer to leave this as the default.

Also should we use timeoutIntervalForResource be used instead of timeoutIntervalForRequest?

Copy link
Author

@tcldr tcldr Aug 17, 2023

Choose a reason for hiding this comment

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

timeoutIntervalForRequest? is the maximum duration a task should wait for additional data to arrive before giving up. In other words, it will cancel a partially uploaded task if it's been greater than the timeout interval since the last packet was sent. The timer is reset each time the task progresses.

the default timeoutIntervalForResource is 7 days, and is the maximum duration a task should take from initiation to completion. It's more relevant for long running downloads or uploads. I think the default is fine here.

In terms of whether it should be 20s or 60s, 20s has the benefit of being under the background task deadline time (30s) so helps avoids the app being penalised by the system for running for too long. Also, as flush() is automatically being called every 30s or so, any timeout task will soon be retried. This isn't a huge deal though – I'm happy with 60s if you're more comfortable with it.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Thanks for the detailed explanation. We would prefer to keep the default timeout.

@tcldr
Copy link
Author

tcldr commented Aug 17, 2023

Hi @justin-fiedler , left some comments, let me know your thoughts and I can update the PR. Cheers!

@tcldr
Copy link
Author

tcldr commented Aug 29, 2023

Superseded by #78.

@tcldr tcldr closed this Aug 29, 2023
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.

None yet

2 participants