CacheableNetworkOperation: avoid unnecessarily creating operations for cache hits#2135
Conversation
5e851df to
cd79981
Compare
…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.
cd79981 to
beaf492
Compare
…mpletion Looking into the leak in #2135, I realized that we have an operation which doesn't call `completion`, we would be leaking the completion block in the cache. This ensures that it doesn't happen in tests. In production, we won't risk crashing.
tonidero
left a comment
There was a problem hiding this comment.
An alternative could be to have a method in the cache that accepts an operation type and cacheKey and tells you if it exists in the cache already, and we can create the operation or not depending on that. Seems simpler to me than having all operations require a factory to calculate the cache key beforehand, but it might be less extensible than this... So I'm ok with this.
|
|
||
| let operation = GetCustomerInfoOperation(configuration: config, | ||
| customerInfoCallbackCache: self.customerInfoCallbackCache) | ||
| let factory = GetCustomerInfoOperation.createFactory(configuration: config, |
There was a problem hiding this comment.
Nitpick: Maybe rename to operationFactory? It's only used in the local scope so it's NABD though. Same in the other operations.
| source: T.Type, | ||
| completion: @escaping Completion) { | ||
| self.cacheKey = cacheKey | ||
| self.source = T.self |
There was a problem hiding this comment.
The source parameter in the constructor seems unused right now. Probably meant to pass it in here? Though if it works with this, we might want to remove the parameter.
There was a problem hiding this comment.
It's used by callbacks(ofType type: NetworkOperation.Type).
| final class CacheableNetworkOperationFactory<T: CacheableNetworkOperation> { | ||
|
|
||
| let individualizedCacheKeyPart: String | ||
| let creator: (_ cacheKey: String) -> T |
There was a problem hiding this comment.
Nitpick: Maybe rename to creatorBlock?
There was a problem hiding this comment.
Swift's guidelines say to not include typed in variables names.
|
That was my first approach, but it wasn't possible to abstract that with a dynamic
For sure, I didn't want to have to create a separate type either :/ |
**This is an automatic release.** ### Bugfixes * `ErrorUtils.purchasesError(withUntypedError:)`: handle `PublicError`s (#2165) via NachoSoto (@NachoSoto) * Fixed race condition finishing `SK1` transactions (#2148) via NachoSoto (@NachoSoto) * `IntroEligibilityStatus`: added `CustomStringConvertible` implementation (#2182) via NachoSoto (@NachoSoto) * `BundleSandboxEnvironmentDetector`: fixed logic for `macOS` (#2176) via NachoSoto (@NachoSoto) * Fixed `AttributionFetcher.adServicesToken` hanging when called in simulator (#2157) via NachoSoto (@NachoSoto) ### Other Changes * `Purchase Tester`: added ability to purchase products directly with `StoreKit` (#2172) via NachoSoto (@NachoSoto) * `DNSChecker`: simplified `NetworkError` initialization (#2166) via NachoSoto (@NachoSoto) * `Purchases` initialization: refactor to avoid multiple concurrent instances in memory (#2180) via NachoSoto (@NachoSoto) * `Purchase Tester`: added button to clear messages on logger view (#2179) via NachoSoto (@NachoSoto) * `NetworkOperation`: added assertion to ensure that subclasses call completion (#2138) via NachoSoto (@NachoSoto) * `CacheableNetworkOperation`: avoid unnecessarily creating operations for cache hits (#2135) via NachoSoto (@NachoSoto) * `PurchaseTester`: fixed `macOS` support (#2175) via NachoSoto (@NachoSoto) * `IntroEligibilityCalculator`: added log including `AppleReceipt` (#2181) via NachoSoto (@NachoSoto) * `Purchase Tester`: fixed scene manifest (#2170) via NachoSoto (@NachoSoto) * `HTTPClientTests`: refactored to use `waitUntil` (#2168) via NachoSoto (@NachoSoto) * `Integration Tests`: split up tests in smaller files (#2158) via NachoSoto (@NachoSoto) * `StoreKitRequestFetcher`: removed unnecessary dispatch (#2152) via NachoSoto (@NachoSoto) * `Purchase Tester`: added companion `watchOS` app (#2140) via NachoSoto (@NachoSoto) * `StoreKit1Wrapper`: added warning if receiving too many updated transactions (#2117) via NachoSoto (@NachoSoto) * `StoreKitTestHelpers`: cleaned up unnecessary log (#2177) via NachoSoto (@NachoSoto) * `TrialOrIntroPriceEligibilityCheckerSK1Tests`: use `waitUntilValue` (#2173) via NachoSoto (@NachoSoto) * `DNSChecker`: added log with resolved host (#2167) via NachoSoto (@NachoSoto) * `MagicWeatherSwiftUI`: updated `README` to point to workspace (#2142) via NachoSoto (@NachoSoto) * `Purchase Tester`: fixed `.storekit` config file reference (#2171) via NachoSoto (@NachoSoto) * `Purchase Tester`: fixed error alerts (#2169) via NachoSoto (@NachoSoto) * `CI`: don't make releases until `release-checks` pass (#2162) via NachoSoto (@NachoSoto) * `Fastfile`: changed `match` to `readonly` (#2161) via NachoSoto (@NachoSoto)
Changes:
CacheableNetworkOperationFactoryto abstract operation and cache key creationI'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.