예제 #1
0
    def _on_request(self, message):
        log = logging.getLogger(__name__)
        try:
            task = task_pb2.Task()
            try:
                # `body` is of unicode type, but we need str type for
                # `ParseFromString()` to work.  It seems to work.
                # Maybe kombu estimate that, without any information,
                # the body should be something as json, and thus a
                # unicode string.  On the c++ side, I didn't manage to
                # find a way to give a content-type or something like
                # that.
                body = str(message.payload)
                task.ParseFromString(body)
            except DecodeError as e:
                log.warn('invalid protobuf: {}'.format(str(e)))
                return

            log.info('Getting a full feed publication request', extra={'task': task})
            if task.action != task_pb2.LOAD_REALTIME or not task.load_realtime:
                return
            start_datetime = datetime.utcnow()
            begin_date = None
            end_date = None
            if hasattr(task.load_realtime, "begin_date"):
                if task.load_realtime.begin_date:
                    begin_date = str_to_date(task.load_realtime.begin_date)

            if hasattr(task.load_realtime, "end_date"):
                if task.load_realtime.end_date:
                    end_date = str_to_date(task.load_realtime.end_date)
            feed = convert_to_gtfsrt(TripUpdate.find_by_contributor_period(task.load_realtime.contributors,
                                                                           begin_date,
                                                                           end_date),
                                     gtfs_realtime_pb2.FeedHeader.FULL_DATASET)

            feed_str = feed.SerializeToString()
            log.info('Starting of full feed publication {}, {}'.format(len(feed_str), task), extra={'size': len(feed_str), 'task': task})
            # http://docs.celeryproject.org/projects/kombu/en/latest/userguide/producers.html#bypassing-routing-by-using-the-anon-exchange
            self.producer.publish(feed_str,
                                  routing_key=task.load_realtime.queue_name,
                                  retry=True,
                                  retry_policy={
                                      'interval_start': 0,  # First retry immediately,
                                      'interval_step': 2,   # then increase by 2s for every retry.
                                      'interval_max': 10,   # but don't exceed 10s between retries.
                                      'max_retries':  self.max_retries,     # give up after 10 (by default) tries.
                                      })
            duration = (datetime.utcnow() - start_datetime).total_seconds()
            log.info('End of full feed publication', extra={'duration': duration, 'task': task})
            record_call('Full feed publication', size=len(feed_str), routing_key=task.load_realtime.queue_name,
                        duration=duration, trip_update_count=len(feed.entity),
                        contributor=task.load_realtime.contributors)
        finally:
            db.session.remove()
예제 #2
0
def handle(proto, navitia_wrapper, contributor):
    data = str(proto)  # temp, for the moment, we save the protobuf as text
    rt_update = make_rt_update(data, 'gtfs-rt', contributor=contributor)
    start_datetime = datetime.datetime.utcnow()
    try:
        trip_updates = KirinModelBuilder(navitia_wrapper,
                                         contributor).build(rt_update,
                                                            data=proto)
        record_call('OK', contributor=contributor)
    except KirinException as e:
        rt_update.status = 'KO'
        rt_update.error = e.data['error']
        model.db.session.add(rt_update)
        model.db.session.commit()
        record_call('failure', reason=str(e), contributor=contributor)
        raise
    except Exception as e:
        rt_update.status = 'KO'
        rt_update.error = e.message
        model.db.session.add(rt_update)
        model.db.session.commit()
        record_call('failure', reason=str(e), contributor=contributor)
        raise

    real_time_update, log_dict = core.handle(rt_update, trip_updates,
                                             contributor)
    duration = (datetime.datetime.utcnow() - start_datetime).total_seconds()
    log_dict.update({
        'duration':
        duration,
        'input_timestamp':
        datetime.datetime.utcfromtimestamp(proto.header.timestamp)
    })
    record_call('Simple feed publication', **log_dict)
    logging.getLogger(__name__).info('Simple feed publication', extra=log_dict)
