Beispiel #1
0
    def _wait_for_results(self) -> RemoteResult:
        """Wait for results off the queue. If there is an exception raised,
        raise an appropriate RPC exception.

        This does not handle joining, but does terminate the process if it
        timed out.
        """
        if (self.subscriber is None or self.started is None
                or self.process is None):
            raise dbt.exceptions.InternalException(
                '_wait_for_results() called before handle()')

        try:
            msg = self.subscriber.dispatch_until_exit(
                started=self.started,
                timeout=self.timeout,
            )
        except dbt.exceptions.Exception as exc:
            raise dbt_error(exc)
        except Exception as exc:
            raise server_error(exc)
        if isinstance(msg, QueueErrorMessage):
            raise RPCException.from_error(msg.error)
        elif isinstance(msg, QueueTimeoutMessage):
            if not self._single_threaded:
                self.process.terminate()
            raise timeout_error(self.timeout)
        elif isinstance(msg, QueueResultMessage):
            return msg.result
        else:
            raise dbt.exceptions.InternalException(
                'Invalid message type {} (result={})'.format(msg))
 def handle_exception(self, e, ctx):
     logger.debug('Got an exception: {}'.format(e), exc_info=True)
     if isinstance(e, dbt.exceptions.Exception):
         if isinstance(e, dbt.exceptions.RuntimeException):
             e.add_node(ctx.node)
         return dbt_error(e)
     elif isinstance(e, RPCException):
         return e
     else:
         return server_error(e)
Beispiel #3
0
    def task_exec(self) -> None:
        """task_exec runs first inside the child process"""
        signal.signal(signal.SIGTERM, sigterm_handler)
        # the first thing we do in a new process: push logging back over our
        # queue
        handler = QueueLogHandler(self.queue)
        with handler.applicationbound():
            self._spawn_setup()
            # copy threads over into our credentials, if it exists and is set.
            # some commands, like 'debug', won't have a threads value at all.
            if getattr(self.task.args, 'threads', None) is not None:
                self.task.config.threads = self.task.args.threads
            rpc_exception = None
            result = None
            try:
                result = self.task.handle_request()
            except RPCException as exc:
                rpc_exception = exc
            except dbt.exceptions.RPCKilledException as exc:
                # do NOT log anything here, you risk triggering a deadlock on
                # the queue handler we inserted above
                rpc_exception = dbt_error(exc)
            except dbt.exceptions.Exception as exc:
                logger.debug('dbt runtime exception', exc_info=True)
                rpc_exception = dbt_error(exc)
            except Exception as exc:
                with OutputHandler(sys.stderr).applicationbound():
                    logger.error('uncaught python exception', exc_info=True)
                rpc_exception = server_error(exc)

            # put whatever result we got onto the queue as well.
            if rpc_exception is not None:
                handler.emit_error(rpc_exception.error)
            elif result is not None:
                handler.emit_result(result)
            else:
                error = dbt_error(
                    dbt.exceptions.InternalException(
                        'after request handling, neither result nor error is None!'
                    ))
                handler.emit_error(error.error)
Beispiel #4
0
 def handle_completed(self):
     # killed handlers don't get a result.
     if self.handler.state != TaskHandlerState.Killed:
         if self.handler.result is None:
             # there wasn't an error before, but there sure is one now
             self.handler.error = dbt_error(
                 dbt.exceptions.InternalException(
                     'got an invalid result=None, but state was {}'.format(
                         self.handler.state)))
         elif self.handler.task.interpret_results(self.handler.result):
             self.handler.state = TaskHandlerState.Success
         else:
             self.handler.state = TaskHandlerState.Failed
     self.set_end()
Beispiel #5
0
 def handle_error(self, exc_type, exc_value, exc_tb) -> bool:
     if isinstance(exc_value, RPCException):
         self.handler.error = exc_value
     elif isinstance(exc_value, dbt.exceptions.Exception):
         self.handler.error = dbt_error(exc_value)
     else:
         # we should only get here if we got a BaseException that is not
         # an Exception (we caught those in _wait_for_results), or a bug
         # in get_result's call stack. Either way, we should set an
         # error so we can figure out what happened on thread death
         self.handler.error = server_error(exc_value)
     if self.handler.state != TaskHandlerState.Killed:
         self.handler.state = TaskHandlerState.Error
     self.set_end()
     return False
