Как лучше всего использовать пул соединений в SQLAlchemy для пула на уровне транзакций PgBouncer?

15

Использование SQLAlchemy для запроса базы данных PostgreSQL за PgBouncer, используя пул на уровне транзакций.

Какой шаблон лучше всего использовать для такого рода настройки? Должен ли я иметь один механизм для каждого процесса, использующий ConnectionPoolили я должен создать механизм для каждого запроса и использовать NullPoolдля каждого из них? Есть ли другой шаблон, который я должен использовать?

Огромное спасибо! Дайте мне знать, если потребуется дополнительная информация, и я обновлю как можно скорее.

Хуан Карлос Кото
источник

Ответы:

9

с PGBouncer вы, вероятно, захотите просто придерживаться NullPool. В этом случае вы можете использовать один и тот же механизм для всех подпроцессов, поскольку соединения сокетов не будут переноситься через границу подпроцесса. Но вы не можете поделиться чем-либо, ссылающимся на объект Connection, например, Session с активной транзакцией, через эту границу. Вы определенно не захотите делать «механизм для запроса», хотя Engine - это дорогой объект, который накапливает много информации о конкретном URL базы данных при первом его просмотре.

zzzeek
источник
4

Установите имя приложения

Если вы ожидаете запуска многих процессов, вам нужно знать, откуда они подключаются. PGBouncer сделает это невидимым для pg_stat_activity. Решите это, тщательно установив application_nameнужную информацию:

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

Предпочитайте сессии

Используйте Sessions, так как запросы от объекта Engine могут порождаться и удерживаться для нескольких соединений. Подключение к Postgres не очень дорого, с PGBouncer - еще меньше. Я всегда использовал бы NullPoolтак, чтобы единственными соединениями, которые вы увидите в Postgres, были соединения, которые фактически используются.

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

Устранить пустые транзакции

Если вы намерены использовать PGBouncer для масштабирования, обязательно избегайте оставлять транзакции открытыми. Для этого вам необходимо обратиться autocommit в . Это не просто с SQLAlchemy ... есть три места, где можно установить что-то под названием "autocommit":

psycopg2 autocommit

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

Предполагается, что небезопасно небезопасно, потому что SQLAlchemy должен знать, что происходит под ним.

Сессия автокоммит

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

Это требует осторожного, явного вручения:

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

Функция вызова и исключение вручая чрезвычайно трудно , потому что begin()и commit()не может быть вложенным:

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

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

В этом режиме psycopg2 autocommitвыглядит False(по умолчанию)

Автокоммит двигателя

Установка режима изоляции Engine "AUTOCOMMIT"при создании двигателя устанавливает новое поведение по умолчанию, которое может не требовать изменений в существующем коде.

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

В этом режиме psycopg2 autocommitоказываетсяTrue

Основная проблема здесь состоит в том, что единственный способ гарантировать, что блок кода обернут в транзакцию, состоит в том, чтобы выдать операторы вручную:

session.execute("BEGIN")
#...
session.execute("COMMIT")
eradman
источник