예제 #3
0
파일: model_maker.py 프로젝트: prhod/kirin
def handle(proto, navitia_wrapper, contributor):
    data = str(proto)  # temp, for the moment, we save the protobuf as text
    rt_update = make_rt_update(data, 'gtfs-rt', contributor=contributor)
    start_datetime = datetime.datetime.utcnow()
    try:
        trip_updates = KirinModelBuilder(navitia_wrapper,
                                         contributor).build(rt_update,
                                                            data=proto)
        record_call('OK', contributor=contributor)
    except KirinException as e:
        rt_update.status = 'KO'
        rt_update.error = e.data['error']
        model.db.session.add(rt_update)
        model.db.session.commit()
        record_call('failure', reason=str(e), contributor=contributor)
        raise
    except Exception as e:
        rt_update.status = 'KO'
        rt_update.error = e.message
        model.db.session.add(rt_update)
        model.db.session.commit()
        record_call('failure', reason=str(e), contributor=contributor)
        raise

    real_time_update, log_dict = core.handle(rt_update, trip_updates,
                                             contributor)

    # After merging trip_updates information of gtfs-rt, navitia and kirin database, if there is no new information
    # destinated to navitia, update real_time_update with status = 'KO' and a proper error message.
    if not real_time_update.trip_updates and real_time_update.status == 'OK':
        real_time_update.status = 'KO'
        real_time_update.error = 'No new information destinated to navitia for this gtfs-rt ' \
                                 'with timestamp: {}'.format(proto.header.timestamp)
        logging.getLogger(__name__).error(
            'No new information destinated to navitia for this gtfs-rt '
            'with timestamp: {}'.format(proto.header.timestamp))
        model.db.session.add(rt_update)
        model.db.session.commit()
    duration = (datetime.datetime.utcnow() - start_datetime).total_seconds()
    log_dict.update({
        'duration':
        duration,
        'input_timestamp':
        datetime.datetime.utcfromtimestamp(proto.header.timestamp)
    })
    record_call('Simple feed publication', **log_dict)
    logging.getLogger(__name__).info('Simple feed publication', extra=log_dict)
예제 #4
0
    def process_post(self, input_raw, contributor_type, is_new_complete=False):

        # create a raw rt_update obj, save the raw_input into the db
        rt_update = make_rt_update(input_raw,
                                   contributor_type,
                                   contributor=self.contributor)
        start_datetime = datetime.utcnow()
        try:
            # assuming UTF-8 encoding for all input
            rt_update.raw_data = rt_update.raw_data.encode('utf-8')

            # raw_input is interpreted
            trip_updates = self.builder(self.navitia_wrapper,
                                        self.contributor).build(rt_update)
            record_call('OK', contributor=self.contributor)
        except KirinException as e:
            rt_update.status = 'KO'
            rt_update.error = e.data['error']
            model.db.session.add(rt_update)
            model.db.session.commit()
            record_call('failure', reason=str(e), contributor=self.contributor)
            raise
        except Exception as e:
            rt_update.status = 'KO'
            rt_update.error = e.message
            model.db.session.add(rt_update)
            model.db.session.commit()
            record_call('failure', reason=str(e), contributor=self.contributor)
            raise

        _, log_dict = core.handle(rt_update,
                                  trip_updates,
                                  self.contributor,
                                  is_new_complete=is_new_complete)
        duration = (datetime.utcnow() - start_datetime).total_seconds()
        log_dict.update({'duration': duration})
        record_call('Simple feed publication', **log_dict)
        logging.getLogger(__name__).info('Simple feed publication',
                                         extra=log_dict)

        return 'OK', 200
예제 #5
0
    def post(self):
        raw_xml = get_ire(flask.globals.request)

        # create a raw ire obj, save the raw_xml into the db
        rt_update = make_rt_update(raw_xml,
                                   'ire',
                                   contributor=self.contributor)
        start_datetime = datetime.utcnow()
        try:
            # assuming UTF-8 encoding for all ire input
            rt_update.raw_data = rt_update.raw_data.encode('utf-8')

            # raw_xml is interpreted
            trip_updates = KirinModelBuilder(self.navitia_wrapper,
                                             self.contributor).build(rt_update)
            record_call('OK', contributor=self.contributor)
        except KirinException as e:
            rt_update.status = 'KO'
            rt_update.error = e.data['error']
            model.db.session.add(rt_update)
            model.db.session.commit()
            record_call('failure', reason=str(e), contributor=self.contributor)
            raise
        except Exception as e:
            rt_update.status = 'KO'
            rt_update.error = e.message
            model.db.session.add(rt_update)
            model.db.session.commit()
            record_call('failure', reason=str(e), contributor=self.contributor)
            raise

        _, log_dict = core.handle(rt_update, trip_updates,
                                  current_app.config['CONTRIBUTOR'])
        duration = (datetime.utcnow() - start_datetime).total_seconds()
        log_dict.update({'duration': duration})
        record_call('Simple feed publication', **log_dict)
        logging.getLogger(__name__).info('Simple feed publication',
                                         extra=log_dict)

        return 'OK', 200
