def lock_unlock_key(): # pragma: NO COVER lock = yield _cache.global_lock_for_write(key) cache_value = yield _cache.global_get(key) assert lock in cache_value yield _cache.global_unlock_for_write(key, lock) cache_value = yield _cache.global_get(key) assert lock not in cache_value
def test_global_get_clear_cache_soon_with_error(_batch, _global_cache): """Regression test for #633 https://github.com/googleapis/python-ndb/issues/633 """ class TransientError(Exception): pass batch = _batch.get_batch.return_value future = _future_result("hi mom!") batch.add.return_value = future _global_cache.return_value = mock.Mock( transient_errors=(TransientError), clear_cache_soon=True, strict_read=False, clear=mock.Mock(side_effect=TransientError("oops!"), spec=()), spec=("transient_errors", "clear_cache_soon", "clear", "strict_read"), ) with warnings.catch_warnings(record=True) as logged: assert _cache.global_get(b"foo").result() is None assert len(logged) == 2 _global_cache.return_value.clear.assert_called_once_with()
def lookup(key, options): """Look up a Datastore entity. Gets an entity from Datastore, asynchronously. Checks the global cache, first, if appropriate. Uses batching. Args: key (~datastore.Key): The key for the entity to retrieve. options (_options.ReadOptions): The options for the request. For example, ``{"read_consistency": EVENTUAL}``. Returns: :class:`~tasklets.Future`: If not an exception, future's result will be either an entity protocol buffer or _NOT_FOUND. """ context = context_module.get_context() use_datastore = context._use_datastore(key, options) if use_datastore and options.transaction: use_global_cache = False else: use_global_cache = context._use_global_cache(key, options) if not (use_global_cache or use_datastore): raise TypeError( "use_global_cache and use_datastore can't both be False") entity_pb = _NOT_FOUND key_locked = False if use_global_cache: cache_key = _cache.global_cache_key(key) result = yield _cache.global_get(cache_key) key_locked = _cache.is_locked_value(result) if not key_locked: if result is not None: entity_pb = entity_pb2.Entity() entity_pb.MergeFromString(result) elif use_datastore: yield _cache.global_lock(cache_key, read=True) yield _cache.global_watch(cache_key) if entity_pb is _NOT_FOUND and use_datastore: batch = _batch.get_batch(_LookupBatch, options) entity_pb = yield batch.add(key) # Do not cache misses if use_global_cache and not key_locked: if entity_pb is not _NOT_FOUND: expires = context._global_cache_timeout(key, options) serialized = entity_pb.SerializeToString() yield _cache.global_compare_and_swap(cache_key, serialized, expires=expires) else: yield _cache.global_unwatch(cache_key) raise tasklets.Return(entity_pb)
def test_global_get_with_error_strict(_batch, _global_cache, sleep): class TransientError(Exception): pass sleep.return_value = future_result(None) batch = _batch.get_batch.return_value future = _future_exception(TransientError("oops")) batch.add.return_value = future _global_cache.return_value = mock.Mock( transient_errors=(TransientError,), strict_read=True, spec=("transient_errors", "strict_read"), ) with pytest.raises(TransientError): _cache.global_get(b"foo").result() _batch.get_batch.assert_called_with(_cache._GlobalCacheGetBatch) batch.add.assert_called_with(b"foo")
def test_global_get(_batch, _global_cache): batch = _batch.get_batch.return_value future = _future_result("hi mom!") batch.add.return_value = future _global_cache.return_value = mock.Mock( transient_errors=(), strict_read=False, spec=("transient_errors", "strict_read"), ) assert _cache.global_get(b"foo").result() == "hi mom!" _batch.get_batch.assert_called_once_with(_cache._GlobalCacheGetBatch) batch.add.assert_called_once_with(b"foo")
def test_global_get_clear_cache_soon(_batch, _global_cache): batch = _batch.get_batch.return_value future = _future_result("hi mom!") batch.add.return_value = future _global_cache.return_value = mock.Mock( transient_errors=(), clear_cache_soon=True, spec=("transient_errors", "clear_cache_soon", "clear"), ) with warnings.catch_warnings(record=True) as logged: assert _cache.global_get(b"foo").result() == "hi mom!" assert len(logged) == 1 _batch.get_batch.assert_called_once_with(_cache._GlobalCacheGetBatch) batch.add.assert_called_once_with(b"foo") _global_cache.return_value.clear.assert_called_once_with()
def test_global_get_with_error_not_strict(_batch, _global_cache): class TransientError(Exception): pass batch = _batch.get_batch.return_value future = _future_exception(TransientError("oops")) batch.add.return_value = future _global_cache.return_value = mock.Mock( transient_errors=(TransientError,), strict_read=False, spec=("transient_errors", "strict_read"), ) with warnings.catch_warnings(record=True) as logged: assert _cache.global_get(b"foo").result() is None assert len(logged) == 1 _batch.get_batch.assert_called_once_with(_cache._GlobalCacheGetBatch) batch.add.assert_called_once_with(b"foo")
def test_global_get_with_error_strict_retry(_batch, _global_cache, sleep): class TransientError(Exception): pass sleep.return_value = future_result(None) batch = _batch.get_batch.return_value batch.add.side_effect = [ _future_exception(TransientError("oops")), future_result("hi mom!"), ] _global_cache.return_value = mock.Mock( transient_errors=(TransientError,), strict_read=True, spec=("transient_errors", "strict_read"), ) assert _cache.global_get(b"foo").result() == "hi mom!" _batch.get_batch.assert_called_with(_cache._GlobalCacheGetBatch) batch.add.assert_called_with(b"foo")
def test_global_get(_batch): batch = _batch.get_batch.return_value assert _cache.global_get(b"foo") is batch.add.return_value _batch.get_batch.assert_called_once_with(_cache._GlobalCacheGetBatch) batch.add.assert_called_once_with(b"foo")