This is sort of a fundamental database question that isn’t directly related to Objectify or the datastore, but I’ll have at it:
If you want a series of operations to occur as if they are executed in serial, you need to use transactions and you need *each* operation to be wrapped in a transaction. If you do this, the database (datastore, postgres*, oracle*, etc) will ensure that the world works as if each transaction was executed in serial - even if it wasn’t. The datastore has an “optimistic concurrency model” which means that instead of locking and blocking, it lets each transaction execute in full and detects collisions. Objectify (and, fwiw, GAE/Python transactions) automatically retries transactions with collisions. If you have massive contention around a single piece of data, you’ll see timeouts as various transactions retry beyond reasonable bounds - just like as in with pessimistic concurrency models you’ll see timeouts waiting for locks to be acquired. The difference is that locks are expensive and potentially cause deadlocks; retries just go until a fixed timeout.
You can try to cheat the system by loading data outside of a transaction, and sometimes this is appropriate. If you didn’t really understand that last paragraph at a deep and intuitive level, DO NOT attempt to cheat the system, you’re probably doing it wrong.
Ok to answer your questions specifically:
* A load() is just a load(). In a transaction you skip the cache. It loads the current state of the entity. The loaded value is *ALWAYS* potentially stale, even 0.000001s after executing (hey, it’s a live system). The only reason you can count on a loaded value being consistent is when you load it in a transaction — when that txn commits, it will either write (yeay) or retry (if something else modified it in the mean time).
* A “non transactional save” is the same as a transaction that does only the save. It will retry until success. If you didn’t load the value inside the transaction, that means the unit of work is “save exactly this value no matter what, overwriting whatever was or wasn’t in the datastore beforehand and ignore any other transactions in progress”.
* Outside of a transaction, you cannot trust the value of any data. It can *always* be written by some other operation 0.00000001s after the read executes. If you care about consistency and order of operations, you need transactions and carefully defined units of work. This is the same whether you are using the datastore or any other kind of database.
Suerte,
Jeff
* Assuming you have the database in SERIALIZABLE mode