invertase / react-native-firebase

🔥 A well-tested feature-rich modular Firebase implementation for React Native. Supports both iOS & Android platforms for all Firebase services.
https://rnfirebase.io
Other
11.74k stars 2.22k forks source link

[🐛] Write operation randomly take very long time before the (add/set) promise returned, only happening on iOS #4741

Closed TrustyTechSG closed 3 years ago

TrustyTechSG commented 3 years ago

Issue

We have a B2B App using this awesome library for a year now, we have around 500 Android devices customers using our App on a daily bases with no issue at all (tens of thousands document writes from client App everyday). But recently we start to have customers using iPhone. Then we start to receive feedback say basically all write operation in the App take very long time to complete. from around 10 seconds to forever base on customers feedbacks. For our internal testing on our own iPhone X (iOS 14), repeat a simple data set operation below, from 'START LOG' to 'FINISH LOG' can randomly take from 0 seconds to 90 seconds. Also we monitor the firebase console on the collection at the same time, it took the same amount of time before we saw the new document appear on the firebase console. (In Android or web case, the new document appear on the firebase console immediately after the set() been called every single time)

const operationRef = firestore().collection(realms/${realmId}/order_operations).doc(); // START LOG operationRef.set({ "businessCode": "TESTING", "checkStatus": "PENDING CONFIRMING", "createDate": {"date": "2021-01-07", "month": "2021-01", "raw": 2021-01-07T00:49:35.702Z, "time": "08:49:35", "year": "2021"}, "customer": { "contacts": ["87191452"], "email": "testing@gmail.com", "id": "NAtCt1JUl33YdiOvdsAY", "labelExpires": ["2020-12-31"], "labels": ["VIP 3"], "name": "NG Wei Jin test 888", "uid": "rn0Aicw8bZZiWTJMWkn0UOnwT0n2"}, "operator": {"id": "vQx2erVY1TUUAY3QYxFMMtBKVIL2", "name": "Ng"}, "orderId": "HwmJHiRC2YkJPrTNQGvE", "orderNo": "01277", "serviceId": "LAUNDRY", "status": "pending" }) .then(() => { // FINISH LOG });

Also we have one use case is client side update document A. then triggered the cloud onWrite function to update document B. On our Web and Android clients, no matter how quickly client made the updates on A, the B always get updated according to the latest A within 1 ~ 3 seconds (7 seconds for function cold start case). Which also proved every client writes succeed immediately. But on iOS, we notice in some case, if quickly update document A to V1, V2, V3, V4, V5. and monitor on B, you can see B been update from V1 to V5 at a random interval from 1 ~ 30 seconds one after another. So it feels like thats internal time should be the amount of time been taken for the initial write operation on A to succeed, and thats why cloud function been triggered one by one.

So It feels like something randomly blocked or put write operation in queue on iPhone.

Thank you very much for your time and support.

Project Files

Javascript

Click To Expand

