Firebase: Asynchronous Operations with Admin Java SDK

Hiranya Jayathilaka
Dec 5, 2017 · 7 min read

Version 5.4.0 of the Firebase Admin Java SDK made significant improvements to how threads and asynchronous operations are handled by the SDK. Among the changes, the deprecation of the interface, and introduction of the interface are perhaps the most noteworthy. These changes cut across multiple APIs exposed by the Admin Java SDK, and have a noticeable impact on how developers write code using the SDK. This post discusses the rationale behind these changes, and explains how to migrate a Java application from Tasks to ApiFutures.


Most public API methods in the Admin Java SDK are asynchronous. That is, the caller does not get blocked on the methods. The SDK simply submits the execution of the method body to a pool of worker threads, and immediately returns to the caller with an object that represents the submitted operation. Up until version 5.4.0 of the SDK, this returned object was an instance of where the generic type parameter represented the type of the result produced by the asynchronous operation. For example, the method for creating custom JWTs would return a .

The interface supports registering callbacks that will fire when the underlying asynchronous operation completes. It also facilitates continuations — i.e. chaining multiple asynchronous operations, so that the result produced by one operation can be piped to another.

Version 5.4.0 of the Admin Java SDK deprecated the interface, as well as all the public API methods that return a . The SDK now recommends using the newly introduced methods that return . For instance, instead of , developers are now advised to use the method, which returns an . Similar replacements have been introduced to all other public methods in the SDK. These new methods are functionally equivalent to their deprecated counterparts. That is, they also submit the method execution to a pool of worker threads, and return immediately. But the returned objects that represent asynchronous operations are of a different type.

Architecturally, and interfaces represent two distinct styles of asynchronous programming. Therefore they are seemingly quite different from each other. However, they can be used to implement the same use cases, and thus migrating from one to the other is often trivial, as we shall see shortly.

Why deprecate the ?

This move was motivated by two main reasons:

  1. Most server-side Java libraries — including the libraries shipped by Google Cloud Platform (GCP)— expose asynchronous operations using Java’s built-in interface (or a child-interface of it). We wanted the Admin Java SDK to look more like those libraries, so that server-side Java developers would feel at home. We also wanted to provide a consistent developer experience when mixing Firebase and GCP libraries in the same application.
  2. The interface and the associated utilities were forked from the Android Google Mobile Services (GMS) API, during the early days of the Admin Java SDK. Keeping this code in sync with Android GMS, and maintaining it was becoming cumbersome.

The interface being an Android API should not surprise you. The callback-based programming style promoted by this API is prevalent in client-side platforms like Android. Client apps are usually tied to a graphical user interface (GUI). Hence they are expected to be highly interactive and responsive. Imagine a mobile app with a button, where some computation should be performed at the click of the button, and the results displayed on screen. is the perfect abstraction to implement this use case. The app can start a when the button is clicked, and the GUI update logic can be implemented as a callback to that . This way, the GUI worker thread that receives input from the user never gets blocked, and the GUI remains responsive the whole time.

In contrast, most server-side applications do not have to be interactive. They often do not have a GUI, and are instead designed for machine-to-machine interactions at scale. Consequently, server-side Java applications tend to prioritize scale and reduced end-to-end request latency over GUI experience. In this context, what developers typically need is the simple fork-join style of asynchronous programming. This is where a program would fork a set of potentially expensive operations in parallel threads, and later join them if necessary. The interface is great for that.

When taking these intricacies into consideration, it becomes apparent why an API based on Futures is better-suited for the Admin Java SDK. However, we did not want to completely give up on callbacks either. In many event-driven and reactive systems, callbacks are a must have (even in server-side). Therefore instead of the built-in interface, we chose one of its richer child-interfaces— from the Google API Common project. ApiFutures offer best of both worlds. They are derived from the same built-in interface of Java, but also support adding callbacks. Moreover, several GCP libraries have also adopted ApiFutures, which is all the more reason to use the same interface in Firebase Admin Java SDK.

Migrating from Tasks to ApiFutures

What follows is a set of code samples that demonstrate how to migrate Java code from Tasks to ApiFutures. Each sample shows a certain feature of Tasks, and the equivalent code using ApiFutures. If you have any code that uses Tasks, chances are you are making generous use of callbacks. Therefore, lets begin by demonstrating how to add callbacks to an .

