Pg cancel query: примеры (PHP)
pg_cancel_query(PgSql\Connection $connection): boolБазовое описание функции pg_cancel_query
Функция pg_cancel_query в PHP предназначена для отмены асинхронного запроса к базе данных PostgreSQL, который был отправлен с помощью pg_send_query, pg_send_query_params, pg_send_execute или pg_send_prepare. Она позволяет остановить выполнение долгого запроса без необходимости разрыва соединения с базой данных.
Функция принимает один обязательный параметр:
- $connection - ресурс соединения с базой данных PostgreSQL, полученный через
pg_connect()илиpg_pconnect().
Возвращаемое значение: true при успешной отправке запроса на отмену или false при ошибке.
Простые примеры использования
<?php
$conn = pg_connect("host=localhost dbname=test user=postgres");
// Отправляем асинхронный запрос
pg_send_query($conn, "SELECT pg_sleep(10)");
// Отменяем выполнение через 2 секунды
sleep(2);
$result = pg_cancel_query($conn);
if ($result) {
echo "Запрос успешно отменен";
} else {
echo "Не удалось отменить запрос";
}
?>Запрос успешно отменен
<?php
$conn = pg_connect("host=localhost dbname=test");
pg_send_query($conn, "SELECT * FROM large_table");
// Немедленная отмена
if (pg_cancel_query($conn)) {
// Получаем результат (будет false при отмене)
$res = pg_get_result($conn);
if ($res === false) {
echo "Запрос был прерван";
}
}
?>Запрос был прерван
Похожие функции в PHP
Функция проверяет состояние соединения без возможности отмены запроса. Полезна для мониторинга активности соединения.
Получает результат асинхронного запроса. Может использоваться совместно с pg_cancel_query для обработки прерванных операций.
Системная функция PostgreSQL, доступная через SQL-запрос, которая принудительно завершает процесс на стороне сервера. Более агрессивный метод, требующий прав суперпользователя.
Аналоги в других языках программирования
import psycopg2
from threading import Thread
import time
conn = psycopg2.connect("dbname=test")
cursor = conn.cursor()
# Запуск долгого запроса в отдельном потоке
def execute_query():
cursor.execute("SELECT pg_sleep(20)")
thread = Thread(target=execute_query)
thread.start()
time.sleep(2)
# Отмена через метод cancel
cursor.cancel()
print("Запрос отменен")const { Client } = require('pg');
const client = new Client();
async function run() {
await client.connect();
const query = client.query("SELECT pg_sleep(10)");
setTimeout(() => {
// Отмена через метод cancel
query.cancel();
console.log("Запрос отменен");
}, 2000);
}
run();В MySQL используется команда KILL QUERY [id] для остановки выполняющегося запроса, где id идентификатор потока, который можно получить из SHOW PROCESSLIST.
Типичные ошибки
<?php
$conn = pg_connect("host=localhost dbname=test");
// Синхронный запрос
pg_query($conn, "SELECT pg_sleep(1)");
// Эта отмена не будет работать
$result = pg_cancel_query($conn);
var_dump($result); // bool(false)
?>bool(false)
<?php
$conn = pg_connect("host=localhost dbname=test");
pg_send_query($conn, "SELECT 1");
// Сначала получаем результат
pg_get_result($conn);
// Поздняя отмена (бесполезна)
$canceled = pg_cancel_query($conn);
var_dump($canceled); // bool(true), но отменять нечего
?>bool(true)
<?php
$conn = pg_connect("host=localhost dbname=test");
pg_send_query($conn, "SELECT pg_sleep(5)");
pg_close($conn); // Соединение закрыто
$result = pg_cancel_query($conn); // Предупреждение
?>Изменения в последних версиях PHP
В PHP 8.1 была улучшена обработка ошибок для функций расширения pgsql. Функция pg_cancel_query теперь более стабильно работает при прерывании запросов в многопоточных окружениях.
Начиная с PHP 8.0, все функции pgsql выбрасывают исключения при неудачных операциях, если не используется режим молчания через @. Это касается и pg_cancel_query при передаче неверного параметра соединения.
Расширенные примеры использования
<?php
$conn = pg_connect("host=localhost dbname=test");
function cancel_with_retry($connection, $max_retries = 3) {
$retry_count = 0;
while ($retry_count < $max_retries) {
if (pg_cancel_query($connection)) {
return true;
}
usleep(100000); // 100ms задержка
$retry_count++;
}
return false;
}
pg_send_query($conn, "SELECT pg_sleep(30)");
// Ждем 5 секунд, затем пытаемся отменить
sleep(5);
if (cancel_with_retry($conn)) {
echo "Запрос отменен после попыток";
} else {
echo "Не удалось отменить запрос";
}
?><?php
$conn1 = pg_connect("host=localhost dbname=test");
$conn2 = pg_connect("host=localhost dbname=test");
// Отправляем два запроса на разных соединениях
pg_send_query($conn1, "SELECT pg_sleep(20)");
pg_send_query($conn2, "SELECT pg_sleep(20)");
// Отменяем только первый запрос
if (pg_cancel_query($conn1)) {
echo "Первый запрос отменен";
// Второй продолжает выполняться
$result2 = pg_get_result($conn2);
if ($result2) {
echo "Второй запрос завершен";
}
}
?><?php
declare(ticks=1);
$conn = pg_connect("host=localhost dbname=test");
$should_cancel = false;
// Обработчик сигнала
pcntl_signal(SIGINT, function() use (&$should_cancel) {
$should_cancel = true;
});
pg_send_query($conn, "SELECT pg_sleep(60)");
// Проверка флага каждую секунду
for ($i = 0; $i < 60; $i++) {
if ($should_cancel) {
pg_cancel_query($conn);
echo "Запрос отменен по сигналу";
break;
}
sleep(1);
}
?><?php
class QueryMonitor {
private $activeQueries = [];
public function startQuery($connection, $query) {
$id = uniqid();
$this->activeQueries[$id] = [
'conn' => $connection,
'query' => $query,
'start' => microtime(true)
];
pg_send_query($connection, $query);
return $id;
}
public function cancelLongRunning($timeout = 5.0) {
$now = microtime(true);
$canceled = [];
foreach ($this->activeQueries as $id => $info) {
if (($now - $info['start']) > $timeout) {
if (pg_cancel_query($info['conn'])) {
$canceled[] = $id;
unset($this->activeQueries[$id]);
}
}
}
return $canceled;
}
}
$monitor = new QueryMonitor();
$conn = pg_connect("host=localhost dbname=test");
$queryId = $monitor->startQuery($conn, "SELECT pg_sleep(10)");
// Через 3 секунды проверяем долгие запросы
sleep(3);
$canceled = $monitor->cancelLongRunning(2.5);
if (in_array($queryId, $canceled)) {
echo "Долгий запрос был автоматически отменен";
}
?>