#### `package.json`: ```json # N/A ``` #### `firebase.json` for react-native-firebase v6: ```json { "scripts": { "start": "react-native start", "android": "react-native run-android", "ios": "react-native run-ios", "web": "expo start --web", "postinstall": "npx jetify", "publish": "expo publish --release-channel easybus", "log": "adb logcat --pid=`adb shell pidof app.easybus.driver`" }, "dependencies": { "@react-native-community/datetimepicker": "3.0.4", "@react-native-community/masked-view": "0.1.10", "@react-native-community/netinfo": "5.9.7", "@react-native-community/viewpager": "4.2.0", "@react-native-firebase/app": "^10.4.0", "@react-native-firebase/auth": "^10.4.0", "@react-native-firebase/firestore": "^10.4.0", "@react-native-firebase/functions": "^10.4.0", "@react-native-firebase/ml": "^10.4.0", "@react-native-firebase/storage": "^10.4.0", "@react-native-mapbox-gl/maps": "^8.1.0-rc10", "@react-navigation/bottom-tabs": "^5.11.1", "@react-navigation/native": "^5.8.9", "@react-navigation/stack": "^5.12.6", "@terrylinla/react-native-sketch-canvas": "^0.8.0", "Base64": "^1.1.0", "add": "^2.0.6", "base-64": "^0.1.0", "buffer": "^5.5.0", "esc-pos-encoder": "^1.3.0", "expo": "^40.0.0", "expo-av": "~8.7.0", "expo-constants": "~9.3.3", "expo-file-system": "~9.3.0", "expo-gl": "~9.2.0", "expo-image-manipulator": "~8.4.0", "expo-image-picker": "~9.2.0", "expo-localization": "~9.1.0", "expo-location": "~10.0.0", "expo-notifications": "~0.8.2", "expo-permissions": "~10.0.0", "expo-speech": "~8.5.0", "expo-splash-screen": "~0.8.1", "expo-task-manager": "~8.6.0", "expo-updates": "~0.4.1", "firebase": "^8.2.1", "geolib": "^3.2.1", "jetifier": "^1.6.5", "moment": "^2.24.0", "react": "16.13.1", "react-dom": "16.13.1", "react-native": "0.63.4", "react-native-ble-plx": "^2.0.2", "react-native-bluetooth-escpos-printer": "https://github.com/TrustyTechSG/react-native-bluetooth-escpos-printer", "react-native-calendars": "^1.403.0", "react-native-camera": "^3.40.0", "react-native-dialog": "^5.6.0", "react-native-draggable-flatlist": "^2.3.2", "react-native-elements": "^1.2.7", "react-native-fix-image": "^2.0.1", "react-native-gesture-handler": "~1.8.0", "react-native-image-crop-picker": "^0.32.2", "react-native-image-picker": "^2.3.1", "react-native-image-zoom-viewer": "^2.2.27", "react-native-intercom": "^17.0.0", "react-native-keyevent": "^0.2.8", "react-native-modal": "^11.5.6", "react-native-modal-datetime-picker": "^9.1.0", "react-native-open-maps": "^0.3.4", "react-native-paper": "3.10.1", "react-native-pickers": "^2.0.0", "react-native-progress": "^4.1.2", "react-native-qrcode-svg": "^6.0.3", "react-native-reanimated": "~1.13.0", "react-native-root-siblings": "^4.0.6", "react-native-root-toast": "^3.2.0", "react-native-safe-area-context": "3.1.9", "react-native-safe-area-view": "^1.0.0", "react-native-screens": "~2.15.0", "react-native-share": "^4.1.0", "react-native-side-menu": "^1.1.3", "react-native-svg": "12.1.0", "react-native-tab-view": "^2.15.2", "react-native-table-component": "^1.2.1", "react-native-unimodules": "~0.12.0", "react-native-web": "~0.13.12", "yarn": "^1.22.10" }, "devDependencies": { "@babel/core": "~7.9.0", "babel-jest": "~24.9.0", "babel-preset-expo": "8.3.0", "jest": "~24.9.0", "metro-react-native-babel-preset": "~0.56.4", "react-test-renderer": "~16.9.0" }, "private": true } ```

iOS

Click To Expand

#### `ios/Podfile`: - [ ] I'm not using Pods - [x] I'm using Pods and my Podfile looks like: ```ruby require_relative '../node_modules/react-native/scripts/react_native_pods' require_relative '../node_modules/react-native-unimodules/cocoapods.rb' require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' platform :ios, '10.0' $ReactNativeMapboxGLIOSVersion = '~> 5.8' target 'Easybus' do use_unimodules! config = use_native_modules! use_react_native!(:path => config["reactNativePath"]) # Enables Flipper. # # Note that if you have use_frameworks! enabled, Flipper will not work and # you should disable these next few lines. use_flipper! post_install do |installer| flipper_post_install(installer) end end ``` #### `AppDelegate.m`: ```objc #import "AppDelegate.h" #import "Intercom/intercom.h" #import #import #import #import #import #import #import #import #import #ifdef FB_SONARKIT_ENABLED #import #import #import #import #import #import static void InitializeFlipper(UIApplication *application) { FlipperClient *client = [FlipperClient sharedClient]; SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults]; [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]]; [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]]; [client addPlugin:[FlipperKitReactPlugin new]]; [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]]; [client start]; } #endif @interface AppDelegate () @property (nonatomic, strong) UMModuleRegistryAdapter *moduleRegistryAdapter; @property (nonatomic, strong) NSDictionary *launchOptions; @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { if ([FIRApp defaultApp] == nil) { [FIRApp configure]; } #ifdef FB_SONARKIT_ENABLED InitializeFlipper(application); #endif self.moduleRegistryAdapter = [[UMModuleRegistryAdapter alloc] initWithModuleRegistryProvider:[[UMModuleRegistryProvider alloc] init]]; self.launchOptions = launchOptions; self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; #ifdef DEBUG [self initializeReactNativeApp]; #else EXUpdatesAppController *controller = [EXUpdatesAppController sharedInstance]; controller.delegate = self; [controller startAndShowLaunchScreen:self.window]; #endif [super application:application didFinishLaunchingWithOptions:launchOptions]; // Intercom [Intercom setApiKey:@"ios_sdk-c33efd69f35dbd62dc9adb3cdfe5654fd03b45f6" forAppId:@"dyid66pa"]; return YES; } - (RCTBridge *)initializeReactNativeApp { RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:self.launchOptions]; RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:@"main" initialProperties:nil]; rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; UIViewController *rootViewController = [UIViewController new]; rootViewController.view = rootView; self.window.rootViewController = rootViewController; [self.window makeKeyAndVisible]; return bridge; } - (NSArray> *)extraModulesForBridge:(RCTBridge *)bridge { NSArray> *extraModules = [_moduleRegistryAdapter extraModulesForBridge:bridge]; // If you'd like to export some custom RCTBridgeModules that are not Expo modules, add them here! return extraModules; } - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { #ifdef DEBUG return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; #else return [[EXUpdatesAppController sharedInstance] launchAssetUrl]; #endif } - (void)appController:(EXUpdatesAppController *)appController didStartWithSuccess:(BOOL)success { appController.bridge = [self initializeReactNativeApp]; EXSplashScreenService *splashScreenService = (EXSplashScreenService *)[UMModuleRegistryProvider getSingletonModuleForClass:[EXSplashScreenService class]]; [splashScreenService showSplashScreenFor:self.window.rootViewController]; } @end ```


