Why would I ever want to load an Objectify entity asynchronously? And what does asynchronous loading actually mean?
According to Objectify documentation about loading, the following way of loading an Entity is asynchronous:
// Simple key fetch, always asynchronous
Result<Thing> th = ofy().load().key(thingKey);
And if I want a load to perform synchronously then I should do this:
Thing th = ofy().load().key(thingKey).now(); // added .now()
To me, asynchronous means that the action will take place later at some unspecified time. For saving, asynchronous makes sense because the datastore operation may need some time to finish on its own without blocking the application code.
But with loading, does asynchronous mean the load will take place at another time? How is that even possible in Java? I thought the variable Result<Thing> th
had to be updated when the line of code Result<Thing> th = ofy().load().key(thingKey);
finishes executing.
As a novice it's taken me a long time to figure this out (see for instance Objectify error "You cannot create a Key for an object with a null @Id" in JUnit).
So I have a few questions:
1] Why would I ever want to load an Objectify entity asynchronously?
2] What does asynchronous loading actually mean?
3] What is the conceptual link between now()
for loading and now()
for saving?
Synchronous Load (source)
Thing th = ofy().load().key(thingKey).now();
Synchronous Save (source)
ofy().save().entity(thing1).now();
4] Why isn't synchronous the default behavior for saving and loading?
Asynchronous operations start a network fetch to the backend and then let your code continue executing. The advantage of async operations is that you can run several of them in parallel:
This executes significantly faster than calling
now()
immediately each time. Note this is a bad example because you probably would use a batch get (which also parallelizes the operation) but you can have several queries, saves, deletes, etc running simultaneously.now()
always forces synchronous completion, blocking until done.The
Google Cloud Datasore
was designed to give the user a positive relational and non-relational experience, essentially the best of both worlds. Google datastore is a NoSQL database which offers eventual consistency for improved scalability but also gives you the option to choose strong consistency.This article by Google, Balancing Strong and Eventual Consistency with Google Cloud Datastore, will go a long way to answering some of your questions. It explains the eventual consistency model which is key to understanding how the datastore works under the hood in relation to your question.
Response from Google Cloud Support to support case 05483551:
“Asynchronous” in the context of Java means the use of “Futures” or Future-like constructs. A Future in java[1] is an object that represents an operation that doesn’t necessarily need to be performed and completed by the time the next line begins executing in the current thread.
A call to an asynchronous function in Java will return a Future immediately, representing the promise that a background “thread” will work on the computation/network call while the next line of the code continues to execute, not needing that result yet. When the method .get() is called on the Future object, either the result is returned, having been obtained in time, or the thread will wait until the result is obtained, passing execution to the next line after the .get() call only once this happens.
In Objectify, Futures were avoided, and instead the Result interface was defined[2], for reasons related to exceptions being thrown that made it painful to develop on the basis of Futures. They work in almost identical fashion, however. Where a regular Future has the method .get(), the Result interface (implemented by several different concrete classes depending what kind of Objectify call you’re doing) has .now(), which retrieves the result or waits the thread until it’s available.
The reason why you might want to load an entity asynchronously is when you have a request handler or API method that needs an Entity later in the function, but has some other computation to do as well, unrelated to the Entity. You can kick off the load for the entity in the first line, obtaining a Result, and then only call .now() on the Result once your other unrelated code has finished its execution. If you waited for the point when you call .now() to actually initiate the load, you might have your response handler/API method just waiting around for the result, instead of doing useful computations.
Finally, the conceptual link between .now() for loading and .now() for saving is that both operations happen in the background, and are only finally forced, waiting the execution thread, when .now() is called on the Result-interface-implementing object that is returned by the call to save() or load().
I hope this has helped explain the asynchronous constructs in Java Objectify for you. If you have any further questions or issues, feel free to include these in your reply, and I'll be happy to help.
Sincerely,
Nick Technical Solutions Representative Cloud Platform Support
[1] http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/Future.html
[2] http://objectify-appengine.googlecode.com/svn/trunk/javadoc/com/googlecode/objectify/Result.html