Realm-js: Realm.open() hangs on Android

Created on 25 Aug 2017  路  25Comments  路  Source: realm/realm-js

EDIT: This also happens after reloading the emulator on React Native, in a very simple setup (see below https://github.com/realm/realm-js/issues/1248#issuecomment-328313637)

Goals

Get an error message when opening a synced realm if server not available or user is offline.

Expected Results

Realm.open() returns a promise that gets rejected with useful error message.

Actual Results

The promise simply stays in pending status indefinitely.

This callback does not get called:

https://github.com/realm/realm-js/blob/416632c9adc7350cdca12dc792edcf3e9d0b30d8/lib/extensions.js#L47

Hence the promise never returns.

Steps to Reproduce

Run this code after log in with valid user:

const openRealm = (user, url, schema) => {
  return Realm.open({
    sync: { user, url },
    schema,
    schemaVersion: 1
  });
};

Version of Realm and Tooling

  • Realm JS SDK Version: 1.10.3
  • React Native: 0.44.3
  • Which debugger for React Native: None
O-Customer T-Bug

All 25 comments

This only happens on Android, iOS works fine.

@Damnum In the server log, can you see that the user is logged in?
Can you describe your set up (is it a physical device or an emulator? are you connecting to localhost? )?

@kneth by the time I make the Realm.open call, the server is silent. But every 10 minutes I'm getting this server log:

10:21:03 PMinfo
sync: Sync Connection[3174]: Sync connection closed due to being idle
10:21:03 PMinfo
sync: Sync Connection[3174]: Session[2]: Session terminated (session_ident=2).
10:20:52 PMerror
sync: Sync Connection[3180]: Session[2]: Client file already bound in other session (message_type='ident', path='/b07f92ab1e5344d5a256bdc7bbb8de7c/th').
10:20:52 PMinfo
sync: Sync Connection[3178]: Session[2]: Disabled
10:20:52 PMinfo
sync: Sync Connection[3180]: Session[2]: Received: IDENT(server_file_ident=1, client_file_ident=16, client_file_ident_secret=5264769406148302654, scan_server_version=70, scan_client_version=168, latest_server_version=70, latest_server_session_ident=7022817502712743451)
10:20:52 PMinfo
sync: HTTP Connection[3180]: Received: Sync HTTP request(protocol_version=18)
10:20:52 PMinfo
sync: Sync Connection[3180]: Session[2]: Session initiated (session_ident=2).
10:20:52 PMinfo
sync: Sync Connection[3180]: Session[2]: Received: BIND(server_path=/b07f92ab1e5344d5a256bdc7bbb8de7c/th, signed_user_token='...p4j/BfL1oyc1wI4RkJ0RzMAatp7w==', need_file_ident_pair=0)
10:20:52 PMinfo
sync: HTTP Connection[3180]: Connection from 127.0.0.1:56488

I suppose that happens when the user token is refreshed. Same error happens with iOS on the server, though it only hangs on Android.

Happens on Android physical device and simulator. I'm connecting to a server hosted on DigitalOcean. May be related to https://github.com/realm-demos/realm-tasks/issues/397

EDIT: I also setup the realm on a server running on localhost. Still hangs, and the server doesn't even show the error above, so the error is probably not related.

@kneth I tested the exact same configuration on Android (both simulator and device) and iOS, with both localhost and server.

Result: On iOS, everything works as expected.

On Android, function _waitForDownload is never called -> Promise never resolves.

Looks like this function is wrapped into CPP code: https://github.com/realm/realm-js/blob/85fb49b35455880763cf7f2e1b5722db5c6871e9/src/js_realm.hpp#L588

Please let me know how to debug this code. Is there a way to print out logs there?

@Damnum Sorry the the late reply. Is it possible for you to contribute a small project which can reproduce the issue?

@kneth I will try. Not sure if it's important but I'm using the Android Java SDK to pre-fill the data in the first place.

I now tried to switch to ROS 2.0.0 rc-2, and realm-js 2.0.0-rc5 (on Android Java site: 4.0.0-BETA2-afe7114-SNAPSHOT). Same result though.

I'm attaching the server log from the moment the client makes the Realm.open call. The client log is silent (just hangs).

server-log.txt

EDIT: I'm also attaching the server log for the (working) Realm.open call on iOS.

server-log-ios-WORKING.txt

Seems like the login takes place in both logs, but the actual synced realm (called shared.realm) is not mentioned in the Android log.

@kneth have you had the chance to look at the logs? I realized that the logs are identical until a point where on iOS, the log says:

4:14:36 PMdebug proxy: attempting to upgrade client.....

After that, the actual download starts.

On Android, the log is silent instead (apart from sending stats). (see time 3:50:13)

@kneth I could reproduce the bug with a very simple setup.

  1. Download the tasks example app: https://github.com/realm-demos/realm-tasks
  2. Install the example ROS on localhost: https://realm.io/docs/get-started/installation/mac/
  3. Run the app as-is (just changelocalhost:9080 to 10.0.2.2:9080 in config.js to make it work on Android): Everything works as expected.
  4. Change the connect function in realm-tasks.js from synchronously opening via new Realm() to asynchronously opening via Realm.open (as recommended in the docs), see code below
  5. Now on Android emulator: First login works. Refresh app. For the following and all subsequent logins, the Realm.open callback hangs.
  6. On iOS, everything works fine even after refreshing multiple times.
function connect(action, username, password, callback) {
  username = username.trim();
  password = password.trim();
  if (username === "") {
    return callback(new Error("Username cannot be empty"));
  } else if (password === "") {
    return callback(new Error("Password cannot be empty"));
  }

  Realm.Sync.User[
    action
  ](config.auth_uri, username, password, (error, user) => {
    if (error) {
      return callback(new Error(error.message));
    } else {
      console.log("Opening realm...");
      Realm.open({
        schema: [Task, TaskList],
        sync: {
          user,
          url: config.db_uri
        },
        path: config.db_path
      }).then(realm => {
        console.log("Realm successfully opened!");
        callback(null, realm);
      });
    }
  });
}

Guys, this maybe related to not invoking the promise rejection with settimeout. This maybe the fix for it

https://github.com/realm/realm-js/pull/1279/commits/3c5f28fb9ecce520fe6fcab728330d3a3f3887be

Long story short on RN we have a requirement to use this hack with setTimeout(func, 1) to allow the promise to continue. We have an issue to resolve this properly here https://github.com/realm/realm-js/issues/1255 until then this seems like the workaround to use.

You may try cherry picking this fix and testing if the rejection is causing the promise to be stuck

@blagoev I think that will not help in this case. When I debug in this code, the callback given to the _waitForDownload function does not get called: https://github.com/realm/realm-js/blob/416632c9adc7350cdca12dc792edcf3e9d0b30d8/lib/extensions.js#L47

That means, line 48 will not even get called at all, so your timeout will never be reached. mE the only way to fix this is inside the wrapping wait_for_download_completion function mentioned above. Or at least improving the logging/error handling there to find out what's causing the issue.

As far as I can tell, the server log is also silent about it.

I'm able to reproduce the issue and indeed the callback to _waitForDownload never gets called.

@Damnum I mentioned this maybe the reason for a hang. If the callback is not called then the hang is in c++ code and we need to debug it to know exactly why. Still a hang in Realm.open will happen in RN if setTimeout is not used for resolve and reject, until we fix the callback issue in RN.

Hopefully when we debug the c++ we will get more details why this happens.

@blagoev do you know how to add debug statements into the c++ code? I tried it with

#include <android/log.h>
...
__android_log_print(ANDROID_LOG_INFO, "JSRealm", "INSIDE wait_for_download_completion");

But no luck so far...

Probably the best way is to debug the c++ code in Android Studio, but these __android_log_print should be working as well. Check that this code is picked up when you build the android app. Probably this requires an npm uninstall realm realm-tests && npm install again before build. @ashwinphatak is looking into this as well.

Added some debugging statements and it appears that the callback to async_wait_for in realm-js/react-native/android/src/main/jni/core/include/realm/sync/client.hpp never gets called the 2nd time onward (after the refresh). That's about as far as I could debug as the implementation of async_wait_for is inside an object file librealm-sync-android-x86.a.

image

image

Thanks for the digging @ashwinphatak ! I'm not really familiar with these native components... do you know if the source code for them is open source?

@ashwinphatak any news on this? This is a real blocker for our project.

librealm-sync-android-x86.a isn't open source, so we might have to wait for @blagoev to take a look.

I'm using [email protected] and [email protected], also facing this issue on Android.

With even simpler setup code:
~
return Realm.open({
path: db.realm,
schema
});
~

I'm able to reproduce this as well, on android the first app load works, but reloading JS code makes realm.open never resolve.

@bmunkholm @kneth not sure why this has been marked as "optional"? Failing to open the database is pretty basic stuff because without it you can't do anything else?!

@Damnum I can see how that can be confusing :-)
It's not an absolute priority, but a relative priority to the next upcoming release. It means that we will not hold back the release if we can't this fixed before the target date for the release.

Ok thanks for clarifying, that makes sense!

is this fix available via npm yet?

yes it is

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ugendrang picture ugendrang  路  3Comments

kevinnguy picture kevinnguy  路  3Comments

laznrbfe picture laznrbfe  路  3Comments

ashah888 picture ashah888  路  3Comments

bdebout picture bdebout  路  3Comments