@@ -585,6 +585,23 @@ def _get_memcache_deadline(self, options=None):
585
585
# If this returns None, the system default (typically, 5) will apply.
586
586
return ContextOptions .memcache_deadline (options , self ._conn .config )
587
587
588
+
589
+ def _load_from_cache_if_available (self , key ):
590
+ """Returns a cached Model instance given the entity key if available.
591
+
592
+ Args:
593
+ key: Key instance.
594
+
595
+ Returns:
596
+ A Model instance if the key exists in the cache.
597
+ """
598
+ if key in self ._cache :
599
+ entity = self ._cache [key ] # May be None, meaning "doesn't exist".
600
+ if entity is None or entity ._key == key :
601
+ # If entity's key didn't change later, it is ok.
602
+ # See issue 13. http://goo.gl/jxjOP
603
+ raise tasklets .Return (entity )
604
+
588
605
# TODO: What about conflicting requests to different autobatchers,
589
606
# e.g. tasklet A calls get() on a given key while tasklet B calls
590
607
# delete()? The outcome is nondeterministic, depending on which
@@ -604,17 +621,12 @@ def get(self, key, **ctx_options):
604
621
**ctx_options: Context options.
605
622
606
623
Returns:
607
- A Model instance it the key exists in the datastore; None otherwise.
624
+ A Model instance if the key exists in the datastore; None otherwise.
608
625
"""
609
626
options = _make_ctx_options (ctx_options )
610
627
use_cache = self ._use_cache (key , options )
611
628
if use_cache :
612
- if key in self ._cache :
613
- entity = self ._cache [key ] # May be None, meaning "doesn't exist".
614
- if entity is None or entity ._key == key :
615
- # If entity's key didn't change later, it is ok.
616
- # See issue 13. http://goo.gl/jxjOP
617
- raise tasklets .Return (entity )
629
+ self ._load_from_cache_if_available (key )
618
630
619
631
use_datastore = self ._use_datastore (key , options )
620
632
if (use_datastore and
@@ -631,6 +643,9 @@ def get(self, key, **ctx_options):
631
643
mvalue = yield self .memcache_get (mkey , for_cas = use_datastore ,
632
644
namespace = ns , use_cache = True ,
633
645
deadline = memcache_deadline )
646
+ # A value may have appeared while yielding.
647
+ if use_cache :
648
+ self ._load_from_cache_if_available (key )
634
649
if mvalue not in (_LOCKED , None ):
635
650
cls = model .Model ._kind_map .get (key .kind ())
636
651
if cls is None :
@@ -794,7 +809,6 @@ def helper():
794
809
try :
795
810
inq = tasklets .SerialQueueFuture ()
796
811
query .run_to_queue (inq , self ._conn , options )
797
- is_ancestor_query = query .ancestor is not None
798
812
while True :
799
813
try :
800
814
batch , i , ent = yield inq .getq ()
@@ -903,10 +917,10 @@ def transaction(self, callback, **ctx_options):
903
917
adapter = parent ._conn .adapter ,
904
918
config = parent ._conn .config ,
905
919
transaction = transaction )
906
- old_ds_conn = datastore ._GetConnection ()
907
920
tctx = parent .__class__ (conn = tconn ,
908
921
auto_batcher_class = parent ._auto_batcher_class ,
909
922
parent_context = parent )
923
+ tctx ._old_ds_conn = datastore ._GetConnection ()
910
924
ok = False
911
925
try :
912
926
# Copy memcache policies. Note that get() will never use
@@ -929,7 +943,7 @@ def transaction(self, callback, **ctx_options):
929
943
raise
930
944
except Exception :
931
945
t , e , tb = sys .exc_info ()
932
- yield tconn .async_rollback (options ) # TODO: Don't block???
946
+ tconn .async_rollback (options ) # Fire and forget.
933
947
if issubclass (t , datastore_errors .Rollback ):
934
948
# TODO: Raise value using tasklets.get_return_value(t)?
935
949
return
@@ -943,7 +957,8 @@ def transaction(self, callback, **ctx_options):
943
957
raise tasklets .Return (result )
944
958
# The finally clause will run the on-commit queue.
945
959
finally :
946
- datastore ._SetConnection (old_ds_conn )
960
+ datastore ._SetConnection (tctx ._old_ds_conn )
961
+ del tctx ._old_ds_conn
947
962
if ok :
948
963
# Call the callbacks collected in the transaction context's
949
964
# on-commit queue. If the transaction failed the queue is
0 commit comments