web-dev-qa-db-fra.com

Comment utiliser au mieux le regroupement de connexions dans SQLAlchemy pour le regroupement au niveau des transactions PgBouncer?

Utilisation de SQLAlchemy pour interroger une base de données PostgreSQL derrière PgBouncer, en utilisant le regroupement au niveau des transactions.

Quel est le meilleur schéma à utiliser pour ce type de configuration? Dois-je avoir un moteur par processus, en utilisant un ConnectionPool, ou dois-je créer un moteur par demande et utiliser NullPool pour chacun d'eux? Existe-t-il un modèle différent que je devrais utiliser?

Merci beaucoup! Faites-moi savoir si plus d'informations sont nécessaires et je mettrai à jour dès que possible.

16
Juan Carlos Coto

avec PGBouncer, vous voudrez probablement rester avec NullPool. Dans ce cas, vous pourrez peut-être partager un seul moteur entre des sous-processus car aucune connexion de socket ne sera transportée sur la limite du sous-processus. Mais vous ne pouvez pas partager quoi que ce soit faisant référence à un objet Connection, comme une session avec une transaction active, sur cette limite. Vous ne voudriez certainement pas faire "moteur par demande", un moteur est un objet coûteux qui accumule beaucoup d'informations sur une URL de base de données particulière la première fois qu'il la voit.

9
zzzeek

Définissez le nom de l'application

Si vous prévoyez d'exécuter de nombreux processus, vous devez savoir d'où ils se connectent. PGBouncer rendra cela invisible pour pg_stat_activity. Résolvez cela en définissant soigneusement le application_name Avec les informations dont vous aurez besoin:

# 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)

Préférences de sessions

Utilisez les sessions, car les demandes d'un objet Engine peuvent apparaître et s'accrocher à plusieurs connexions. Se connecter à Postgres n'est pas très cher, avec PGBouncer c'est encore moins le cas. J'utiliserais toujours NullPool pour que les seules connexions que vous verrez dans Postgres soient les connexions réellement utilisées.

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

Éliminer les transactions inactives

Si votre intention est d'utiliser PGBouncer pour évoluer, il est impératif d'éviter de laisser les transactions bloquées. Pour ce faire, vous devez activer autocommiton. Ce n'est pas simple avec SQLAlchemy ... il y a trois endroits où quelque chose appelé "autocommit" peut être défini:

autocommit psycopg2

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

Présumé dangereux dangereux car SQLAlchemy doit savoir ce qui se passe en dessous.

Autocommit de session

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

Cela nécessite une remise soigneuse et explicite:

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

L'appel de fonction et le traitement des exceptions sont extrêmement difficiles car begin() et commit() ne peuvent pas être imbriqués:

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

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

Dans ce mode, psycopg2 autocommit semble être False (par défaut)

Autocommit du moteur

La définition du mode d'isolation du moteur sur "AUTOCOMMIT" Lors de la création du moteur établit un nouveau comportement par défaut qui peut ne pas nécessiter de modifications du code existant.

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

Dans ce mode, psycopg2 autocommit semble être True

Le problème majeur ici est que la seule façon de garantir qu'un bloc de code est encapsulé dans une transaction est d'émettre les instructions manuellement:

session.execute("BEGIN")
#...
session.execute("COMMIT")
4
eradman