ReceiptFetcher: throttle receipt refreshing to avoid StoreKit throttle errors#2146
Conversation
de76eb9 to
c442ad3
Compare
c442ad3 to
4e3348d
Compare
There was a problem hiding this comment.
makes sense. I wonder if a more reliable way of doing this would be to throttle unless we've had a response for the last request, i.e.: there are no requests in flight.
Do we know from the logs if for the customers that encountered this, they were throttled while a refresh was in flight or they were throttled after successful but frequent requests finished?
There was a problem hiding this comment.
makes sense. I wonder if a more reliable way of doing this would be to throttle unless we've had a response for the last request, i.e.: there are no requests in flight.
In most of these cases we're not doing concurrent requests, because we handle transactions linearly (see the example log in #2116). The problem is that we're refreshing it multiple times every second.
Do we know from the logs if for the customers that encountered this, they were throttled while a refresh was in flight or they were throttled after successful but frequent requests finished?
See log:
DEBUG: ℹ️ PaymentQueue updatedTransaction: product2 2000000218313433 2000000042575785 1
DEBUG: ℹ️ Force refreshing the receipt to get latest transactions from Apple.
DEBUG: ℹ️ PaymentQueue updatedTransaction: product2 2000000218320027 2000000042575785 1
DEBUG: ℹ️ Force refreshing the receipt to get latest transactions from Apple.
DEBUG: ℹ️ PaymentQueue updatedTransaction: product2 2000000218327443 2000000042575785 1
DEBUG: ℹ️ Force refreshing the receipt to get latest transactions from Apple.
DEBUG: ℹ️ PaymentQueue updatedTransaction: product2 2000000218334901 2000000042575785 1
DEBUG: ℹ️ Force refreshing the receipt to get latest transactions from Apple.
DEBUG: ℹ️ PaymentQueue updatedTransaction: product2 2000000218342186 2000000042575785 1
DEBUG: ℹ️ Force refreshing the receipt to get latest transactions from Apple.
DEBUG: ℹ️ PaymentQueue updatedTransaction: product2 2000000218353996 2000000042575785 1
DEBUG: ℹ️ Force refreshing the receipt to get latest transactions from Apple.
DEBUG: ℹ️ PaymentQueue updatedTransaction: product2 2000000218362596 2000000042575785 1
DEBUG: ℹ️ Force refreshing the receipt to get latest transactions from Apple.
DEBUG: ℹ️ PaymentQueue updatedTransaction: product2 2000000218370255 2000000042575785 1
DEBUG: ℹ️ Force refreshing the receipt to get latest transactions from Apple.
DEBUG: ℹ️ PaymentQueue updatedTransaction: product2 2000000218377794 2000000042575785 1
DEBUG: ℹ️ Force refreshing the receipt to get latest transactions from Apple.
<SKReceiptRefreshRequest: 0x283edc860>: Finished refreshing receipt with error: Error Domain=ASDErrorDomain Code=603 "Request throttled" UserInfo={NSLocalizedFailureReason=Unified receipt is valid and current, NSLocalizedDescription=Request throttled, AMSServerErrorCode=0}
DEBUG: ℹ️ Loaded receipt from url file:///private/var/mobile/Containers/Data/Application/5E49D650-1DE9-40C7-A20C-147D6C8718F8/StoreKit/sandboxReceipt
All those happen within the same second.
There was a problem hiding this comment.
that highlights what I mean, though - we don't wait until we get the response from SKReceiptRefreshRequest before allowing for the next request to start, right? the logs there show a single finish
There was a problem hiding this comment.
Ooooh I see what you're saying.
Okay, I think maybe a better fix would be to reuse the completion block for concurrent receipt requests instead of throttling them.
That's what you have in mind too?
There was a problem hiding this comment.
chatted about this offline - we already have a mechanism for this, it's just not enough if the receipt request finishes quickly.
There was a problem hiding this comment.
chatted about this offline - we already have a mechanism for this, it's just not enough if the receipt request finishes quickly.
) This would have given more visibility into the need for #2146.
…tle errors Fixes #2116.
4e3348d to
5c5d7c0
Compare
**This is an automatic release.** ### Bugfixes * Fix sending presentedOfferingIdentifier in StoreKit2 (#2156) via Toni Rico (@tonidero) * `ReceiptFetcher`: throttle receipt refreshing to avoid `StoreKit` throttle errors (#2146) via NachoSoto (@NachoSoto) ### Other Changes * Added integration and unit tests to verify observer mode behavior (#2069) via NachoSoto (@NachoSoto) * Created `ClockType` and `TestClock` to be able to mock time (#2145) via NachoSoto (@NachoSoto) * Extracted `asyncWait` to poll `async` conditions in tests (#2134) via NachoSoto (@NachoSoto) * `StoreKitRequestFetcher`: added log when starting/ending requests (#2151) via NachoSoto (@NachoSoto) * `CI`: fixed `PurchaseTester` deployment (#2147) via NachoSoto (@NachoSoto)
…nlyIfEmpty` after a purchase We've had reports of receipt fetching throttling errors (#2116). #2146 improved this by throttling receipt refreshing. However, we can avoid this altogether by not refreshing the receipt unless it's empty. The main reason being that the backend will automatically refresh the receipt using `/verifyReceipt`, so it doesn't matter if the receipt we post is slightly out of date.
…nlyIfEmpty` after a purchase (#2245) We've had reports of receipt fetching throttling errors (#2116). #2146 improved this by throttling receipt refreshing. However, we can avoid this altogether by not refreshing the receipt unless it's empty. The main reason being that the backend will automatically refresh the receipt using `/verifyReceipt`, so it doesn't matter if the receipt we post is slightly out of date.
Fixes #2116.
Depends on #2134, #2144, #2145.
We've known that, especially for sandbox accounts with lots of purchases, the SDK can get flooded with a lot of transactions to process at once.
I've been making some improvements for this (like #2115).
Another consequence of this behavior, is that we can end up failing due to
StoreKitthrottling us (see #2116):This change avoids that by skipping the refresh if less than 2 seconds have elapsed. I chose 2 to make this a low-ish risk change, with a huge benefit for multiple semi-concurrent requests coming from a big transaction queue.