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.
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.
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 autocommit
on. 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")