В чем разница между соединением с базой данных и сессией этого соединения
Что проверяют
Понимаете ли вы, что под словом «сессия» прячутся две разные вещи: состояние на стороне базы и объект ORM в Python. От этого зависят пулы соединений, транзакции и утечки. Следом обычно спрашивают, что станет с сессией за PgBouncer или почему Session нельзя делить между потоками.
Как отвечать
Разведите уровни:
- соединение: физический канал до сервера, сокет плюс аутентификация. PostgreSQL на каждое соединение держит отдельный процесс, поэтому открывать его дорого, и соединения переиспользуют через пул. В Python это объект DB-API, например из
psycopg.connect(); - сессия базы: состояние, привязанное к соединению: параметры после
SET,search_path, временные таблицы, подготовленные запросы, advisory-блокировки, текущая транзакция. В PostgreSQL сессия живёт ровно столько, сколько соединение; Sessionв SQLAlchemy: логическая единица работы. Держит identity map и список изменений (unit of work), а соединение берёт уEngineиз пула лениво, при первом запросе, и возвращает приcommit(),rollback()илиclose(). За свою жизнь однаSessionможет поработать с разными физическими соединениями;Engine: владелец пула. По умолчанию этоQueuePoolна пять соединений плюс десять сверх лимита.
На практике правильный цикл такой: одна Session на запрос или задачу, закрытая в конце через with Session(engine) as session:. После commit() по умолчанию действует expire_on_commit=True, и следующее обращение к атрибуту объекта снова идёт в базу. В Django соединение живёт в пределах потока, время жизни задаёт CONN_MAX_AGE.
Где спотыкаются
Считают Session из SQLAlchemy и соединение одним и тем же. Отсюда идея держать одну глобальную сессию на всё приложение, хотя она не потокобезопасна, а AsyncSession нельзя делить между конкурентными задачами.
Забывают про пулер в режиме транзакций. PgBouncer отдаёт одно серверное соединение разным клиентам, поэтому SET, временные таблицы и advisory-блокировки уезжают к чужому запросу или теряются.
Не закрывают сессию после ошибки. Соединение остаётся в состоянии idle in transaction, держит блокировки и мешает autovacuum убирать мёртвые строки.