Go: отменили HTTP-запрос, а база всё ещё работает
Представим ситуацию. Клиент отправил запрос в go api. Сервер начал выполнять тяжёлый sql-запрос, но клиент не дождался ответа и отключился. Кажется, что работа должна остановиться. Ведь у нас есть context.Context. Но не всё так просто. Например:
func getOrders(ctx context.Context, db *sql.DB) error { rows, err := db.Query("SELECT * FROM orders") if err != nil { return err } defer rows.Close() // Обработка результата return nil }
Здесь ctx вообще не участвует в выполнении sql-запроса. Даже если он отменится, база может продолжить работу.
Правильнее использовать контекстный метод:
func getOrders(ctx context.Context, db *sql.DB) error { rows, err := db.QueryContext( ctx, "SELECT * FROM orders", ) if err != nil { return err } defer rows.Close() // Обработка результата return nil }
Теперь database/sql передаёт отмену контекста драйверу. Если драйвер и СУБД поддерживают отмену, запрос можно прервать.
Но есть нюанс: отмена контекста не гарантирует мгновенную остановку работы на стороне базы. Всё зависит от драйвера, базы данных и того, как именно выполняется запрос.
И ещё одна распространённая ошибка: db.QueryContext(context.Background(), query)
Формально метод контекстный. Но context.Background() не отменится при отключении http-клиента и не имеет дедлайна.
В обработчике http-запроса обычно стоит передавать контекст самого запроса или его производный контекст с таймаутом.
Например: ctx, cancel := context.WithTimeout( r.Context(), 2*time.Second, ) defer cancel() rows, err := db.QueryContext(ctx, query)
Так мы ограничиваем время ожидания и связываем операцию с жизненным циклом http-запроса.
Вывод: наличие context.Context в функции ещё не означает, что операция умеет отменяться. Контекст нужно передать именно туда, где выполняется работа, а поддержка отмены зависит от конкретной реализации.
В высоконагруженном сервисе это важно: клиент уже ушёл, а ресурсы базы всё ещё могут расходоваться.
· вчера
Несколько советов по базам данных: 1. Не используйте select * в запросах (звёздная болезнь), чтобы потом внезапно не удивиться огромному количеству приехавших полей 2. Сразу указывайте нужные вам поля, желательно с приведением типов вроде select Id::uuid, type::int from table, тогда изменения типов полей вас внезапно не настигнут
0
ответить
коммент скрыт — часть юзеров считает его токсичным или некорректным
коммент удалён