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

[Improve] Handle HTTP header Retry-After from responses from OneSignal #2064

Merged
merged 5 commits into from
May 1, 2024

Conversation

jkasten2
Copy link
Member

@jkasten2 jkasten2 commented Apr 25, 2024

Description

One Line Summary

Respect standard HTTP Retry-After header from any responses from OneSignal's backend.

Details

Motivation

When OneSignal's backend is under heavy load they want to be able to throttle back requests to them.

Scope

Only effects delaying follow up network requests after we receive Retry-After in a response. If OpeartionRepo triggers this then it handles this smartly by delaying future requests, so more batching can be done. Other code that depends on the HttpClient simply delays requests to respect this header.

Testing

Unit testing

Added and updated a number of Unit tests. OperationRepo, it's executors, and HttpClientTests.

Manual testing

Tested on Android 14 emulator. Ensured we correctly handled 429 with it's Retry-After by doing the following.

  1. In a browser on my machine refreshed /users fetch over and over until I got a 429
  2. Opened the app on my emulator (using the same internet connection)
  3. Check the logcat to ensure the delay was respected.
2024-04-24 22:14:24.572  4583-4616  OneSignal               com.onesignal.sdktest                D  [DefaultDispatcher-worker-1] HttpClient: GET apps/77e32082-ea27-42e3-a898-c72e141824ef/users/by/onesignal_id/102c7c89-54f8-49fb-96cb-f55420f1b789
2024-04-24 22:14:24.603  4583-4616  OneSignal               com.onesignal.sdktest                D  [DefaultDispatcher-worker-1] HttpClient: Response Retry-After: 44
2024-04-24 22:14:24.605  4583-4616  OneSignal               com.onesignal.sdktest                D  [DefaultDispatcher-worker-1] HttpClient: GET apps/77e32082-ea27-42e3-a898-c72e141824ef/users/by/onesignal_id/102c7c89-54f8-49fb-96cb-f55420f1b789 - FAILED STATUS: 429
2024-04-24 22:14:24.612  4583-4616  OneSignal               com.onesignal.sdktest                W  [DefaultDispatcher-worker-1] HttpClient: null RECEIVED JSON: {"errors":["API rate limit exceeded"],"limit":"API Per App"}



2024-04-24 22:15:08.839  4583-4618  OneSignal               com.onesignal.sdktest                D  [DefaultDispatcher-worker-3] HttpClient: GET apps/77e32082-ea27-42e3-a898-c72e141824ef/users/by/onesignal_id/102c7c89-54f8-49fb-96cb-f55420f1b789
2024-04-24 22:15:09.010  4583-4618  OneSignal               com.onesignal.sdktest                D  [DefaultDispatcher-worker-3] HttpClient: GET apps/77e32082-ea27-42e3-a898-c72e141824ef/users/by/onesignal_id/102c7c89-54f8-49fb-96cb-f55420f1b789 - STATUS: 200 JSON: 

Affected code checklist

  • Notifications
    • Display
    • Open
    • Push Processing
    • Confirm Deliveries
  • Outcomes
  • Sessions
  • In-App Messaging
  • REST API requests
  • Public API changes

Checklist

Overview

  • I have filled out all REQUIRED sections above
  • PR does one thing
    • If it is hard to explain how any codes changes are related to each other then it most likely needs to be more than one PR
  • Any Public API changes are explained in the PR details and conform to existing APIs

Testing

  • I have included test coverage for these changes, or explained why they are not needed
  • All automated tests pass, or I explained why that is not possible
  • I have personally tested this on my device, or explained why that is not possible

Final pass

  • Code is as readable as possible.
    • Simplify with less code, followed by splitting up code into well named functions and variables, followed by adding comments to the code.
  • I have reviewed this PR myself, ensuring it meets each checklist item
    • WIP (Work In Progress) is ok, but explain what is still in progress and what you would like feedback on. Start the PR title with "WIP" to indicate this.

This change is Reviewable

@jkasten2 jkasten2 force-pushed the improve/handle-http-response-header-retry-after branch 2 times, most recently from f38d522 to 533d321 Compare April 25, 2024 02:12
@jkasten2 jkasten2 changed the title [WIP] [Improve] Handle http response header retry after [Improve] Handle http response header retry after Apr 25, 2024
@jkasten2 jkasten2 changed the title [Improve] Handle http response header retry after [Improve] Handle HTTP header Retry-After from responses from OneSignal Apr 25, 2024
Copy link
Contributor

@nan-li nan-li left a comment

Choose a reason for hiding this comment

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

I had a hard time keeping in my head for all the variables what are in milliseconds and what are in seconds.
Edit - yup, removed the comments, I just re-read those and messed myself up

@@ -37,6 +37,11 @@ class ExecutionResponse(
* When specified, any operations that should be prepended to the operation repo.
*/
val operations: List<Operation>? = null,
/**
* Optional Integer value maybe returned from the backend.
* Any future requests by this time.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit - is this missing first part of "The module handing this should delay any future requests by this time."

Copy link
Member Author

Choose a reason for hiding this comment

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

ah good catch, just fixed this with a git commit --fixup and then did a git rebase --autosquash to keep the commits clean. New commit is 0e1f4a9

@@ -12,4 +12,9 @@ class BackendException(
* The response, if one exists.
*/
val response: String? = null,
/**
* Optional Integer value maybe returned from the backend.
* Any future requests by this time.
Copy link
Contributor

Choose a reason for hiding this comment

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

nit - is this missing first part of "The module handing this should delay any future requests by this time."

Copy link
Member Author

Choose a reason for hiding this comment

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

ah good catch, just fixed this with a git commit --fixup and then did a git rebase --autosquash to keep the commits clean. New commit is 0e1f4a9

@nan-li nan-li self-requested a review April 30, 2024 22:35
@nan-li
Copy link
Contributor

nan-li commented Apr 30, 2024

I confused myself with seconds and milliseconds. Approved now!

Add all the framework to parse Retry-After from the HTTP client layer,
through to the code calling it, to the OperationRepo.
This commit doesn't change any of the logic yet, but gets everything in
place before making behavior changes and adding the tests.
The real getHeaderField supports returning null when the key is not set.
Correcting this fixes failing HTTPClientTest
Use the retryAfterSeconds to stall processing in the OperationRepo. We
wait in the OperationRepo as this allows more operations to be grouped
together, as we getGroupableOperations will be called after the delay.

There are number of other places that use HTTPClient other than the
OperationRepo, we will address those in a follow up commit.
When we get a Retry-After we delay all future requests until after this
time as passed. To be safe we assume the server's Retry-After value
means all traffic should be delayed until then. If we wanted finer
grain control we could work with the the backend to decide which
endpoints should be affected.
@jkasten2 jkasten2 force-pushed the improve/handle-http-response-header-retry-after branch from 533d321 to 0a8684b Compare April 30, 2024 22:45
@jkasten2 jkasten2 merged commit 6a6349e into main May 1, 2024
2 of 3 checks passed
@jkasten2 jkasten2 deleted the improve/handle-http-response-header-retry-after branch May 1, 2024 17:50
@jkasten2 jkasten2 mentioned this pull request May 1, 2024
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