Uploaded image for project: 'Realm Core'
  1. Realm Core
  2. RCORE-476

Add support for progress listeners and timeouts when using AsyncOpen

      I have been looking at OS/Sync trying to figure out how exactly we can support timeouts and progress listeners. Unfortunately it looks at bit complicated and involves quite a few design choise we might have to revisit.

      Problems encountered so far:

      1. Progress listeners when downloading.

      In most (all?) of our bindings, the session is tied to the existence of a Realm. In the case of async open, no Realm exists, meaning it is impossible to get to the session. This in turn makes it impossible to register progress listeners to be notified about

      Note, this restriction is only in our binding side API's. Sync has no such restriction, and while Object Store kinda does, the new RealmCoordinator::get_realm() method will create the session without having a Realm.

      Possible solution:

      Bundle progress with the callback. ObjectStore will become responsible for registering them and reporting them until the initial Realm is downloaded. Setting this up is done in the configuration object

      In JS this would look something like this:

      let config = user.createConfiguration({
      	sync: {
      		url:  'https://myinstance.us1.cloud.realm.io'
      		waitForInitialRemoteData: true, // Wait indefinitely
      	}
      });
      Realm.open(config).then((realm, downloaded_bytes, total_bytes) => {
      	if (realm) {
      		// Once a Realm is returned, progress stops being reported
      		showData(realm);
      	} else {
      		showProgress(downloaded_bytes, total_bytes);
      	}
      })
      
      • This approach should be fully backward compatible (for JS), but does make the API slightly awkward since downloaded_bytes/total_bytes are meaningless for normal Realms and query-based Realms.

      In Java it would look something like this:

      SyncConfiguration config = user.createConfiguration(url)
      	.waitForInitialRemoteData()
      	.build()
      
      Realm.getInstanceAsync(new Realm.ProgressAwareCallback() {
      	@Override
      	public void onSuccess(Realm realm) {
      		// Continue with Realm
      	}
      	
      	@Override
      	public void onProgressUpdate(long bytesDownloaded, long totalBytes) {
      		// Always called one last time where bytesDownloaded = totalBytes before
      		// calling onSuccess
      	}
      
      	@Override
      	public void onError(Throwable error) {
      		// Handle error
      	}
      });
      

      Questions:

      • Would bundling progress notifications this way work for all bindings?
      • Would such an API be fine?

      2. Timing out or cancel download

      When working with a UX for Realm, most developers want a way to bail out if downloading the initial Realm is not possible or to slow. This requires some kind of timeout that is currently not supported by ObjectStore or Sync, so it is left up to the individual bindings to implement their own timeout mechanisms.

      The API would be the same as the above but in an extended version that makes it possible to set a timeout.

      For JS, this is a new API:

      let config = user.createConfiguration({
      	sync: {
      		url:  'https://myinstance.us1.cloud.realm.io'
      		waitForInitialRemoteData: 30 * 1000 /*ms*/
      	}
      });
      
      Realm.open(config).then((realm, bytes, total) => {
      	// Handle Realm
      }).catch(e => /* catch timeout */);
      
      // No way to manually cancel the download
      

      For Java, this API is currently available, and should be re-used when using async open:

      SyncConfiguration config = user.createConfiguration(url)
      	.waitForInitialRemoteData(30, TimeUnit.SECONDS)
      	.build()
      
      RealmAsyncTask task = Realm.getInstanceAsync(config, callback);
      task.cancel(); // Manually cancels the async job
      

      Possible solutions:

      It isn't clear to me how to fix this in ObjectStore, mostly because a lot of operations are guarded by mutexes preventing modifying the Session. One solution might be to let RealmCoordinator::get_realm() return a notification token like our standard listeners. It is then up to bindings to keep this token alive until either the Realm is ready or the timeout is hit.

      I don't know if there is any reason for why timeouts are not implemented directly in Sync in their Session::async_wait_for_download_completion() method.

      Questions:

      • Need ideas for how to approach this.
      • Is it acceptable that JS cannot cancel the download? If not, it isn't clear to me how that might be achieved without breaking changes.

            Assignee:
            Unassigned Unassigned
            Reporter:
            unitosyncbot Unito Sync Bot
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated: