def test_lock_missing(_global_cache, _global_get, _global_watch, _global_compare_and_swap, uuid): lock = b".arandomuuid" _global_cache.return_value = mock.Mock( transient_errors=(), strict_write=False, spec=("transient_errors", "strict_write"), ) lock_value = _cache._LOCKED_FOR_WRITE + b".adifferentlock" _global_get.return_value = _future_result(lock_value) _global_watch.return_value = _future_result(None) _global_compare_and_swap.return_value = _future_result(True) with warnings.catch_warnings(record=True) as logged: assert _cache.global_unlock_for_write(b"key", lock).result() is None logged = [ warning for warning in logged if warning.category is RuntimeWarning ] assert len(logged) == 1 _global_get.assert_called_once_with(b"key") _global_watch.assert_not_called() _global_compare_and_swap.assert_not_called()
def test_not_last_time_fail_once(_global_cache, _global_get, _global_watch, _global_compare_and_swap, uuid): lock = b".arandomuuid" _global_cache.return_value = mock.Mock( transient_errors=(), strict_write=False, spec=("transient_errors", "strict_write"), ) new_lock_value = _cache._LOCKED_FOR_WRITE + b".whatevs" old_lock_value = new_lock_value + lock _global_get.return_value = _future_result(old_lock_value) _global_watch.return_value = _future_result(None) _global_compare_and_swap.side_effect = ( _future_result(False), _future_result(True), ) assert _cache.global_unlock_for_write(b"key", lock).result() is None _global_get.assert_has_calls([ mock.call(b"key"), mock.call(b"key"), ]) _global_watch.assert_has_calls([ mock.call(b"key", old_lock_value), mock.call(b"key", old_lock_value), ]) _global_compare_and_swap.assert_has_calls([ mock.call(b"key", new_lock_value, expires=64), mock.call(b"key", new_lock_value, expires=64), ])
def test_lock_overwritten(_global_cache, _global_get, _global_watch, _global_compare_and_swap, uuid): lock = b".arandomuuid" _global_cache.return_value = mock.Mock( transient_errors=(), strict_write=False, spec=("transient_errors", "strict_write"), ) lock_value = b"SOMERANDOMVALUE" _global_get.return_value = _future_result(lock_value) _global_watch.return_value = _future_result(None) _global_compare_and_swap.return_value = _future_result(True) with warnings.catch_warnings(record=True) as logged: assert _cache.global_unlock_for_write(b"key", lock).result() is None logged = [ warning for warning in logged if warning.category is RuntimeWarning ] assert len(logged) == 1 _global_get.assert_called_once_with(b"key") _global_watch.assert_called_once_with(b"key", lock_value) _global_compare_and_swap.assert_called_once_with(b"key", b"", expires=64)
def put(entity, options): """Store an entity in datastore. The entity can be a new entity to be saved for the first time or an existing entity that has been updated. Args: entity_pb (datastore.Entity): The entity to be stored. options (_options.Options): Options for this request. Returns: tasklets.Future: Result will be completed datastore key (datastore.Key) for the entity. """ context = context_module.get_context() use_global_cache = context._use_global_cache(entity.key, options) use_datastore = context._use_datastore(entity.key, options) if not (use_global_cache or use_datastore): raise TypeError("use_global_cache and use_datastore can't both be False") if not use_datastore and entity.key.is_partial: raise TypeError("Can't store partial keys when use_datastore is False") lock = None entity_pb = helpers.entity_to_protobuf(entity) cache_key = _cache.global_cache_key(entity.key) if use_global_cache and not entity.key.is_partial: if use_datastore: lock = yield _cache.global_lock_for_write(cache_key) else: expires = context._global_cache_timeout(entity.key, options) cache_value = entity_pb.SerializeToString() yield _cache.global_set(cache_key, cache_value, expires=expires) if use_datastore: transaction = context.transaction if transaction: batch = _get_commit_batch(transaction, options) else: batch = _batch.get_batch(_NonTransactionalCommitBatch, options) key_pb = yield batch.put(entity_pb) if key_pb: key = helpers.key_from_protobuf(key_pb) else: key = None if lock: if transaction: def callback(): _cache.global_unlock_for_write(cache_key, lock).result() context.call_on_transaction_complete(callback) else: yield _cache.global_unlock_for_write(cache_key, lock) raise tasklets.Return(key)
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 delete(key, options): """Delete an entity from Datastore. Deleting an entity that doesn't exist does not result in an error. The result is the same regardless. Args: key (datastore.Key): The key for the entity to be deleted. options (_options.Options): Options for this request. Returns: tasklets.Future: Will be finished when entity is deleted. Result will always be :data:`None`. """ context = context_module.get_context() use_global_cache = context._use_global_cache(key, options) use_datastore = context._use_datastore(key, options) transaction = context.transaction if use_global_cache: cache_key = _cache.global_cache_key(key) if use_datastore: if use_global_cache: lock = yield _cache.global_lock_for_write(cache_key) if transaction: batch = _get_commit_batch(transaction, options) else: batch = _batch.get_batch(_NonTransactionalCommitBatch, options) yield batch.delete(key) if use_global_cache: if transaction: def callback(): _cache.global_unlock_for_write(cache_key, lock).result() context.call_on_transaction_complete(callback) elif use_datastore: yield _cache.global_unlock_for_write(cache_key, lock) else: yield _cache.global_delete(cache_key)
def test_transient_error(_global_cache, _global_get, _global_watch, uuid): class TransientError(Exception): pass lock = b".arandomuuid" _global_cache.return_value = mock.Mock( transient_errors=(TransientError,), strict_write=False, spec=("transient_errors", "strict_write"), ) lock_value = _cache._LOCKED_FOR_WRITE + lock _global_get.return_value = _future_result(lock_value) _global_watch.return_value = _future_exception(TransientError()) assert _cache.global_unlock_for_write(b"key", lock).result() is None _global_get.assert_called_once_with(b"key") _global_watch.assert_called_once_with(b"key", lock_value)
def test_last_time( _global_cache, _global_get, _global_watch, _global_compare_and_swap, uuid ): lock = b".arandomuuid" _global_cache.return_value = mock.Mock( transient_errors=(), strict_write=False, spec=("transient_errors", "strict_write"), ) lock_value = _cache._LOCKED_FOR_WRITE + lock _global_get.return_value = _future_result(lock_value) _global_watch.return_value = _future_result(None) _global_compare_and_swap.return_value = _future_result(True) assert _cache.global_unlock_for_write(b"key", lock).result() is None _global_get.assert_called_once_with(b"key") _global_watch.assert_called_once_with(b"key", lock_value) _global_compare_and_swap.assert_called_once_with(b"key", b"", expires=32)
def test_no_value_in_cache(_global_cache, _global_get, global_set_if_not_exists, uuid): lock = b".arandomuuid" _global_cache.return_value = mock.Mock( transient_errors=(), strict_write=False, spec=("transient_errors", "strict_write"), ) _global_get.return_value = _future_result(None) global_set_if_not_exists.return_value = _future_result(True) with warnings.catch_warnings(record=True) as logged: assert _cache.global_unlock_for_write(b"key", lock).result() is None logged = [ warning for warning in logged if warning.category is RuntimeWarning ] assert len(logged) == 1 _global_get.assert_called_once_with(b"key") global_set_if_not_exists.assert_not_called()
def callback(): _cache.global_unlock_for_write(cache_key, lock).result()