Listing 1: Adding callbacks to a Task and an ApiFuture

There are few noteworthy points about this example:

  1. The Task interface directly exposes the methods for adding callbacks (e.g. and ). But to add a callback to an , one must use the static method in the helper class.
  2. The interface facilitates adding separate callbacks to handle success, failure and completion events. The on the other hand has a less flexible API for adding callbacks, in the sense it only supports one type of callbacks — which handles both success and failure events.
  3. In both APIs interfaces there is no limit to the number of callbacks that can be added, and there are no guarantees concerning the order of callbacks when they execute.

An inquisitive developer might like to know on which thread the added in listing 1 gets executed. If the underlying asynchronous operation has not yet completed by the time is invoked, the callback will run on the same thread as the asynchronous operation, whenever it completes. If the operation has already completed, the callback runs immediately on the thread that called . This behavior is good enough when the callbacks are simple and short-lived. If your callbacks are expensive or if you wish to have more predictable semantics with respect to this, you should use the override that accepts an as a third argument. [Caveat: This override is only available since version 1.2.0 of Google API Common, whereas the latest Admin SDK (5.5.0) ships with 1.1.0. Until this gets resolved, one must upgrade Google API Common manually to use the new method.]

Next we shall discuss how to migrate continuations. A continuation transforms the output of an asynchronous operation, and exposes the aggregate computation (asynchronous operation+transformation) as a new . The recursive nature of this action effectively enables chaining sequences of asynchronous operations. Listing 2 shows a continuation that transforms the string produced by a into a list of strings. The resulting aggregate computation is represented by an instance of .

Listing 2: Continuations with Tasks and ApiFutures

In this example, the interface plays the role of the now deprecated interface. The helper method exposes the resulting aggregate computation as an instance of . There is also an interface, and an helper method for cases where the transformation itself needs to make asynchronous calls.

Listing 3 demonstrates how to wrap values and exceptions in ApiFutures. There’s little cause for a developer using Admin SDK to use it, since the SDK already provides methods that return ApiFutures. It is mentioned here for completeness.

Listing 3: Wrapping values and exceptions

Be mindful when adding callbacks or transformations to an immediate . They will always execute on the calling thread of the or method. If this is not the desired behavior, an explicit should be specified when calling those helper methods.

Listing 4 illustrates how to wait until an asynchronous operation completes. The helper class is required to wait on a . But it is rather trivial with an .

Listing 4: Waiting for Tasks and ApiFutures

Sometimes we want to create an incomplete asynchronous operation, and complete it later. This is often useful when you want to represent an arbitrary chunk of code as an asynchronous operation. With Tasks this was achieved using the . Our last code sample in listing 5 shows how to replace a with an .

Listing 5: Incomplete asynchronous operations

Conclusion

Since the 5.4.0 release of the Java Admin SDK, I’ve seen several inquiries for more details and examples regarding the interface. I hope this post addresses them, while alleviating some of the tensions around to migration. I also invite the Firebase Java community to take a look at the newly added interface, which enables configuring thread pools and thread factories for the Admin SDK. It provides far greater control over how the SDK schedules and executes asynchronous operations.

Going forward, we can expect more features, stability and documentation around the support available in the Admin SDK. Firebase is also making it easier to seamlessly mix and match Admin SDK APIs with various GCP libraries (there’s already support for Google Cloud Storage and Google Cloud Firestore). If you’re following the Java Admin SDK GitHub repo, you may have seen that there’s also an effort towards exposing a set of blocking APIs from the SDK. Share your thoughts in comments as well as on GitHub. Let us know how you use the Java Admin SDK, and how the developer experience can be further improved.

Google Cloud Platform - Community

A collection of technical articles published or curated by Google Cloud Platform Developer Advocates. The views expressed are those of the authors and don't necessarily reflect those of Google.

Hiranya Jayathilaka

Written by

Software engineer at Google. Enjoys working at the intersection of cloud, mobile and programming languages. Fan of all things tech and open source.

Google Cloud Platform - Community

A collection of technical articles published or curated by Google Cloud Platform Developer Advocates. The views expressed are those of the authors and don't necessarily reflect those of Google.