2
votes

Objectify's save().entity(E entity).now() method returns a the Key<E> of the saved entity, which is useful when saving a new entity for the first time. So far, so good.

However, it's not entirely clear what this method returns when saving an entity that was already present in datastore, or whether the return value can tell me anything about whether that write was successful. Can I assume it was successful unless a RuntimeException is thrown, as per these javadocs? If so, is the true regardless of whether the write is inside a transaction?

Specifically, I'm reading, then modifying and saving two entities in an XG transaction using Objectify. I'm currently checking the return value of the first save before saving the second, like this:

if(ofy().save().entity(entA).now() != null) {
    ofy().save().entity(entB).now();
}

Firstly, I guess the first thing is I should save these with one ofy().save().entities(ent1, ent2).now() call?

Secondly, is it even meaningful for me to check the return value of the ofy().save().entity().now() call:

  1. ever
  2. inside a transaction? (e.g., Objectify retries if it catches a ConcurrentModificationException but what happens if a transaction commit keeps failing?)

Thanks for any clarification anyone can give on this.

2

2 Answers

5
votes

If you don't care about the Key value (which may have been assigned, ie you had a null Long id), then you can ignore the return value.

Not directly in your question but needs to be mentioned: Using the automatic id generator inside of a transaction is dangerous and should not be done, because it's not an idempotent operation. It's possible that you may get a transaction retry even though the transaction succeeded, and the retry will create a new entity. Always assign ids outside of the transaction.

I will go further and say that you should not use automatic assignment of ids at all. Explicitly generate them with the allocator when you construct your POJOs.

1
votes

Saving the entities with one call will result in a single RPC, meaning that the window of transaction failure will be smaller (calling .now() twice is triggering two synchronous RPCs). Remember also that there is a throttle on writes to an entity group, so any pattern that causes more RPCs has a wider impact than just transaction elapsed time itself.

The result of save will always have a key, a failure would result in an exception (and in the context of a transaction, possibly a retry). So it isn't meaningful to use it as a status check. Its just useful if you want to know the key after you do a put.