async def test_destroy_lock_manager(self, lock_manager_redis_patched): lock_manager, redis = lock_manager_redis_patched lock_manager.unlock = MagicMock(side_effect=LockError('Can not lock')) await lock_manager.lock("resource", 1.0) await lock_manager.destroy() redis.clear_connections.assert_called_once_with()
async def test_extend_lock_error(self, lock_manager_redis_patched, locked_lock): lock_manager, redis = lock_manager_redis_patched lock = await lock_manager.lock('resource') redis.set_lock = CoroutineMock(side_effect=LockError('Can not lock')) with pytest.raises(LockError): await lock_manager.extend(lock)
async def test_lock_expire_retries(self, lock_manager_redis_patched, locked_lock): lock_manager, redis = lock_manager_redis_patched redis.set_lock = CoroutineMock(side_effect=[ LockError('Can not lock'), LockError('Can not lock'), LockError('Can not lock') ]) with pytest.raises(LockError): await lock_manager.lock('resource', 1.0) await real_sleep(0.1) # wait until cleaning is completed calls = [ call('resource', ANY, 1.0), call('resource', ANY, 1.0), call('resource', ANY, 1.0) ] redis.set_lock.assert_has_calls(calls) redis.unset_lock.assert_called_once_with('resource', ANY)
async def test_lock_one_retry(self, lock_manager_redis_patched, locked_lock): lock_manager, redis = lock_manager_redis_patched redis.set_lock = CoroutineMock( side_effect=[LockError('Can not lock'), 0.001]) lock = await lock_manager.lock('resource') calls = [call('resource', ANY), call('resource', ANY)] redis.set_lock.assert_has_calls(calls) redis.unset_lock.assert_not_called() assert lock.resource == 'resource' assert lock.id == ANY assert lock.valid is True
async def handle_message(self, message: SQSMessage) -> tuple: """ Method that hold logic to handle a certain type of mesage :param message: Message to handle :raises LockError: If lock could not be acquired for the message :raises Exception: General exception handler attaches the message and message handler :return: Returns a tuple of the message and result of the handler """ lock = None try: lock = await self.lock_manager.lock( self.lock_key.format(message_id=message.message_id) ) if not lock.valid: raise LockError( f"Could not acquire lock for {message.message_id}" ) if message.event in self.handlers: handler = self.handlers[message.event] elif default in self.handlers: handler = self.handlers[default] else: raise KeyError(f"{message.event} handler not found!") except Exception as e: e.message = message e.handler = None raise e else: try: result = handler(message) if isawaitable(result): result = await result return message, result except Exception as e: e.message = message e.handler = handler raise e finally: if lock: await self.lock_manager.unlock(lock)
async def test_lock_one_retry(self, lock_manager_redis_patched, locked_lock): lock_manager, redis = lock_manager_redis_patched future = asyncio.Future() future.set_result(0.001) redis.set_lock = MagicMock(side_effect=[ LockError('Can not lock'), future, ]) lock = await lock_manager.lock('resource', 1.0) calls = [call('resource', ANY, 1.0), call('resource', ANY, 1.0)] redis.set_lock.assert_has_calls(calls) redis.unset_lock.assert_not_called() assert lock.resource == 'resource' assert lock.id == ANY assert lock.valid is True