Android

Click To Expand

#### Have you converted to AndroidX? - [ ] my application is an AndroidX application? - [ ] I am using `android/gradle.settings` `jetifier=true` for Android compatibility? - [ ] I am using the NPM package `jetifier` for react-native compatibility? #### `android/build.gradle`: ```groovy // N/A ``` #### `android/app/build.gradle`: ```groovy // N/A ``` #### `android/settings.gradle`: ```groovy // N/A ``` #### `MainApplication.java`: ```java // N/A ``` #### `AndroidManifest.xml`: ```xml ```


Environment

Click To Expand

**`react-native info` output:** ``` System: OS: macOS 10.15.4 CPU: (8) x64 Intel(R) Core(TM) i5-1038NG7 CPU @ 2.00GHz Memory: 1.76 GB / 16.00 GB Shell: 5.7.1 - /bin/zsh Binaries: Node: 12.16.3 - /usr/local/bin/node Yarn: 1.22.4 - /usr/local/bin/yarn npm: 6.14.4 - /usr/local/bin/npm Watchman: 4.9.0 - /usr/local/bin/watchman Managers: CocoaPods: 1.9.3 - /usr/local/bin/pod SDKs: iOS SDK: Platforms: iOS 14.3, DriverKit 20.2, macOS 11.1, tvOS 14.3, watchOS 7.2 Android SDK: API Levels: 27, 28, 29 Build Tools: 28.0.3, 29.0.2, 29.0.3 System Images: android-28 | Intel x86 Atom_64, android-29 | Google Play Intel x86 Atom Android NDK: Not Found IDEs: Android Studio: 3.6 AI-192.7142.36.36.6392135 Xcode: 12.3/12C33 - /usr/bin/xcodebuild Languages: Java: 1.8.0_252 - /usr/bin/javac Python: 2.7.16 - /usr/bin/python npmPackages: @react-native-community/cli: Not Found react: 16.13.1 => 16.13.1 react-native: 0.63.4 => 0.63.4 react-native-macos: Not Found npmGlobalPackages: *react-native*: Not Found ``` - **Platform that you're experiencing the issue on**: - [x] iOS - [ ] Android - [ ] **iOS** but have not tested behavior on Android - [ ] **Android** but have not tested behavior on iOS - [ ] Both - **`react-native-firebase` version you're using that has this issue:** - `10.4.0` - **`Firebase` module(s) you're using that has the issue:** - `Firestore` - **Are you using `TypeScript`?** - `N`


mikehardy commented 3 years ago

Hi there - historically we haven't had a great track record diagnosing and fixing performance issues since I've been involved in the project, maybe 12-18 months now. That leads me to be a little pessimistic here. The closest we got last time was with a very specific setup: a reproduction scenario that started with a data generator javascript method that created a data test fixture of approximately the right size (both in object count and object size) as well as an App.js that was self-contained and demonstrated the performance issue.

If we had this and could drop it into a clean project (e.g., from https://github.com/mikehardy/rnfbdemo/blob/master/make-demo.sh) then it's a matter of firing up a profiler and seeing what we could do. It also allows isolation of layers, by which I mean you could also attempt it against the native firebase-ios-sdk to see if happened there or only when react-native-firebase was involved in your stack.

It is my guess that this is a firebase-ios-sdk issue based on your description of queueing, which makes me wonder if bisecting between various firebase-ios-sdk versions would turn anything up with regard to a regression there, or perhaps you have a record of firebase-ios-sdk versions in use vs when this started happening as a starting basis for that.

None of this is very concrete for you unfortunately, but for us without a solid reproduction, it's difficult to assist.

TrustyTechSG commented 3 years ago

Hi @mikehardy , thanks you for the quick reply, we are now working on the demo, hope can find out the issue soon.

mikehardy commented 3 years ago

lack of response, but if we can get an App.js that reproduces in a clean demo we can always reopen and investigate