Pg cancel query: примеры (PHP)

Отмена запросов PostgreSQL через pg_cancel_query
Раздел: Базы данных (PostgreSQL)
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_connection_status

Функция проверяет состояние соединения без возможности отмены запроса. Полезна для мониторинга активности соединения.

pg_get_result

Получает результат асинхронного запроса. Может использоваться совместно с pg_cancel_query для обработки прерванных операций.

pg_terminate_backend

Системная функция PostgreSQL, доступная через SQL-запрос, которая принудительно завершает процесс на стороне сервера. Более агрессивный метод, требующий прав суперпользователя.

Аналоги в других языках программирования

Python (psycopg2)
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("Запрос отменен")
JavaScript (node-postgres)
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

В 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
<?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
<?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
<?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
<?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 "Долгий запрос был автоматически отменен";
}
?>

PHP pg_cancel_query function comments

En
Pg cancel query Cancel an asynchronous query