mjibson/goon — pull request #43 by xStrom (2014-11-11 20:36:30+00:00)
Title: Now properly updating memcache after datastore even in transactions.

The basics of this change are the same as that of pull request #2.

However that patch fell a bit short, as it doesn't properly handle **transactions where the commit is delayed**. While the memcache is correctly updated after the datastore in the ```PutMulti``` / ```DeleteMulti``` methods, these aren't necessarily close to the datastore transaction commit time.

To illustrate the race condition, consider three parallel requests.

Time Request #1 Request #2 Request #3
T+000ms Transaction starts
T+010ms DeleteMulti() starts
T+020ms datastore clear logged
T+030ms memcache cleared
T+040ms additional TXN work Get() starts
T+050ms additional TXN work entity read from datastore
T+060ms TXN log committed memcache updated with entity
T+070ms Get() starts
T+080ms entity read from memcache
---
Result: entity == nil entity != nil entity != nil

Like the third request in the example, any further request with a ```Get``` will **incorrectly** belive that the entity still exists, because the cache isn't synchronized with the datastore.

This new patch collects the memcache keys that need to be updated, similarly to how the local cache is currently handled during transactions. Then, the memcache updating is done after the transaction has been successfully committed.

Now, lets look at what happens when my changes are applied.

Time Request #1 Request #2 Request #3
T+000ms Transaction starts
T+010ms DeleteMulti() starts
T+020ms memcache clear logged
T+030ms datastore clear logged
T+040ms additional TXN work Get() starts
T+050ms additional TXN work entity read from memcache
T+060ms TXN log committed
T+070ms memcache log committed Get() starts
T+080ms no entity found in memcache/datastore
---
Result: entity == nil entity != nil entity == nil

Like the third request in the example, any further request with a ```Get``` will belive that the entity does not exist, because the cache is **correctly synchronized** with the datastore.