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

fix: Remove managedSelf from InterceptorRequestChain #3070

Merged
merged 14 commits into from
Jun 14, 2023

Conversation

calvincestari
Copy link
Member

@calvincestari calvincestari commented Jun 13, 2023

Fixes #3057 (supersedes #3065)

This PR removes managedSelf from InterceptorRequestChain going back to the memory management model that was used before the introduction of HTTP-based subscriptions.

The gist of this solution is to deprecate the current proceedAsync function, adding a new one that requires the interceptor to identify itself. It's the interceptor identifier that allows us to get rid of ApolloInterceptorReentrantWrapper and managedSelf.

@@ -4,6 +4,8 @@ import ApolloAPI

/// A protocol to set up a chainable unit of networking work.
public protocol ApolloInterceptor {

var id: String { get set }
Copy link
Member Author

Choose a reason for hiding this comment

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

Identifiable is only available on iOS 13+ and macOS 15+, we support lower versions of both. This imposes the same 'identifiable' requirement for interceptors.

Copy link

Choose a reason for hiding this comment

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

  1. It would be really helpful to document this method and explain what the purpose and scope of this identifier is, so that custom implementers can decide what the best identifier would be for their specific use case.
  2. In order to avoid the breaking change introduced by this, have you considered a default implementation of this requirement using a protocol extension?

Copy link
Member Author

Choose a reason for hiding this comment

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

It would be really helpful to document this method and explain what the purpose and scope of this identifier is, so that custom implementers can decide what the best identifier would be for their specific use case.

Documentation can be added to clarify it's purpose, I'll get that done. As long as it's unique to other identifiers and a String it shouldn't matter. This was required as a workaround to the interceptor architecture while fixing the memory leak and maintaining the ability to support HTTP-based subscriptions.

In order to avoid the breaking change introduced by this, have you considered a default implementation of this requirement using a protocol extension?

Yes I did, but Swift extensions cannot hold stored properties and the identifier needs to be constant during the lifetime of the interceptor. Static constants don't work here either.

Copy link

Choose a reason for hiding this comment

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

Documentation can be added to clarify it's purpose, I'll get that done.

Thanks! Perhaps the scope is the interceptor instance? What does "other identifiers" mean concretely? Unique within the set of installed interceptors active during the processing of one request?

Swift extensions cannot hold stored properties and the identifier needs to be constant during the lifetime of the interceptor.

Ahh, makes sense. Perhaps an alternative might've been an id { ObjectIdentifier(self) }, but it would incur a protocol requirement for class-semantics by the implementer.

Copy link
Member Author

Choose a reason for hiding this comment

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

What does "other identifiers" mean concretely? Unique within the set of installed interceptors active during the processing of one request?

Correct. The interceptor provider passed into RequestChainNetworkTransport during initialization is used to build the request chain for each request. They need to be unique amongst that array of interceptors.

)
}

private func proceedAsync<Operation: GraphQLOperation>(
Copy link
Member Author

Choose a reason for hiding this comment

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

The shared logic of proceedAsync so the other two public functions are purely for interceptor lookup.

@calvincestari
Copy link
Member Author

@AnthonyMDev, @BobaFetters - this solution is generally ready for review. It needs some tests for the index sync between the two proceedAsync methods and the beginnings of a migration guide can go into here too.

Copy link
Member

@BobaFetters BobaFetters left a comment

Choose a reason for hiding this comment

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

The code changes look great! I think with some extra tests and migration guide like you mentioned this will be good to go.

Copy link
Contributor

@AnthonyMDev AnthonyMDev left a comment

Choose a reason for hiding this comment

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

Awesome work! This definitely feels way better. Sorry for pushing us in the wrong direction previously.

Sources/Apollo/InterceptorRequestChain.swift Outdated Show resolved Hide resolved
Sources/Apollo/InterceptorRequestChain.swift Outdated Show resolved Hide resolved
)

// when
requestChain.kickoff(request: request) { result in }
Copy link
Member Author

Choose a reason for hiding this comment

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

We don't care about the response in these tests, there are other tests for that. These purely ensure that the interceptors are executed in the correct order.

@calvincestari calvincestari merged commit 1ec00c8 into release/1.3 Jun 14, 2023
@calvincestari calvincestari deleted the fix/remove-managed-self branch June 14, 2023 17:40
This was referenced Jun 20, 2023
eXhausted pushed a commit to eXhausted/apollo-ios that referenced this pull request Jun 10, 2024
…ql#3070)

# Conflicts:
#	Tests/ApolloInternalTestHelpers/InterceptorTester.swift
#	docs/source/config.json
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.

Memory leak from InterceptorRequestChain when ending the chain with returnValueAsync
4 participants