def spawn_worker(self, provider, args, kwargs, context_data=None, handle_result=None): """ Spawn a worker thread for running the service method decorated with an entrypoint ``provider``. ``args`` and ``kwargs`` are used as arguments for the service method. ``context_data`` is used to initialize a ``WorkerContext``. ``handle_result`` is an optional function which may be passed in by the entrypoint provider. It is called with the result returned or error raised by the service method. If provided it must return a value for ``result`` and ``exc_info`` to propagate to dependencies; these may be different to those returned by the service method. """ if self._being_killed: _log.info("Worker spawn prevented due to being killed") raise ContainerBeingKilled() service = self.service_cls() worker_ctx = self.worker_ctx_cls( self, service, provider, args, kwargs, data=context_data) _log.debug('spawning %s', worker_ctx) gt = self._worker_pool.spawn(self._run_worker, worker_ctx, handle_result) self._active_threads.add(gt) gt.link(self._handle_thread_exited) return worker_ctx
def test_consume_provider(empty_config): container = Mock(spec=ServiceContainer) container.worker_ctx_cls = WorkerContext container.service_name = "service" container.config = empty_config worker_ctx = WorkerContext(container, None, DummyProvider()) spawn_worker = container.spawn_worker spawn_worker.return_value = worker_ctx queue_consumer = Mock() consume_provider = ConsumeProvider(queue=foobar_queue, requeue_on_error=False) consume_provider.queue_consumer = queue_consumer consume_provider.bind("name", container) message = Mock(headers={}) # test lifecycle consume_provider.prepare() queue_consumer.register_provider.assert_called_once_with(consume_provider) consume_provider.stop() queue_consumer.unregister_provider.assert_called_once_with( consume_provider) # test handling successful call queue_consumer.reset_mock() consume_provider.handle_message("body", message) handle_result = spawn_worker.call_args[1]['handle_result'] handle_result(worker_ctx, 'result') queue_consumer.ack_message.assert_called_once_with(message) # test handling failed call without requeue queue_consumer.reset_mock() consume_provider.requeue_on_error = False consume_provider.handle_message("body", message) handle_result = spawn_worker.call_args[1]['handle_result'] handle_result(worker_ctx, None, (Exception, Exception('Error'), "tb")) queue_consumer.ack_message.assert_called_once_with(message) # test handling failed call with requeue queue_consumer.reset_mock() consume_provider.requeue_on_error = True consume_provider.handle_message("body", message) handle_result = spawn_worker.call_args[1]['handle_result'] handle_result(worker_ctx, None, (Exception, Exception('Error'), "tb")) assert not queue_consumer.ack_message.called queue_consumer.requeue_message.assert_called_once_with(message) # test requeueing on ContainerBeingKilled (even without requeue_on_error) queue_consumer.reset_mock() consume_provider.requeue_on_error = False spawn_worker.side_effect = ContainerBeingKilled() consume_provider.handle_message("body", message) assert not queue_consumer.ack_message.called queue_consumer.requeue_message.assert_called_once_with(message)
def test_nova_rpc_provider(empty_config): rpc_consumer = Mock() message = Mock(headers={}) message_body = { 'method': 'method', 'args': { "arg": "arg_value" }, 'msg_id': 'msg_id', '_context_user_id': 'user_id' } class Service(object): def method(self, arg): pass container = Mock(spec=ServiceContainer) container.service_cls = Service container.worker_ctx_cls = WorkerContext container.service_name = "service" container.config = empty_config rpc_provider = NovaRpcProvider() rpc_provider.rpc_consumer = rpc_consumer rpc_provider.bind("method", container) container.spawn_worker.side_effect = ContainerBeingKilled() rpc_provider.handle_message(message_body, message) assert rpc_consumer.requeue_message.called