Example #1
0
 def _log_error_rollback_and_raise(self, e: Exception, query_log_line: str):
     self._log_database_notifications()
     error_string = single_line_with_single_spaces(e)
     logger.error("Rolling back query=\'%s\' due to error: \'%s\'" % (query_log_line, error_string))
     self.rollback()
     if isinstance(e, PostgresDBError):
         # just re-raise our PostgresDBError
         raise
     else:
         # wrap original error in PostgresDBQueryExecutionError
         raise PostgresDBQueryExecutionError("Could not execute query '%s' error=%s" % (query_log_line, error_string))
Example #2
0
    def connect(self):
        if self.is_connected:
            logger.debug("already connected to database: %s", self)
            return

        for retry_cntr in range(self.__num_connect_retries+1):
            try:
                logger.debug("connecting to database: %s", self)

                self._connection = psycopg2.connect(host=self._dbcreds.host,
                                                    user=self._dbcreds.user,
                                                    password=self._dbcreds.password,
                                                    database=self._dbcreds.database,
                                                    port=self._dbcreds.port,
                                                    connect_timeout=5)

                if self._connection:
                    self._cursor = self._connection.cursor(cursor_factory=psycopg2.extras.RealDictCursor)

                    logger.info("connected to database: %s", self)

                    # see http://initd.org/psycopg/docs/connection.html#connection.notices
                    # try to set the notices attribute with a non-list collection,
                    # so we can log more than 50 messages. Is only available since 2.7, so encapsulate in try/except.
                    try:
                        self._connection.notices = collections.deque()
                    except TypeError:
                        logger.warning("Cannot overwrite self._connection.notices with a deque... only max 50 notifications available per query. (That's ok, no worries.)")

                    # we have a proper connection, so return
                    return
            except psycopg2.DatabaseError as dbe:
                error_string = single_line_with_single_spaces(dbe)
                logger.error(error_string)

                if self._is_recoverable_connection_error(dbe):
                    # try to reconnect on connection-like-errors
                    if retry_cntr == self.__num_connect_retries:
                        raise PostgresDBConnectionError("Error while connecting to %s. error=%s" % (self, error_string))

                    logger.info('retrying to connect to %s in %s seconds', self.database, self.__connect_retry_interval)
                    time.sleep(self.__connect_retry_interval)
                else:
                    # non-connection-error, raise generic PostgresDBError
                    raise PostgresDBError(error_string)
Example #3
0
    def _do_execute_query(self, query, qargs=None, fetch=FETCH_NONE):
        '''execute the query and reconnect upon OperationalError'''
        query_log_line = self._queryAsSingleLine(query, qargs)

        try:
            self.connect_if_needed()

            # log
            logger.debug('executing query: %s', query_log_line)

            # execute (and time it)
            start = datetime.utcnow()
            self._cursor.execute(query, qargs)
            elapsed = datetime.utcnow() - start
            elapsed_ms = 1000.0 * totalSeconds(elapsed)

            # log execution result
            logger.info('executed query in %.1fms%s yielding %s rows: %s', elapsed_ms,
                                                                           ' (SLOW!)' if elapsed_ms > 250 else '', # for easy log grep'ing
                                                                           self._cursor.rowcount,
                                                                           query_log_line)

            # log any notifications from within the database itself
            self._log_database_notifications()

            self._commit_selects_if_needed(query)

            # fetch and return results
            if fetch == FETCH_ONE:
                row = self._cursor.fetchone()
                return dict(row) if row is not None else None
            if fetch == FETCH_ALL:
                return [dict(row) for row in self._cursor.fetchall() if row is not None]
            return []

        except psycopg2.OperationalError as oe:
            if self._is_recoverable_connection_error(oe):
                raise PostgresDBConnectionError("Could not execute query due to connection errors. '%s' error=%s" %
                                                (query_log_line,
                                                 single_line_with_single_spaces(oe)))
            else:
                self._log_error_rollback_and_raise(oe, query_log_line)

        except Exception as e:
            self._log_error_rollback_and_raise(e, query_log_line)
Example #4
0
    def _send_task_status_notification(self, spec, new_status):
        """
        Sends a message about the task's status on the RA notification bus

        :param spec:    the task concerned
        :param new_status:  the task's status

        :raises Exception if sending the notification fails
        """
        #TODO can maybe move to Specification in the future? Logically it should be resource_assigner that sends the notification
        content = {
            'radb_id': spec.radb_id,
            'otdb_id': spec.otdb_id,
            'mom_id': spec.mom_id
        }
        subject = 'Task' + new_status[0].upper() + new_status[
            1:]  #TODO this is MAGIC, needs explanation!
        event_message = EventMessage(subject="%s.%s" %
                                     (DEFAULT_RA_NOTIFICATION_PREFIX, subject),
                                     content=content)

        logger.info('Sending notification %s: %s' %
                    (subject, single_line_with_single_spaces(content)))
        self.ra_notification_bus.send(event_message)
Example #5
0
 def onDataWritersFinished(self, msg_content):
     logger.info("received DataWritersFinished event: %s",
                 single_line_with_single_spaces(str(msg_content)))
     # signal that we're done waiting
     self._waiter.wait_event.set()
Example #6
0
 def onDataWritersStopped(self, msg_content):
     logger.info("received DataWritersStopped event: %s",
                 single_line_with_single_spaces(str(msg_content)))
Example #7
0
 def _queryAsSingleLine(query, qargs=None):
     line = ' '.join(single_line_with_single_spaces(query).split())
     if qargs:
         line = line % tuple(['\'%s\'' % a if isinstance(a, str) else a for a in qargs])
     return line
Example #8
0
    def handle_message(self, msg: LofarMessage):
        # try to handle an incoming message, and call the associated on<SomeMessage> method
        if not isinstance(msg, EventMessage):
            raise ValueError("%s: Ignoring non-EventMessage: %s" % (self.__class__.__name__, msg))

        stripped_subject = msg.subject.replace("%s." % DEFAULT_TBB_NOTIFICATION_PREFIX, '')

        logger.debug("TBBEventMessageHandler.handle_message: on%s content=%s", stripped_subject, single_line_with_single_spaces(str(msg.content)))

        if stripped_subject == 'DataWritersStarting':
            self.onDataWritersStarting(msg.content)
        elif stripped_subject == 'DataWritersStarted':
            self.onDataWritersStarted(msg.content)
        elif stripped_subject == 'DataWritersFinished':
            self.onDataWritersFinished(msg.content)
        elif stripped_subject == 'DataWritersStopping':
            self.onDataWritersStopping(msg.content)
        elif stripped_subject == 'DataWritersStopped':
            self.onDataWritersStopped(msg.content)
        else:
            raise ValueError("TBBEventMessageHandler.handleMessage: unknown subject: %s" % msg.subject)
Example #9
0
 def onError(self, msg_content):
     logger.info("%s.onError(%s)", self.__class__.__name__, single_line_with_single_spaces(msg_content))
Example #10
0
 def onCreatedInspectionPlots(self, msg_content):
     logger.info("%s.onCreatedInspectionPlots(%s)", self.__class__.__name__, single_line_with_single_spaces(msg_content))
Example #11
0
 def onConvertedBF2Hdf5(self, msg_content):
     logger.info("%s.onConvertedBF2Hdf5(%s)", self.__class__.__name__, single_line_with_single_spaces(msg_content))