Documentation
¶
Overview ¶
Package listcursorplan доказывает ПОВЕДЕНИЕМ, что страница курсорного списка берёт свой порядок из индекса, а не достраивает его сортировкой.
Зачем отдельная проба, если гейт уже зелёный ¶
Гейт `internal/repohygiene` сводит текст миграции с текстом запроса. Это утверждение о СХЕМЕ: «объявлен индекс такой-то формы». Утверждение о ПОВЕДЕНИИ — другое: «планировщик Postgres берёт порядок отсюда». Между ними умещается всё, что гейт по построению не знает: тип индекса, класс операторов, сортировка колонки, версия сервера, порядок колонок, который человек прочитал иначе, чем читает база. Поэтому форма проверяется разбором, а действие — планом настоящего Postgres на настоящей схеме сервиса.
Что именно утверждается и почему без данных ¶
Проба спрашивает у базы ПЛАН страницы:
EXPLAIN SELECT … FROM <схема>.<таблица> [WHERE <равенство>]
ORDER BY <ключи курсора> LIMIT <размер+1>
и требует двух вещей сразу: план ИМЕНУЕТ ожидаемый индекс и НЕ содержит узла сортировки.
Выбор плана зависит от статистики, а статистика — от числа строк, которое проба сама и насыпала бы. Такая проба меряла бы не свойство схемы, а удачность подобранного объёма: на трёхстах строках вердикт один, на трёх тысячах другой, и оба «настоящие». Поэтому основной вопрос ставится ДЕТЕРМИНИРОВАННО — последовательное чтение и сортировка выключаются на время одного запроса (`SET LOCAL enable_seqscan/enable_sort = off`). Обе ручки МЯГКИЕ: они не запрещают план, а делают его дорогим. Значит база возьмёт упорядоченный путь по индексу тогда и только тогда, когда он СУЩЕСТВУЕТ, — а когда его нет, честно построит сортировку, и проба это увидит.
Один случай на сервис проверяется ВДОБАВОК реалистично — на насыпанных строках, с `ANALYZE` и БЕЗ единой ручки: там план выбирает сам планировщик по своей стоимости. Реалистичным сделан общий список операций: у его таблицы нет ни внешних ключей, ни триггеров учёта, поэтому строки насыпаются одним оператором и не тянут за собой фикстуру половины сервиса.
Контроль в обратную сторону обязателен ¶
Утверждение «сортировки в плане нет» само по себе зеленело бы на пробе, которая ничего не читает. Поэтому каждый прогон обязан нести КОНТРОЛЬ: тот же запрос к той же таблице, упорядоченный по колонке, под которую индекса нет. Его план обязан содержать сортировку. Без контроля проба доказывает только то, что она выполнилась.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func SeedOperations ¶
SeedOperations — 5000 строк общей таблицы операций одним оператором.
Живёт здесь, а не у каждого сервиса: семь копий одного посева разошлись бы молча, и разошлись бы на составе колонок. Отметки времени расходятся, поэтому порядок по (created_at, id) не вырожден и ничьи не решают всё.
Types ¶
type Case ¶
type Case struct {
// Table — таблица без схемы.
Table string
// Index — индекс, который план ОБЯЗАН назвать.
Index string
// Order — ключи курсора дословно, как их пишет репозиторий:
// `created_at ASC, id ASC`.
Order string
// Where — равенство, которое несёт настоящий обход (`project_id = 'prj-plan'`).
// Пусто, если у обхода обязательного равенства нет.
Where string
// Seed — оператор, насыпающий строки. Непуст ⇒ случай проверяется ВДОБАВОК
// реалистично: `ANALYZE`, затем план БЕЗ единой ручки.
Seed string
}
Case — один курсорный обход, чей план проверяется.