Beispiel #6
0
    def handle(self, kwargs: Dict[str, Any]) -> Dict[str, Any]:
        self.started = datetime.utcnow()
        self.state = TaskHandlerState.Initializing
        self.task_kwargs = kwargs

        with SetArgsStateHandler(self):
            # this will raise a TypeError if you provided bad arguments.
            self.task_params = self._collect_parameters()
            self.task.set_args(self.task_params)
            # now that we have called set_args, we can figure out our flags
            flags: RemoteMethodFlags = self.task.get_flags()
            if RemoteMethodFlags.RequiresConfigReloadBefore in flags:
                # tell the manager to reload the config.
                self.manager.reload_config()
            # set our task config to the version on our manager now. RPCCLi
            # tasks use this to set their `real_task`.
            self.task.set_config(self.manager.config)
            if self.task_params is None:  # mypy appeasement
                raise dbt.exceptions.InternalException(
                    'Task params set to None!')

            if RemoteMethodFlags.Builtin in flags:
                # bypass the queue, logging, etc: Straight to the method
                return self.task.handle_request()

        self.subscriber = QueueSubscriber(dbt.flags.MP_CONTEXT.Queue())
        self.process = BootstrapProcess(self.task, self.subscriber.queue)

        if RemoteMethodFlags.BlocksManifestTasks in flags:
            # got a request to do some compiling, but we already are!
            if not self.manager.set_parsing():
                raise dbt_error(dbt.exceptions.RPCCompiling())

        if self._single_threaded:
            # all requests are synchronous in single-threaded mode. No need to
            # create a process...
            return self.handle_singlethreaded(kwargs, flags)

        self.start()
        return {'request_token': str(self.task_id)}
Beispiel #7
0
 def __init__(self, exception: dbt.exceptions.Exception):
     self.exception = dbt_error(exception)
Beispiel #8
0
 def compilation_error(self, *args, **kwargs):
     """Raise an RPC exception to trigger the error handler."""
     raise dbt_error(dbt.exceptions.RPCLoadException(self.last_parse.error))
Beispiel #9
0
 def currently_compiling(self, *args, **kwargs):
     """Raise an RPC exception to trigger the error handler."""
     raise dbt_error(dbt.exceptions.RPCCompiling('compile in progress'))
Beispiel #10
0
    def handle_request(self) -> PollResult:
        if self.params is None:
            raise dbt.exceptions.InternalException('Poll: params not set')
        task_id = self.params.request_token
        task: RequestTaskHandler = self.task_manager.get_request(task_id)

        task_logs: List[LogMessage] = []
        if self.params.logs:
            task_logs = task.logs[self.params.logs_start:]

        # Get a state and store it locally so we ignore updates to state,
        # otherwise things will get confusing. States should always be
        # "forward-compatible" so if the state has transitioned to error/result
        # but we aren't there yet, the logs will still be valid.

        timing = task.make_task_timing(datetime.utcnow())
        state = timing.state
        if state <= TaskHandlerState.Running:
            return PollInProgressResult(
                tags=task.tags,
                logs=task_logs,
                state=timing.state,
                start=timing.start,
                end=timing.end,
                elapsed=timing.elapsed,
            )
        elif state == TaskHandlerState.Error:
            err = task.error
            if err is None:
                exc = dbt.exceptions.InternalException(
                    f'At end of task {task_id}, error state but error is None')
                raise RPCException.from_error(
                    dbt_error(exc, logs=_dict_logs(task_logs)))
            # the exception has logs already attached from the child, don't
            # overwrite those
            raise err
        elif state in (TaskHandlerState.Success, TaskHandlerState.Failed):

            if task.result is None:
                exc = dbt.exceptions.InternalException(
                    f'At end of task {task_id}, state={state} but result is '
                    'None')
                raise RPCException.from_error(
                    dbt_error(exc, logs=_dict_logs(task_logs)))
            return poll_complete(timing=timing,
                                 result=task.result,
                                 tags=task.tags,
                                 logs=task_logs)
        elif state == TaskHandlerState.Killed:
            return PollKilledResult(
                tags=task.tags,
                logs=task_logs,
                state=timing.state,
                start=timing.start,
                end=timing.end,
                elapsed=timing.elapsed,
            )
        else:
            exc = dbt.exceptions.InternalException(
                f'Got unknown value state={state} for task {task_id}')
            raise RPCException.from_error(
                dbt_error(exc, logs=_dict_logs(task_logs)))