예제 #6
0
def wrap_build(builder, input_raw):
    """
    Function wrapping the processing of realtime information of an external feed
    This manages errors/logger/newrelic
    :param builder: the KirinModelBuilder to be called (must inherit from abstract_builder.AbstractKirinModelBuilder)
    :param input_raw: the feed to process
    """
    contributor = builder.contributor
    start_datetime = datetime.datetime.utcnow()
    rt_update = None
    log_dict = {"contributor": contributor.id}
    record_custom_parameter("contributor", contributor.id)
    status = "OK"
    log_dict.update({"input_feed_size": sys.getsizeof(input_raw)})

    try:
        # create a raw rt_update obj, save the raw_input into the db
        rt_update, rtu_log_dict = builder.build_rt_update(input_raw)
        log_dict.update(rtu_log_dict)

        # raw_input is interpreted
        trip_updates, tu_log_dict = builder.build_trip_updates(rt_update)
        log_dict.update(tu_log_dict)

        # finally confront to previously existing information (base_schedule, previous real-time)
        _, handler_log_dict = handle(builder, rt_update, trip_updates)
        log_dict.update(handler_log_dict)

    except Exception as e:
        status = "warning" if is_only_warning_exception(e) else "failure"
        allow_reprocess = is_reprocess_allowed(e)

        if rt_update is not None:
            error = e.data["error"] if (isinstance(e, KirinException)
                                        and "error" in e.data) else str(e)
            set_rtu_status_ko(rt_update,
                              error,
                              is_reprocess_same_data_allowed=allow_reprocess)
            db_commit(rt_update)
        else:
            # rt_update is not built, make sure reprocess is allowed
            allow_reprocess_same_data(contributor.id)

        log_dict.update({"exc_summary": str(e), "reason": str(e)})

        record_custom_parameter(
            "reason", str(e))  # using __str__() here to have complete details
        # Re-raise all exceptions (as flask manages exceptions for output)
        # try/except mechanism must wrap calls to this wrap_build() function (auto. for flask)
        # See CONTRIBUTING.md for newrelic's error filtering
        raise

    finally:
        if rt_update is not None and rt_update.status == "pending":
            rt_update.status = "OK" if (status == "OK") else "KO"
            db_commit(rt_update)
        log_dict.update({
            "duration":
            (datetime.datetime.utcnow() - start_datetime).total_seconds()
        })
        record_call("kirin_feed_process_info", status, **log_dict)
        if status == "OK":
            logging.getLogger(__name__).info(status, extra=log_dict)
        elif status == "warning":
            logging.getLogger(__name__).warning(status, extra=log_dict)
        else:
            logging.getLogger(__name__).error(status, extra=log_dict)
