Skip to content

BasePurchasesTests: detecting and fixing many DeviceCache leaks#2105

Merged
NachoSoto merged 2 commits into
mainfrom
device-cache-leaking-test
Dec 12, 2022
Merged

BasePurchasesTests: detecting and fixing many DeviceCache leaks#2105
NachoSoto merged 2 commits into
mainfrom
device-cache-leaking-test

Conversation

@NachoSoto

@NachoSoto NachoSoto commented Nov 30, 2022

Copy link
Copy Markdown
Contributor

Follow up to #2104. See also #2101. This has uncovered a huge source of leaks across many tests. Fixing this provides many benefits:

  • Faster test runs
  • Lower memory overhead
  • Isolated state across each test run
  • Ensure that these types don't leak outside of tests too

@NachoSoto NachoSoto requested a review from a team November 30, 2022 01:29
@NachoSoto

Copy link
Copy Markdown
Contributor Author

There's still a few tests failing that I'm trying to figure out why.

class PurchaseDeferredPurchasesTests: BasePurchasesTests {

private var storeKit1WrapperDelegate: StoreKit1WrapperDelegate!
private var storeKit1WrapperDelegate: StoreKit1WrapperDelegate {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We were creating retain cycles with this.

class PurchaseDeferredPurchasesSK2Tests: BasePurchasesTests {

private var paymentQueueWrapperDelegate: PaymentQueueWrapperDelegate!
private var paymentQueueWrapperDelegate: PaymentQueueWrapperDelegate {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Another retain cycle.

class MockNotificationCenter: NotificationCenter {

typealias ObserversWithSelector = (observer: AnyObject,
typealias ObserversWithSelector = (observer: WeakBox<AnyObject>,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This was leaking objects.

Comment on lines +109 to +141
self.mockOperationDispatcher = nil
self.paymentQueueWrapper = nil
self.requestFetcher = nil
self.receiptFetcher = nil
self.mockProductsManager = nil
self.mockIntroEligibilityCalculator = nil
self.mockTransactionsManager = nil
self.backend = nil
self.attributionFetcher = nil
self.purchasesDelegate.makeDeferredPurchase = nil
self.purchasesDelegate = nil
self.storeKit1Wrapper.delegate = nil
self.storeKit1Wrapper = nil
self.systemInfo = nil
self.notificationCenter = nil
self.subscriberAttributesManager = nil
self.trialOrIntroPriceEligibilityChecker = nil
self.attributionPoster = nil
self.attribution = nil
self.customerInfoManager = nil
self.identityManager = nil
self.mockOfferingsManager = nil
self.mockManageSubsHelper = nil
self.mockBeginRefundRequestHelper = nil
self.purchasesOrchestrator = nil

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is a lot, but it's been the only way to remove one by one the references at the end of the tests to see what's leaking. And there's still some retain cycle somewhere!

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

were leaks in tests only or also in the main SDK?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

So far the ones I've found are only in tests, but DeviceCache is still leaking. I need to spend a bit more time to find the last cause, which might be in the main SDK.


func testFinishesTransactionsIfSentToBackendCorrectly() throws {
var finished = true
var finished = false

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This test was probably not doing anything before :)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🤦

NachoSoto added a commit that referenced this pull request Nov 30, 2022
Follow up to #2104 and #2105. This ensures that `Purchases` is re-created for each test.
Comment on lines +109 to +141
self.mockOperationDispatcher = nil
self.paymentQueueWrapper = nil
self.requestFetcher = nil
self.receiptFetcher = nil
self.mockProductsManager = nil
self.mockIntroEligibilityCalculator = nil
self.mockTransactionsManager = nil
self.backend = nil
self.attributionFetcher = nil
self.purchasesDelegate.makeDeferredPurchase = nil
self.purchasesDelegate = nil
self.storeKit1Wrapper.delegate = nil
self.storeKit1Wrapper = nil
self.systemInfo = nil
self.notificationCenter = nil
self.subscriberAttributesManager = nil
self.trialOrIntroPriceEligibilityChecker = nil
self.attributionPoster = nil
self.attribution = nil
self.customerInfoManager = nil
self.identityManager = nil
self.mockOfferingsManager = nil
self.mockManageSubsHelper = nil
self.mockBeginRefundRequestHelper = nil
self.purchasesOrchestrator = nil

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

were leaks in tests only or also in the main SDK?


func testFinishesTransactionsIfSentToBackendCorrectly() throws {
var finished = true
var finished = false

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

🤦

@NachoSoto NachoSoto force-pushed the purchase-leaking-test branch from ec006ee to 425de93 Compare December 9, 2022 01:24
Base automatically changed from purchase-leaking-test to main December 9, 2022 01:36
@NachoSoto NachoSoto force-pushed the device-cache-leaking-test branch 2 times, most recently from c4963bd to 5f9e7c8 Compare December 12, 2022 18:28
NachoSoto added a commit that referenced this pull request Dec 12, 2022
…llbacks

See also #2135.
I'm looking into the leak in #2105, and I have a theory that it's due to callbacks being stored in this cache, but sometimes not being dequeued, so leaking all references in them.

This assertion will only run in RC tests, and will ensure that this doesn't happen.
Follow up to #2100. This has uncovered a huge source of leaks across many tests. Fixing this will provide many benefits:
- Faster test runs
- Lower memory overhead
- Isolated state across each test run
- Ensure that these types don't leak outside of tests too
@NachoSoto NachoSoto force-pushed the device-cache-leaking-test branch from 5f9e7c8 to 9cd046a Compare December 12, 2022 21:35
@NachoSoto NachoSoto removed the WIP label Dec 12, 2022
@NachoSoto

Copy link
Copy Markdown
Contributor Author

Tests are passing now 🎉

@NachoSoto NachoSoto merged commit 52fd952 into main Dec 12, 2022
@NachoSoto NachoSoto deleted the device-cache-leaking-test branch December 12, 2022 21:43
NachoSoto added a commit that referenced this pull request Dec 13, 2022
…llbacks (#2137)

See also #2135.
I'm looking into the leak in #2105, and I have a theory that it's due to
callbacks being stored in this cache, but sometimes not being dequeued,
so leaking all references in them.

This assertion will only run in RC tests, and will ensure that this
doesn't happen.
NachoSoto added a commit that referenced this pull request Dec 13, 2022
…for cache hits

- Created `CacheableNetworkOperationFactory` to abstract operation and cache key creation
- Instead of always creating an operation, we create a factory first, which pre-computes the cache key
- This cache key is used to determine if it's a cache hit, as well as for the subsequently created operation

I'm still investigating a leak (#2105), and I suspect it's coming from us creating and storing completion blocks, and those staying in memory beyond the lifetime of the operation.
This refactor and performance improvement helps us avoid get more things in memory than we need to.
NachoSoto pushed a commit that referenced this pull request Dec 14, 2022
**This is an automatic release.**

### Bugfixes
* Un-deprecate `Purchases.configure(withAPIKey:appUserID:)` and
`Purchases.configure(withAPIKey:appUserID:observerMode:)` (#2129) via
NachoSoto (@NachoSoto)
### Other Changes
* `ReceiptFetcherTests`: refactored tests using `waitUntilValue` (#2144)
via NachoSoto (@NachoSoto)
* Added a few performance improvements for `ReceiptParser` (#2124) via
NachoSoto (@NachoSoto)
* `CallbackCache`: fixed reference (#2143) via NachoSoto (@NachoSoto)
* `PostReceiptDataOperation`: clarified receipt debug log (#2128) via
NachoSoto (@NachoSoto)
* `CallbackCache`: avoid exposing internal mutable cache (#2136) via
NachoSoto (@NachoSoto)
* `CallbackCache`: added assertion for tests to ensure we don't leak
callbacks (#2137) via NachoSoto (@NachoSoto)
* `NetworkOperation`: made `Atomic` references immutable (#2139) via
NachoSoto (@NachoSoto)
* `ReceiptParser`: ensure parsing never happens in the main thread
(#2123) via NachoSoto (@NachoSoto)
* `PostReceiptDataOperation`: also print receipt data with `verbose`
logs (#2127) via NachoSoto (@NachoSoto)
* `BasePurchasesTests`: detecting and fixing many `DeviceCache` leaks
(#2105) via NachoSoto (@NachoSoto)
* `StoreKitIntegrationTests`: added test to check applying a promotional
offer during subscription (#1588) via NachoSoto (@NachoSoto)
NachoSoto added a commit that referenced this pull request Dec 21, 2022
…for cache hits (#2135)

### Changes:
- Created `CacheableNetworkOperationFactory` to abstract operation and
cache key creation
- Instead of always creating an operation, we create a factory first,
which pre-computes the cache key
- This cache key is used to determine if it's a cache hit, as well as
for the subsequently created operation

I'm still investigating a leak (#2105), and I suspect it's coming from
us creating and storing completion blocks, and those staying in memory
beyond the lifetime of the operation.
This refactor and performance improvement helps us avoid get more things
in memory than we need to.

See #2138 for how this is tested.
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.

2 participants