Método Python simples que leva uma eternidade para executar / executar

8

Meu código fica preso no clean_up()método emMyClass()

my_class.py:

import os
import pandas as pd
import psycopg2, pymysql, pyodbc
from db_credentials_dict import db_credentials

class MyClass():

    def __init__(self, from_database, to_table_name, report_name):
        ...

    def get_sql(self):
        ...

    def get_connection(self):
        ...

    def query_to_csv(self):
        ...

    def csv_to_postgres(self):
        ...

    def extract_and_load(self):
        self.query_to_csv()
        self.csv_to_postgres()

    def get_final_sql(self):
        ...

    def postgres_to_csv(self):
        ...

    def clean_up(self):
        print('\nTruncating {}...'.format(self.to_table_name), end='')
        with self.postgres_connection.cursor() as cursor:
            cursor.execute("SELECT NOT EXISTS (SELECT 1 FROM %s)" % self.to_table_name)
            empty = cursor.fetchone()[0]
            if not empty:
                cursor.execute("TRUNCATE TABLE %s" % self.to_table_name)
        self.postgres_connection.commit()
        print('DONE')

        print('Removing {}...'.format(self.filename), end='')
        if os.path.exists(self.filepath):
            os.remove(self.filepath)
            print('DONE')
        else:
            print('{} does not exist'.format(self.filename))

main.py:

from my_class import MyClass
from time import sleep

bookings = MyClass(from_database='...',to_table_name='...',report_name='...')
bookings2 = MyClass(from_database='...',to_table_name='...',report_name='...')
channel = MyClass(from_database='...',to_table_name='...',report_name='...')
cost = MyClass(from_database='...',to_table_name='...',report_name='...')

tables = [bookings, bookings2, channel, cost]
for table in tables:
    table.extract_and_load()

daily_report = MyClass(from_database='...',to_table_name='...',report_name='...')
daily_report.postgres_to_csv()
sleep(10)

for table in tables:
    table.clean_up()

Quando eu corro, main.pyele executa tudo até o loop final, ou seja for table in tables: table.clean_up(). Ele fica preso lá, sem erros ou avisos.

Ao executar esse método, ele funciona bem, ou seja, trunca as tabelas do postgres. Precisa de ajuda para que isso funcione e para entender por que o método final não é executado quando todos os outros são executados.

Saída de quando executado clean_up()por conta própria:

Truncating bookings...DONE
Removing bookings.csv...DONE

Truncating bookings2...DONE
Removing bookings2.csv...DONE

Truncating channel...DONE
Removing channel.csv...DONE

Truncating cost...DONE
Removing cost.csv...DONE

O que o código geral faz:

  1. Agarrando arquivos que contêm consultas sql, extraindo dados de diferentes bancos de dados e executando essas consultas.
  2. Exportando-os para csv
  3. Importando csv para um banco de dados postgres
  4. Escrevendo uma consulta postgres que reúne dados e depois exporta como um csv para ser usado em uma ferramenta de BI para visualização de dados
  5. Truncando os dados no postgres e excluindo os arquivos csv no ponto 2

Você provavelmente está pensando que isso é uma loucura e eu concordo. Atualmente, estou pagando o que tenho e não consigo armazenar dados no meu computador porque são dados da empresa (daí a truncagem e remoção dos dados).

AK91
fonte
2
Não vejo nada causando esse problema no que você publicou do seu código Python, portanto, o problema provavelmente está no postgres.
bruno desthuilliers
você pode postar a saída do seu método clean_up () ao executar o seu próprio?
kederrac em 3/01

Respostas:

5

TRUNCATEA cláusula no Postgres requer ACCESS EXCLUSIVEbloqueio na relação e também pode disparar alguns BEFORE TRUNCATEgatilhos.

Meu palpite é que o problema está no lado do Postgres, por exemplo, TRUNCATEtenta obter ACCESS EXCLUSIVEbloqueio na relação, mas já está bloqueado por outra pessoa.

Primeiro, verifique seus logs do Postgres.

Em seguida, verifique o que os back-end do Postgres estão fazendo durante o seu travamento TRUNCATE:

SELECT * FROM pg_stat_activity;

Em seguida, investigue bloqueios com:

-- View with readable locks info and filtered out locks on system tables
CREATE VIEW active_locks AS
SELECT clock_timestamp(), pg_class.relname, pg_locks.locktype, pg_locks.database,
       pg_locks.relation, pg_locks.page, pg_locks.tuple, pg_locks.virtualtransaction,
       pg_locks.pid, pg_locks.mode, pg_locks.granted
FROM pg_locks JOIN pg_class ON pg_locks.relation = pg_class.oid
WHERE relname !~ '^pg_' and relname <> 'active_locks';

-- Now when we want to see locks just type
SELECT * FROM active_locks;
ololobus
fonte
Obrigado por isso. Eu criei a view como você mostrou, a saída está vazia #
AK91
OK, o que há com pg_stat_activity? O que outros back-end estão fazendo? Por exemplo, comSELECT * FROM pg_stat_activity WHERE backend_type = 'client backend' AND datname IS NOT NULL;
ololobus 27/12/19
2

Eu sugiro que você faça o processo de limpeza desejado, tudo em uma transação. Para impedir estados indesejados.

E verifique se a tabela existe no banco de dados usando o information_schema

SELECT EXISTS (
   SELECT 1
   FROM   information_schema.tables 
   WHERE  table_schema = 'schema_name'
   AND    table_name = 'table_name'
   );

Marque Se existir, crie seus comandos truncados.

e execute o processo de limpeza em uma única transação.

Frederic
fonte