Jak najlepiej korzystać z puli połączeń w SQLAlchemy dla puli transakcji na poziomie PgBouncer?

15

Użycie SQLAlchemy do zapytania do bazy danych PostgreSQL za PgBouncer, przy użyciu puli na poziomie transakcji.

Jakiego wzoru najlepiej użyć do tego rodzaju konfiguracji? Czy powinienem mieć jeden silnik na proces, używając ConnectionPool, czy powinienem utworzyć silnik na żądanie i używać NullPooldla każdego z nich? Czy jest jakiś inny wzór, którego powinienem używać?

Dziękuję bardzo! Daj mi znać, jeśli potrzeba więcej informacji, a zaktualizuję jak najszybciej.

Juan Carlos Coto
źródło

Odpowiedzi:

9

z PGBouncer prawdopodobnie będziesz chciał trzymać się NullPool. W takim przypadku możesz być w stanie współużytkować pojedynczy silnik między podprocesami, ponieważ żadne połączenia gniazd nie zostaną przeniesione poza granicę podprocesu. Ale nie można udostępniać niczego, co odnosi się do obiektu połączenia, takiego jak sesja z aktywną transakcją, poza tą granicą. Zdecydowanie nie chciałbyś robić „silnika na żądanie”. Silnik to kosztowny obiekt, który gromadzi wiele informacji o danym adresie URL bazy danych za pierwszym razem, gdy go widzi.

zzzeek
źródło
4

Ustaw nazwę aplikacji

Jeśli spodziewasz się uruchomić wiele procesów, musisz wiedzieć, skąd się łączą. PGBouncer sprawi, że będzie to niewidoczne pg_stat_activity. Rozwiąż to, ostrożnie ustawiając application_namepotrzebne informacje:

# Sets the application name for this connection in the form of
#   application-name:user@host
prog = os.path.basename(sys.argv[0]) or 'desjob'
username = pwd.getpwuid (os.getuid ()).pw_name
hostname = socket.gethostname().split(".")[0
args.setdefault('connect_args', {'application_name': "%s:%s@%s" %
    (prog, username, hostname)})
args.setdefault('isolation_level', "AUTOCOMMIT")
engine = create_engine(url, **args)

Wolę sesje

Użyj Sesji, ponieważ żądania z obiektu Engine mogą się odradzać i utrzymywać wiele połączeń. Połączenie z Postgres nie jest bardzo drogie, w PGBouncer jest jeszcze mniej. Zawsze używałbym, NullPoolaby jedynymi połączeniami, które zobaczysz w Postgres, są połączenia, które są faktycznie używane.

from sqlalchemy.pool import Pool, NullPool
engine = create_engine(uri, poolclass=NullPool)

Wyeliminuj transakcje bezczynności

Jeśli zamierzasz używać PGBouncer do skalowania, konieczne jest unikanie pozostawania transakcji zablokowanych. W tym celu trzeba skręcić zrobić autocommit na . W SQLAlchemy nie jest to proste ... istnieją trzy miejsca, w których można ustawić coś o nazwie „automatyczne zatwierdzanie”:

psycopg2 autocommit

conn = psycopg2.connect(uri)
conn.autocommit = True

Zakłada się, że jest niebezpieczny niebezpieczny, ponieważ SQLAlchemy musi wiedzieć, co dzieje się pod spodem.

Autoryzacja sesji

Session = sessionmaker(bind=engine, autocommit=True)
session = Session()

Wymaga to ostrożnego, wyraźnego przekazania:

session.begin()
session.execute(...)
session.rollback()

Wywołanie funkcji i przekazanie Wyjątkiem jest niezmiernie trudne, bo begin()i commit()nie mogą być zagnieżdżone:

def A():
  session.begin()
  ...
  session.rollback()

def B():
  session.begin()
  try:
      A() # error, already open

W tym trybie psycopg2 autocommitwydaje się być False(domyślnie)

Automatyczne zatwierdzanie silnika

Ustawienie trybu izolacji silnika na "AUTOCOMMIT"podczas tworzenia silnika ustanawia nowe domyślne zachowanie, które może nie wymagać zmian w istniejącym kodzie.

engine = create_engine(uri, isolation_level="AUTOCOMMIT")

W tym trybie psycopg2 autocommitwydaje się byćTrue

Głównym problemem jest to, że jedynym sposobem na zagwarantowanie zawarcia bloku kodu w transakcji jest ręczne wysłanie instrukcji:

session.execute("BEGIN")
#...
session.execute("COMMIT")
eradman
źródło