Memory leak with Apollo Client in react native on iOS
See original GitHub issueIntended outcome: Run the application without a quickly increasing memory footprint causing the OS to kill the app
Actual outcome:
The memory footprint of the app quickly grows until the system decides to kill the application. Looking at the profiling information from Instruments, it looks like little chunk of 112 bytes are being allocated quickly and causing the memory usage to spin out of control
How to reproduce the issue:
Code
Note i’m new to this codebase and while a little familiar with Apollo, i’m by no mean an advanced user 😃 So feel free to let me know if you need more info.
In a react native app.
const logErrorLink = onError(({ graphQLErrors, networkError, operation }) => {
reportError(
new Error(`Error Apollo ${operation.operationName}`),
(report) => {
const _params = {
data: {
operationName: operation.operationName,
extensions: operation.extensions,
query: operation.query,
variables: operation.variables,
conext: operation.getContext(),
graphQLErrors,
networkError,
} as any,
};
report.addMetadata('report', _params);
}
);
});
const withToken = setContext(async () => {
const user = auth().currentUser;
let token = '';
try {
if (user) {
token = await user.getIdToken();
}
} catch (error) {
consoleDebug('error la', error);
}
return { token, uid: user?.uid || '' };
});
const authMiddleware = new ApolloLink((operation, forward) => {
const { token, uid } = operation.getContext();
operation.setContext(() => ({
headers: {
...createRequestParams(uid, 'apollo', operation.operationName),
Authorization: token ? `Bearer ${token as string}` : '',
},
}));
return forward(operation);
});
const retryLink = new RetryLink({
delay: (retryNumber) => {
const delay = Math.pow(2, retryNumber) * 300;
const randomSum = delay * 0.2 * Math.random(); // 0-20% of the delay
return delay + randomSum;
},
attempts: {
max: 4,
retryIf: async (error, operation) => {
if (error.statusCode === 401) {
const user = auth().currentUser;
const { uid } = operation.getContext();
let token = '';
if (user) {
token = await user.getIdToken(true);
bugsnag.leaveBreadcrumb('new refreshToken apollo');
}
if (token) {
operation.setContext({
headers: {
...createRequestParams(uid, 'apollo', operation.operationName),
Authorization: `Bearer ${token}`,
},
});
return true;
}
}
return true;
},
},
});
const link = ApolloLink.from([
retryLink,
logErrorLink,
withToken,
authMiddleware.concat(persistedQueriesLink.concat(httpLink)),
]);
const linkPayment = ApolloLink.from([
retryLink,
logErrorLink,
withToken,
authMiddleware.concat(persistedQueriesLink.concat(httpLinkPayment)),
]);
const client = new ApolloClient({
cache: new InMemoryCache(),
name: `mobile-${Platform.OS}`,
version: DeviceInfo.getReadableVersion(),
// link: concat(authMiddleware, httpLink)
link,
});
async processUser(data: any) {
try {
await client.mutate({
mutation: updateUserMutation,
variables: {
data,
},
});
} catch (e) {
console.log('NEw apollo user', e);
}
}
Update user mutation
mutation updateUser($data: Json!) {
updateUser(data: $data)
}
Run the app from Xcode by selecting Product > Profile
Once the method processUser
is called, the memory footprint start to grow and the app crashes within 30s to 1 minute.
The image below shows the allocation of those 112 bytes of memory chunk over 25 seconds until the app is killed
Looking at where this memory is allocated shows the following stack trace (some are from different places, but 99% is from this place):
0 libc++abi.dylib operator new(unsigned long)
1 Choose void* std::__1::__libcpp_operator_new<unsigned long>(unsigned long) /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.0.sdk/usr/include/c++/v1/new:235
2 Choose std::__1::__libcpp_allocate(unsigned long, unsigned long) /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.0.sdk/usr/include/c++/v1/new:261
3 Choose std::__1::allocator<std::__1::__hash_node<std::__1::__hash_value_type<folly::dynamic, folly::dynamic>, void*> >::allocate(unsigned long) /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.0.sdk/usr/include/c++/v1/memory:870
4 Choose std::__1::allocator_traits<std::__1::allocator<std::__1::__hash_node<std::__1::__hash_value_type<folly::dynamic, folly::dynamic>, void*> > >::allocate(std::__1::allocator<std::__1::__hash_node<std::__1::__hash_value_type<folly::dynamic, folly::dynamic>, void*> >&, unsigned long) /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.0.sdk/usr/include/c++/v1/__memory/allocator_traits.h:260
5 Choose std::__1::unique_ptr<std::__1::__hash_node<std::__1::__hash_value_type<folly::dynamic, folly::dynamic>, void*>, std::__1::__hash_node_destructor<std::__1::allocator<std::__1::__hash_node<std::__1::__hash_value_type<folly::dynamic, folly::dynamic>, void*> > > > std::__1::__hash_table<std::__1::__hash_value_type<folly::dynamic, folly::dynamic>, std::__1::__unordered_map_hasher<folly::dynamic, std::__1::__hash_value_type<folly::dynamic, folly::dynamic>, folly::detail::DynamicHasher, folly::detail::DynamicKeyEqual, true>, std::__1::__unordered_map_equal<folly::dynamic, std::__1::__hash_value_type<folly::dynamic, folly::dynamic>, folly::detail::DynamicKeyEqual, folly::detail::DynamicHasher, true>, std::__1::allocator<std::__1::__hash_value_type<folly::dynamic, folly::dynamic> > >::__construct_node<std::__1::piecewise_construct_t const&, std::__1::tuple<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&>, std::__1::tuple<> >(std::__1::piecewise_construct_t const&, std::__1::tuple<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&>&&, std::__1::tuple<>&&) /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.0.sdk/usr/include/c++/v1/__hash_table:2455
6 Choose std::__1::pair<std::__1::__hash_iterator<std::__1::__hash_node<std::__1::__hash_value_type<folly::dynamic, folly::dynamic>, void*>*>, bool> std::__1::__hash_table<std::__1::__hash_value_type<folly::dynamic, folly::dynamic>, std::__1::__unordered_map_hasher<folly::dynamic, std::__1::__hash_value_type<folly::dynamic, folly::dynamic>, folly::detail::DynamicHasher, folly::detail::DynamicKeyEqual, true>, std::__1::__unordered_map_equal<folly::dynamic, std::__1::__hash_value_type<folly::dynamic, folly::dynamic>, folly::detail::DynamicKeyEqual, folly::detail::DynamicHasher, true>, std::__1::allocator<std::__1::__hash_value_type<folly::dynamic, folly::dynamic> > >::__emplace_unique_impl<std::__1::piecewise_construct_t const&, std::__1::tuple<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&>, std::__1::tuple<> >(std::__1::piecewise_construct_t const&, std::__1::tuple<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&>&&, std::__1::tuple<>&&) /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.0.sdk/usr/include/c++/v1/__hash_table:2137
7 Choose std::__1::pair<std::__1::__hash_iterator<std::__1::__hash_node<std::__1::__hash_value_type<folly::dynamic, folly::dynamic>, void*>*>, bool> std::__1::__hash_table<std::__1::__hash_value_type<folly::dynamic, folly::dynamic>, std::__1::__unordered_map_hasher<folly::dynamic, std::__1::__hash_value_type<folly::dynamic, folly::dynamic>, folly::detail::DynamicHasher, folly::detail::DynamicKeyEqual, true>, std::__1::__unordered_map_equal<folly::dynamic, std::__1::__hash_value_type<folly::dynamic, folly::dynamic>, folly::detail::DynamicKeyEqual, folly::detail::DynamicHasher, true>, std::__1::allocator<std::__1::__hash_value_type<folly::dynamic, folly::dynamic> > >::__emplace_unique<std::__1::piecewise_construct_t const&, std::__1::tuple<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&>, std::__1::tuple<> >(std::__1::piecewise_construct_t const&, std::__1::tuple<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&>&&, std::__1::tuple<>&&) /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.0.sdk/usr/include/c++/v1/__hash_table:1096
8 Choose std::__1::pair<std::__1::__hash_map_iterator<std::__1::__hash_iterator<std::__1::__hash_node<std::__1::__hash_value_type<folly::dynamic, folly::dynamic>, void*>*> >, bool> std::__1::unordered_map<folly::dynamic, folly::dynamic, folly::detail::DynamicHasher, folly::detail::DynamicKeyEqual, std::__1::allocator<std::__1::pair<folly::dynamic const, folly::dynamic> > >::emplace<std::__1::piecewise_construct_t const&, std::__1::tuple<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&>, std::__1::tuple<> >(std::__1::piecewise_construct_t const&, std::__1::tuple<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&>&&, std::__1::tuple<>&&) /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.0.sdk/usr/include/c++/v1/unordered_map:1158
9 Choose auto std::__1::pair<std::__1::__hash_map_iterator<std::__1::__hash_iterator<std::__1::__hash_node<std::__1::__hash_value_type<folly::dynamic, folly::dynamic>, void*>*> >, bool> folly::f14::detail::F14BasicMap<folly::dynamic, folly::dynamic, folly::detail::DynamicHasher, folly::detail::DynamicKeyEqual, std::__1::allocator<std::__1::pair<folly::dynamic const, folly::dynamic> > >::emplace<std::__1::piecewise_construct_t const&, std::__1::tuple<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&>, std::__1::tuple<> >(std::__1::piecewise_construct_t const&, std::__1::tuple<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&>&&, std::__1::tuple<>&&)::'lambda'(auto&, auto&&...)::operator()<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const, std::__1::piecewise_construct_t const&, std::__1::tuple<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&>, std::__1::tuple<> >(auto&, auto&&...) const /Users/otusweb/Projects/AppChoose/ios/Pods/Headers/Public/RCT-Folly/folly/container/detail/F14MapFallback.h:215
10 Choose _ZN5folly6detail22callWithKeyAndPairArgsINS_7dynamicES2_ZNS_3f146detail11F14BasicMapIS2_S2_NS0_13DynamicHasherENS0_15DynamicKeyEqualENSt3__19allocatorINS8_4pairIKS2_S2_EEEEE7emplaceIJRKNS8_21piecewise_construct_tENS8_5tupleIJRNS8_12basic_stringIcNS8_11char_traitsIcEENS9_IcEEEEEEENSJ_IJEEEEEENSA_INS8_19__hash_map_iteratorINS8_15__hash_iteratorIPNS8_11__hash_nodeINS8_17__hash_value_typeIS2_S2_EEPvEEEEEEbEEDpOT_EUlRT_DpOT0_E_SO_JSP_EJEEEDaOT1_RKT2_ONSJ_IJDpT3_EEEONSJ_IJDpT4_EEE /Users/otusweb/Projects/AppChoose/ios/Pods/Headers/Public/RCT-Folly/folly/container/detail/Util.h:125
11 Choose _ZN5folly6detail20callWithExtractedKeyINS_7dynamicES2_NS_3f146detail11F14BasicMapIS2_S2_NS0_13DynamicHasherENS0_15DynamicKeyEqualENSt3__19allocatorINS8_4pairIKS2_S2_EEEEE11UsableAsKeyESD_ZNSE_7emplaceIJRKNS8_21piecewise_construct_tENS8_5tupleIJRNS8_12basic_stringIcNS8_11char_traitsIcEENS9_IcEEEEEEENSK_IJEEEEEENSA_INS8_19__hash_map_iteratorINS8_15__hash_iteratorIPNS8_11__hash_nodeINS8_17__hash_value_typeIS2_S2_EEPvEEEEEEbEEDpOT_EUlRT_DpOT0_E_SQ_JELi0EEEDaRT2_OT3_SH_ONSK_IJT4_EEEONSK_IJDpT5_EEE /Users/otusweb/Projects/AppChoose/ios/Pods/Headers/Public/RCT-Folly/folly/container/detail/Util.h:155
12 Choose std::__1::pair<std::__1::__hash_map_iterator<std::__1::__hash_iterator<std::__1::__hash_node<std::__1::__hash_value_type<folly::dynamic, folly::dynamic>, void*>*> >, bool> folly::f14::detail::F14BasicMap<folly::dynamic, folly::dynamic, folly::detail::DynamicHasher, folly::detail::DynamicKeyEqual, std::__1::allocator<std::__1::pair<folly::dynamic const, folly::dynamic> > >::emplace<std::__1::piecewise_construct_t const&, std::__1::tuple<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&>, std::__1::tuple<> >(std::__1::piecewise_construct_t const&, std::__1::tuple<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&>&&, std::__1::tuple<>&&) /Users/otusweb/Projects/AppChoose/ios/Pods/Headers/Public/RCT-Folly/folly/container/detail/F14MapFallback.h:203
13 Choose std::__1::enable_if<folly::detail::EligibleForHeterogeneousInsert<folly::dynamic, folly::detail::DynamicHasher, folly::detail::DynamicKeyEqual, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&>::value, std::__1::pair<std::__1::__hash_map_iterator<std::__1::__hash_iterator<std::__1::__hash_node<std::__1::__hash_value_type<folly::dynamic, folly::dynamic>, void*>*> >, bool> >::type folly::f14::detail::F14BasicMap<folly::dynamic, folly::dynamic, folly::detail::DynamicHasher, folly::detail::DynamicKeyEqual, std::__1::allocator<std::__1::pair<folly::dynamic const, folly::dynamic> > >::try_emplace<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) /Users/otusweb/Projects/AppChoose/ios/Pods/Headers/Public/RCT-Folly/folly/container/detail/F14MapFallback.h:268
14 Choose std::__1::enable_if<folly::detail::EligibleForHeterogeneousInsert<folly::dynamic, folly::detail::DynamicHasher, folly::detail::DynamicKeyEqual, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&>::value, folly::dynamic&>::type folly::f14::detail::F14BasicMap<folly::dynamic, folly::dynamic, folly::detail::DynamicHasher, folly::detail::DynamicKeyEqual, std::__1::allocator<std::__1::pair<folly::dynamic const, folly::dynamic> > >::operator[]<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&) /Users/otusweb/Projects/AppChoose/ios/Pods/Headers/Public/RCT-Folly/folly/container/detail/F14MapFallback.h:356
15 Choose std::__1::enable_if<!(std::is_convertible<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::__1::__wrap_iter<folly::dynamic*> >::value), void>::type folly::dynamic::insert<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::nullptr_t>(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >&, std::nullptr_t&&) /Users/otusweb/Projects/AppChoose/ios/Pods/Headers/Public/RCT-Folly/folly/dynamic-inl.h:834
16 Choose facebook::jsi::dynamicFromValue(facebook::jsi::Runtime&, facebook::jsi::Value const&) /Users/otusweb/Projects/AppChoose/node_modules/react-native/ReactCommon/jsi/jsi/JSIDynamic.cpp:187
17 Choose facebook::react::JSIExecutor::callNativeModules(facebook::jsi::Value const&, bool) /Users/otusweb/Projects/AppChoose/node_modules/react-native/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp:420
18 Choose facebook::react::JSIExecutor::callFunction(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&, folly::dynamic const&) /Users/otusweb/Projects/AppChoose/node_modules/react-native/ReactCommon/jsiexecutor/jsireact/JSIExecutor.cpp:270
19 Choose std::__1::__function::__value_func<void (facebook::react::JSExecutor*)>::operator()(facebook::react::JSExecutor*&&) const /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.0.sdk/usr/include/c++/v1/functional:1885
20 Choose std::__1::function<void (facebook::react::JSExecutor*)>::operator()(facebook::react::JSExecutor*) const /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.0.sdk/usr/include/c++/v1/functional:2560
21 Choose facebook::react::NativeToJsBridge::runOnExecutorQueue(std::__1::function<void (facebook::react::JSExecutor*)>)::$_8::operator()() const /Users/otusweb/Projects/AppChoose/node_modules/react-native/ReactCommon/cxxreact/NativeToJsBridge.cpp:310
22 Choose decltype(std::__1::forward<facebook::react::NativeToJsBridge::runOnExecutorQueue(std::__1::function<void (facebook::react::JSExecutor*)>)::$_8&>(fp)()) std::__1::__invoke<facebook::react::NativeToJsBridge::runOnExecutorQueue(std::__1::function<void (facebook::react::JSExecutor*)>)::$_8&>(facebook::react::NativeToJsBridge::runOnExecutorQueue(std::__1::function<void (facebook::react::JSExecutor*)>)::$_8&) /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.0.sdk/usr/include/c++/v1/type_traits:3694
23 Choose void std::__1::__invoke_void_return_wrapper<void, true>::__call<facebook::react::NativeToJsBridge::runOnExecutorQueue(std::__1::function<void (facebook::react::JSExecutor*)>)::$_8&>(facebook::react::NativeToJsBridge::runOnExecutorQueue(std::__1::function<void (facebook::react::JSExecutor*)>)::$_8&) /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.0.sdk/usr/include/c++/v1/__functional_base:348
24 Choose std::__1::__function::__alloc_func<facebook::react::NativeToJsBridge::runOnExecutorQueue(std::__1::function<void (facebook::react::JSExecutor*)>)::$_8, std::__1::allocator<facebook::react::NativeToJsBridge::runOnExecutorQueue(std::__1::function<void (facebook::react::JSExecutor*)>)::$_8>, void ()>::operator()() /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.0.sdk/usr/include/c++/v1/functional:1558
25 Choose std::__1::__function::__func<facebook::react::NativeToJsBridge::runOnExecutorQueue(std::__1::function<void (facebook::react::JSExecutor*)>)::$_8, std::__1::allocator<facebook::react::NativeToJsBridge::runOnExecutorQueue(std::__1::function<void (facebook::react::JSExecutor*)>)::$_8>, void ()>::operator()() /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.0.sdk/usr/include/c++/v1/functional:1732
26 Choose std::__1::__function::__value_func<void ()>::operator()() const /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.0.sdk/usr/include/c++/v1/functional:1885
27 Choose std::__1::function<void ()>::operator()() const /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.0.sdk/usr/include/c++/v1/functional:2560
28 Choose facebook::react::tryAndReturnError(std::__1::function<void ()> const&) /Users/otusweb/Projects/AppChoose/node_modules/react-native/React/CxxModule/RCTCxxUtils.mm:74
29 Choose facebook::react::RCTMessageThread::tryFunc(std::__1::function<void ()> const&) /Users/otusweb/Projects/AppChoose/node_modules/react-native/React/CxxBridge/RCTMessageThread.mm:69
30 Choose std::__1::__function::__value_func<void ()>::operator()() const /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.0.sdk/usr/include/c++/v1/functional:1885
31 Choose std::__1::function<void ()>::operator()() const /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.0.sdk/usr/include/c++/v1/functional:2560
32 Choose invocation function for block in facebook::react::RCTMessageThread::runAsync(std::__1::function<void ()>) /Users/otusweb/Projects/AppChoose/node_modules/react-native/React/CxxBridge/RCTMessageThread.mm:45
33 CoreFoundation __CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__
34 CoreFoundation __CFRunLoopDoBlocks
35 CoreFoundation __CFRunLoopRun
36 CoreFoundation CFRunLoopRunSpecific
37 Choose +[RCTCxxBridge runRunLoop] /Users/otusweb/Projects/AppChoose/node_modules/react-native/React/CxxBridge/RCTCxxBridge.mm:367
38 Foundation __NSThread__start__
39 libsystem_pthread.dylib _pthread_start
40 libsystem_pthread.dylib thread_start
Versions System: OS: macOS 11.6 Binaries: Node: 16.11.1 - ~/.nvm/versions/node/v16.11.1/bin/node Yarn: 1.22.17 - ~/.nvm/versions/node/v16.11.1/bin/yarn npm: 7.24.2 - ~/Projects/AppChoose/node_modules/.bin/npm Browsers: Chrome: 95.0.4638.69 Safari: 15.0 npmPackages: @apollo/client: 3.4.15 => 3.4.15
I also tried to downgrade to 3.4.0, with no luck
I’m unsure if this is related to https://github.com/apollographql/apollo-client/issues/8903 or not
Issue Analytics
- State:
- Created 2 years ago
- Reactions:1
- Comments:5 (1 by maintainers)
Top GitHub Comments
The example above is on a mutation, but it also happens on query
Once getUserBalance is called I get the same results.
Turns out this is not due to Apollo, but something we were doing in one of our middleware.