예제 #7
0
def wrap_build(builder, input_raw):
    """
    Function wrapping the processing of realtime information of an external feed
    This manages errors/logger/newrelic
    :param builder: the KirinModelBuilder to be called
    :param input_raw: the feed to process
    """
    contributor = builder.contributor
    start_datetime = datetime.utcnow()
    rt_update = None
    log_dict = {"contributor": contributor.id}
    status = "OK"

    try:
        # create a raw rt_update obj, save the raw_input into the db
        rt_update, rtu_log_dict = builder.build_rt_update(input_raw)
        log_dict.update(rtu_log_dict)

        # raw_input is interpreted
        trip_updates, tu_log_dict = builder.build_trip_updates(rt_update)
        log_dict.update(tu_log_dict)

        # finally confront to previously existing information (base_schedule, previous real-time)
        _, handler_log_dict = core.handle(rt_update, trip_updates,
                                          contributor.id,
                                          builder.is_new_complete)
        log_dict.update(handler_log_dict)

    except Exception as e:
        status = "failure"
        allow_reprocess = True
        if is_invalid_input_exception(e):
            status = "warning"  # Kirin did his job correctly if the input is invalid and rejected
            allow_reprocess = False  # reprocess is useless if input is invalid

        if rt_update is not None:
            error = e.data["error"] if (isinstance(e, KirinException)
                                        and "error" in e.data) else e.message
            set_rtu_status_ko(rt_update,
                              error,
                              is_reprocess_same_data_allowed=allow_reprocess)
            model.db.session.add(rt_update)
            model.db.session.commit()
        else:
            # rt_update is not built, make sure reprocess is allowed
            allow_reprocess_same_data(contributor.id)

        log_dict.update({"exc_summary": six.text_type(e), "reason": e})

        record_custom_parameter(
            "reason", e)  # using __str__() here to have complete details
        raise  # filters later for APM (auto.)

    finally:
        log_dict.update(
            {"duration": (datetime.utcnow() - start_datetime).total_seconds()})
        record_call(status, **log_dict)
        if status == "OK":
            logging.getLogger(__name__).info(status, extra=log_dict)
        elif status == "warning":
            logging.getLogger(__name__).warning(status, extra=log_dict)
        else:
            logging.getLogger(__name__).error(status, extra=log_dict)
예제 #8
0
    def _on_request(self, message):
        log = logging.getLogger(__name__)
        status = "OK"
        log_dict = {}
        start_datetime = datetime.utcnow()
        try:
            task = task_pb2.Task()
            try:
                # `body` is a string, but we need binary type for
                # `ParseFromString()` to work.  It seems to work.
                body = bytes(message.payload, encoding="utf-8")
                task.ParseFromString(body)
            except DecodeError as e:
                log.warning("invalid protobuf: {}".format(str(e)))
                return

            log.info("Getting a full feed publication request",
                     extra={"task": task})
            if task.action != task_pb2.LOAD_REALTIME or not task.load_realtime:
                return
            begin_date = None
            end_date = None
            if hasattr(task.load_realtime,
                       "begin_date") and task.load_realtime.begin_date:
                begin_date = str_to_date(task.load_realtime.begin_date)

            if hasattr(task.load_realtime,
                       "end_date") and task.load_realtime.end_date:
                end_date = str_to_date(task.load_realtime.end_date)
            feed = convert_to_gtfsrt(
                TripUpdate.find_by_contributor_period(
                    task.load_realtime.contributors, begin_date, end_date),
                gtfs_realtime_pb2.FeedHeader.FULL_DATASET,
            )

            feed_str = feed.SerializeToString()
            log_dict.update({
                "contributors": task.load_realtime.contributors,
                "routing_key": task.load_realtime.queue_name,
                "output_trip_update_count": len(feed.entity),
                "output_feed_size": sys.getsizeof(feed_str),
            })
            log.info(
                "Starting of full feed publication {}, {}".format(
                    len(feed_str), task),
                extra={
                    "size": len(feed_str),
                    "task": task
                },
            )
            record_custom_parameter("contributors",
                                    task.load_realtime.contributors)
            # http://docs.celeryproject.org/projects/kombu/en/latest/userguide/producers.html#bypassing-routing-by-using-the-anon-exchange
            self.producer.publish(
                feed_str,
                routing_key=task.load_realtime.queue_name,
                retry=True,
                retry_policy={
                    "interval_start": 0,  # First retry immediately,
                    "interval_step": 2,  # then increase by 2s for every retry.
                    "interval_max":
                    10,  # but don't exceed 10s between retries.
                    "max_retries":
                    self.max_retries,  # give up after 10 (by default) tries.
                },
            )
        except Exception as e:
            status = "failure"
            log_dict.update({"reason": str(e)})
            record_custom_parameter("reason", str(e))
        finally:
            duration = (datetime.utcnow() - start_datetime).total_seconds()
            log_dict.update({"duration": duration})
            record_call("kirin_reload_info", status, **log_dict)
            log.info("End of full feed publication",
                     extra={
                         "duration": duration,
                         "task": task
                     })
            db.session.remove()
        return log_dict