Продолжительный запрос Postgres прерывается, если соединение потеряно / разорвано?


21

Если я открою соединение с Postgres и выполню длительный запрос, а затем разорву соединение (например, уничтожу процесс клиента, открывший соединение), продолжит ли продолжительный запрос или он будет автоматически прерван? Это настраивается?

(Я использую Postgresql 9.2.9)

Ответы:


32

"По-разному".

Если клиент исчезает из-за потери сетевого соединения, запрос обычно выполняется до тех пор, пока не будет извлечено достаточно строк, чтобы заполнить его сетевой буфер отправки, затем остановится и застрянет, пока не прервется соединение TCP, и в этот момент он будет прерван. Если он завершится до того, как заполнит буфер отправки TCP, он завершится успешно, поэтому, если произойдет автоматическое принятие, запрос будет зафиксирован.

Если клиент убит таким образом, что его операционная система может сообщить серверу через TCP RST (например, клиентский segfault / crash, SIGTERM, SIGKILL и т. Д.), Сервер PostgreSQL установит флаг прерывания. В следующий раз, когда запрос проверяет наличие прерываний во время выполнения, он видит флаг и прерывается. Иногда запрос может выполнять нагрузку на процессор внутри кода, который не проверяет прерывания - некоторые расширения и несколько мест в ядре PostgreSQL - в этом случае он может не замечать прерывание в течение длительного времени и продолжать работу. Тем не менее, он будет всегда видеть прерывание и прерывание перед завершением и фиксацией, если это будет автокоммит.

Если клиент будет убит чем-то вроде внезапной перезагрузки ОС, так что клиентский узел внезапно ничего не узнает о TCP-соединении, но все равно сможет ответить в сети, запрос, вероятно, будет прерван при первой попытке записи строки, например Джефф сказал, потому что хост клиента отправит TCP RST в ответ на первый пакет, отправленный сервером после перезагрузки. PostgreSQL проверяет наличие прерываний в каждой строке, которую он отправляет.

Это поведение не настраивается. Что касается PostgreSQL, если клиент уходит, его задача - завершить любые запросы, которые выполнял клиент. Чтобы изменить это, вам понадобится какой-нибудь токен завершения запроса, который вы можете получить при запуске запроса, а затем использовать его, чтобы позже запросить у сервера запрос через другое соединение. По сути, вам придется реализовывать асинхронные / фоновые запросы. Возможно, хорошая функция, но в настоящее время не поддерживается.

Если запрос выполняется автоматически или если ваш запрос COMMITвыполнялся в тот момент, когда вы убили клиента / потеряли соединение, транзакция может находиться в неопределенном состоянии, когда клиент не знает, не совершено. Нет реального способа выяснить это, кроме поиска влияния транзакции на данные.

Там, где это недопустимо, вы можете использовать двухфазную фиксацию и менеджер транзакций на стороне клиента.


1
Вау, именно то, что я искал, отличный подробный ответ! Спасибо @Craig_Ringer!
Роб Беднарк


2

Он будет продолжать работать до тех пор, пока не попытается вернуть строки в соединение и не обнаружит разрыв. Таким образом, для запроса, который выполняет всю работу перед возвратом каких-либо строк, он по существу выполняется до завершения.


Спасибо @jjanes. Можете ли вы указать на любую документацию или исходный код, который указывает на это?
Роб Беднарк
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.