Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GAE HDR: Are entity retrievals by key eventually consistent within a XG transaction?

Consider the second example in "Uses for Transactions" ("update an entity with a named key, or create it if it doesn't yet exist"):

https://developers.google.com/appengine/docs/java/datastore/transactions

Now consider this scenario. A multiplayer game allows only a single match between any two players. To ensure that, a key is created using each of the player's keys. This key is used as the key of a UniqueMatch entity.

So in order to create a match, a XG transaction is created. Within this transaction:

  1. We check if there isn't a UniqueMatch entity with that key already. If a datastore.get() call using that key does not throw EntityNotFoundException, then we know that a match between those two players already exists, so we rollback() and show an error message to the players.

  2. We put() all entities we need to put in order to create a match. This includes the UniqueMatch entity, plus some other few entities.

  3. The transaction is then committed.

This seems to work fine. However, I noticed I can create two matches between any two players within a short time window. For a small period of time (up to 10-20s in one of the tests, actually), my calls to datastore.get(key) throw EntityNotFoundException even though that key has already been put().

This seems to be eventual consistency. But aren't entity retrivals by key guaranteed to be strongly consistent? Is this guarantee affected by the fact that this is done within a XG transaction?

Thanks in advance,

like image 755
fnf Avatar asked Oct 08 '12 15:10

fnf


1 Answers

I think the issue you're seeing may be because datastore gets (by key or queries) only see the state of the datastore at the start of the transaction.

From the docs (under isolation and consistency):

In a transaction, all reads reflect the current, consistent state of the Datastore at the time the transaction started. This does not include previous puts and deletes inside the transaction. Queries and gets inside a transaction are guaranteed to see a single, consistent snapshot of the Datastore as of the beginning of the transaction.

Also, outside of the transaction, you'll only see puts that have been committed (docs):

Entities retrieved from the datastore by queries or gets will only see committed data.

A possible solution:

Create the UniqueMatch entity outside of the transaction (appengine won't allow you to put an entity with the same key, so it'll throw an exception if the entity with the same key already exists). You can then create/put the other entities required to create a match inside a transaction (if need be).

Lastly, make sure when creating the key for the UniqueMatch that they key is always created with the players in the same order because key(playerA,playerB)!=key(playerB,playerA)

like image 110
Rob Curtis Avatar answered Oct 25 '22 21:10

Rob Curtis