#postgresql #amazon-rds #psycopg2
#postgresql #amazon-rds #psycopg2
Вопрос:
У нас есть приложение, которое использует psycopg2 для записи записей в RDS Postgres. Иногда, когда происходит событие уменьшения масштаба и контейнер останавливается во время фиксации вставки, это создает взаимоблокировку в таблице. Мы используем пул поточных соединений с некоторыми стандартными тайм-аутами, как показано ниже:
self._pool = pool.ThreadedConnectionPool(
mincount,
maxcount,
dsn,
cursor_factory=cursor_factory,
application_name=application_name or name,
keepalives_idle=1,
keepalives_interval=1,
keepalives_count=5,
options=f"-c statement_timeout={statement_timeout}s -c idle_in_transaction_session_timeout={idle_in_transaction_session_timeout}s",
Тайм-аут ожидания в транзакции, похоже, работает как транзакции, возникающие после выдачи ошибки тайм-аута, а не в режиме ожидания, но мы все еще получаем проблемы с блокировкой. Есть ли другой тайм-аут, который мы должны использовать, чтобы Postgres уничтожил эти транзакции?
Обновление по проблеме:
У нас есть 2 разных приложения. Тот, который записывает в таблицу, а другой, который считывает из нее. Мы видим, что эта ошибка иногда появляется в приложении записи:
deadlock detected
DETAIL: Process 31504 waits for ShareLock on transaction 33994594; blocked by process 28310.
Process 28310 waits for ShareLock on transaction 33994595; blocked by process 31504.
HINT: See server log for query details.
CONTEXT: while inserting
Что, если я потяну pg_stat_activity для этих pid, я получу это:
[
{
"datid": 262668,
"datname": "app_db",
"pid": 31504,
"usename": "app",
"application_name": "app-Writer",
"query_start": "2020-10-28 23:16:23.859818",
"state_change": "2020-10-28 23:16:23.865455",
"wait_event_type": "Client",
"wait_event": "ClientRead",
"state": "idle",
"backend_xid": null,
"backend_xmin": null,
"query": "COMMIT",
"backend_type": "client backend"
},
{
"datid": 262668,
"datname": "app_db",
"pid": 28310,
"usename": "app",
"application_name": "app-Writer",
"query_start": "2020-10-28 23:12:01.232097",
"state_change": "2020-10-28 23:12:01.234281",
"wait_event_type": "Client",
"wait_event": "ClientRead",
"state": "idle",
"backend_xid": null,
"backend_xmin": null,
"query": "COMMIT",
"backend_type": "client backend"
}
]
Приложение reader позже завершается с этой ошибкой:
psycopg2.InternalError: terminating connection due to idle-in-transaction timeout
SSL connection has been closed unexpectedly
Оба приложения для чтения и записи имеют одинаковые настройки тайм-аута.
Комментарии:
1. Я не понимаю. Если в базе данных есть взаимоблокировка, она будет разрешена автоматически через 1 секунду.
Ответ №1:
Во-первых, если взаимоблокировка возникает редко, не беспокойтесь слишком сильно: все, что вам нужно сделать, это научить ваше приложение повторять транзакцию, если оно столкнется с взаимоблокировкой. Читайте дальше, если вам нужно избавиться от тупиковой ситуации.
То COMMIT
, что вы видите pg_stat_activity
, — это отвлекающий маневр: query
столбец содержит последнее утверждение, которое было отправлено по этому соединению, и, вероятно COMMIT
, именно оно завершило транзакцию после возникновения взаимоблокировки.
Читатели и авторы никогда не блокируют друг друга в PostgreSQL, поэтому взаимоблокировка должна быть между двумя транзакциями, изменяющими данные.
Вы должны сделать то, что указано в сообщении об ошибке, и обратиться к файлу журнала PostgreSQL. Там вы найдете больше информации, в частности, инструкции, которые выполнялись, когда произошла взаимоблокировка. Эта информация не отправляется клиенту, поскольку она может содержать конфиденциальные данные.
Чтобы устранить проблему, вы должны рассмотреть все инструкции, которые были выполнены в этих транзакциях, потому что вполне может быть, что более ранние инструкции в транзакции блокировались, что способствовало взаимоблокировке. Помните, что блокировки удерживаются до конца транзакции.
Если вы не можете идентифицировать транзакции и то, что они сделали, из кода вашего приложения, вы можете установить log_statement = 'all'
в PostgreSQL и убедиться, что идентификатор транзакции ( %x
) включен log_prefix
. Это приведет к тому, что все операторы будут регистрироваться (остерегайтесь проблем с производительностью), и когда произойдет ошибка, вы сможете найти в журнале все операторы, относящиеся к задействованным транзакциям.
Это громоздкий, но единственный способ, если вы не можете найти инструкции из своего приложения.
Как только вы узнаете инструкции, вы можете воспроизвести и отладить проблему.
Комментарии:
1. Спасибо вам за это! Сегодня я собираюсь просмотреть журналы. Когда вы говорите «Читатели и авторы никогда не блокируют друг друга в PostgreSQL», мне интересно, почему наше приложение для чтения не работает с «прерыванием соединения из-за ожидания ожидания в транзакции»? Это всего лишь оператор select, поэтому я не понимаю, почему это будет заблокировано?
2. Ах, это что-то совершенно другое. Эта транзакция не заблокирована, она запустила транзакцию, выполнила soethig, а затем забыла закрыть транзакцию (только для чтения). Теперь
idle_in_transaction_session_timeout
должно быть установлено, и транзакция отменяется.