Есть ли способ установить тайм-аут для фиксации Postgres?

#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 должно быть установлено, и транзакция отменяется.