def pgespresso_stop_backup(self, backup_label): """ Execute a pgespresso_stop_backup This method returns a dictionary containing the following data: * end_wal * timestamp :param str backup_label: backup label as returned by pgespress_start_backup :rtype: psycopg2.extras.DictRow """ try: conn = self.connect() # Issue a rollback to release any unneeded lock conn.rollback() cur = conn.cursor(cursor_factory=DictCursor) cur.execute( "SELECT pgespresso_stop_backup(%s) AS end_wal, " "now() AS timestamp", (backup_label, )) return cur.fetchone() except (PostgresConnectionError, psycopg2.Error) as e: msg = "Error issuing pgespresso_stop_backup() command: %s" % ( str(e).strip()) _logger.debug(msg) raise PostgresException( '%s\n' 'HINT: You might have to manually execute ' 'pgespresso_abort_backup() on your PostgreSQL ' 'server' % msg)
def stop_exclusive_backup(self): """ Calls pg_stop_backup() on the PostgreSQL server This method returns a dictionary containing the following data: * location * file_name * file_offset * timestamp :rtype: psycopg2.extras.DictRow """ try: conn = self.connect() # Rollback to release the transaction, as the pg_stop_backup # invocation could will wait until the current WAL file is shipped conn.rollback() # Stop the backup cur = conn.cursor(cursor_factory=DictCursor) cur.execute("SELECT location, " "(pg_xlogfile_name_offset(location)).*, " "now() AS timestamp " "FROM pg_stop_backup() AS location") return cur.fetchone() except (PostgresConnectionError, psycopg2.Error) as e: msg = "Error issuing pg_stop_backup command: %s" % str(e).strip() _logger.debug(msg) raise PostgresException( 'Cannot terminate exclusive backup. ' 'You might have to manually execute pg_stop_backup ' 'on your PostgreSQL server')
def pgespresso_start_backup(self, backup_label): """ Execute a pgespresso_start_backup :param str backup_label: label for the backup :rtype: tuple """ try: conn = self.connect() # Issue a rollback to release any unneeded lock conn.rollback() # Start the concurrent backup cur = conn.cursor(cursor_factory=DictCursor) cur.execute( 'SELECT pgespresso_start_backup(%s,%s) AS backup_label, ' 'now() AS timestamp', (backup_label, self.config.immediate_checkpoint)) start_row = cur.fetchone() # Rollback to release the transaction, as the connection # is to be retained until the end of backup conn.rollback() return start_row except (PostgresConnectionError, psycopg2.Error) as e: msg = "pgespresso_start_backup(): %s" % str(e).strip() _logger.debug(msg) raise PostgresException(msg)
def start_exclusive_backup(self, label): """ Calls pg_start_backup() on the PostgreSQL server This method returns a dictionary containing the following data: * location * file_name * file_offset * timestamp :param str label: descriptive string to identify the backup :rtype: psycopg2.extras.DictRow """ try: conn = self.connect() # Rollback to release the transaction, as the pg_start_backup # invocation can last up to PostgreSQL's checkpoint_timeout conn.rollback() # Start an exclusive backup cur = conn.cursor(cursor_factory=DictCursor) if self.server_version < 80400: cur.execute( "SELECT location, " "({pg_walfile_name_offset}(location)).*, " "now() AS timestamp " "FROM pg_start_backup(%s) AS location".format( **self.name_map), (label, )) else: cur.execute( "SELECT location, " "({pg_walfile_name_offset}(location)).*, " "now() AS timestamp " "FROM pg_start_backup(%s,%s) AS location".format( **self.name_map), (label, self.config.immediate_checkpoint)) start_row = cur.fetchone() # Rollback to release the transaction, as the connection # is to be retained until the end of backup conn.rollback() return start_row except (PostgresConnectionError, psycopg2.Error) as e: msg = "pg_start_backup(): %s" % str(e).strip() _logger.debug(msg) raise PostgresException(msg)
def start_exclusive_backup(self, backup_label): """ Calls pg_start_backup() on the PostgreSQL server :param str backup_label: label for the backup returned by Postgres :rtype: tuple """ try: conn = self.connect() # Issue a rollback to release any unneeded lock conn.rollback() # Start the exclusive backup cur = conn.cursor(cursor_factory=DictCursor) if self.server_version < 80400: cur.execute( "SELECT location, " "(pg_xlogfile_name_offset(location)).*, " "now() AS timestamp " "FROM pg_start_backup(%s) AS location", (backup_label,)) else: cur.execute( "SELECT location, " "(pg_xlogfile_name_offset(location)).*, " "now() AS timestamp " "FROM pg_start_backup(%s,%s) AS location", (backup_label, self.config.immediate_checkpoint)) start_row = cur.fetchone() # Rollback to release the transaction, as the connection # is to be retained until the end of backup conn.rollback() return start_row except (PostgresConnectionError, psycopg2.Error) as e: msg = "pg_start_backup(): %s" % str(e).strip() _logger.debug(msg) raise PostgresException(msg)
def start_concurrent_backup(self, label): """ Calls pg_start_backup on the PostgreSQL server using the API introduced with version 9.6 This method returns a dictionary containing the following data: * location * timeline * timestamp :param str label: descriptive string to identify the backup :rtype: psycopg2.extras.DictRow """ try: conn = self.connect() # Rollback to release the transaction, as the pg_start_backup # invocation can last up to PostgreSQL's checkpoint_timeout conn.rollback() # Start the backup using the api introduced in postgres 9.6 cur = conn.cursor(cursor_factory=DictCursor) cur.execute( "SELECT location, " "(SELECT timeline_id " "FROM pg_control_checkpoint()) AS timeline, " "now() AS timestamp " "FROM pg_start_backup(%s, %s, FALSE) AS location", (label, self.config.immediate_checkpoint)) start_row = cur.fetchone() # Rollback to release the transaction, as the connection # is to be retained until the end of backup conn.rollback() return start_row except (PostgresConnectionError, psycopg2.Error) as e: msg = "pg_start_backup command: %s" % (str(e).strip(), ) _logger.debug(msg) raise PostgresException(msg)
def drop_repslot(self, slot_name): """ Drop a physical replication slot using the streaming connection :param str slot_name: Replication slot name """ cursor = self._cursor() try: # In the following query, the slot name is directly passed # to the DROP_REPLICATION_SLOT command, without any # quoting. This is a characteristic of the streaming # connection, otherwise if will fail with a generic # "syntax error" cursor.execute('DROP_REPLICATION_SLOT %s' % slot_name) except psycopg2.DatabaseError as exc: if exc.pgcode == UNDEFINED_OBJECT: # A replication slot with the that name does not exist raise PostgresInvalidReplicationSlot() if exc.pgcode == OBJECT_IN_USE: # The replication slot is still in use raise PostgresReplicationSlotInUse() else: raise PostgresException(str(exc).strip())
def pgespresso_start_backup(self, label): """ Execute a pgespresso_start_backup This method returns a dictionary containing the following data: * backup_label * timestamp :param str label: descriptive string to identify the backup :rtype: psycopg2.extras.DictRow """ try: conn = self.connect() # Rollback to release the transaction, # as the pgespresso_start_backup invocation can last # up to PostgreSQL's checkpoint_timeout conn.rollback() # Start the concurrent backup using pgespresso cur = conn.cursor(cursor_factory=DictCursor) cur.execute( 'SELECT pgespresso_start_backup(%s,%s) AS backup_label, ' 'now() AS timestamp', (label, self.config.immediate_checkpoint)) start_row = cur.fetchone() # Rollback to release the transaction, as the connection # is to be retained until the end of backup conn.rollback() return start_row except (PostgresConnectionError, psycopg2.Error) as e: msg = "pgespresso_start_backup(): %s" % str(e).strip() _logger.debug(msg) raise PostgresException(msg)
def create_physical_repslot(self, slot_name): """ Create a physical replication slot using the streaming connection :param str slot_name: Replication slot name """ cursor = self._cursor() try: # In the following query, the slot name is directly passed # to the CREATE_REPLICATION_SLOT command, without any # quoting. This is a characteristic of the streaming # connection, otherwise if will fail with a generic # "syntax error" cursor.execute('CREATE_REPLICATION_SLOT %s PHYSICAL' % slot_name) except psycopg2.DatabaseError as exc: if exc.pgcode == DUPLICATE_OBJECT: # A replication slot with the same name exists raise PostgresDuplicateReplicationSlot() elif exc.pgcode == CONFIGURATION_LIMIT_EXCEEDED: # Unable to create a new physical replication slot. # All slots are full. raise PostgresReplicationSlotsFull() else: raise PostgresException(str(exc).strip())
def stop_concurrent_backup(self): """ Calls pg_stop_backup on the PostgreSQL server using the API introduced with version 9.6 This method returns a dictionary containing the following data: * location * timeline * backup_label * timestamp :rtype: psycopg2.extras.DictRow """ try: conn = self.connect() # Rollback to release the transaction, as the pg_stop_backup # invocation could will wait until the current WAL file is shipped conn.rollback() # Stop the backup using the api introduced with version 9.6 cur = conn.cursor(cursor_factory=DictCursor) cur.execute( 'SELECT end_row.lsn AS location, ' '(SELECT CASE WHEN pg_is_in_recovery() ' 'THEN min_recovery_end_timeline ELSE timeline_id END ' 'FROM pg_control_checkpoint(), pg_control_recovery()' ') AS timeline, ' 'end_row.labelfile AS backup_label, ' 'now() AS timestamp FROM pg_stop_backup(FALSE) AS end_row') return cur.fetchone() except (PostgresConnectionError, psycopg2.Error) as e: msg = "Error issuing pg_stop_backup command: %s" % str(e).strip() _logger.debug(msg) raise PostgresException(msg)