Documentation
¶
Overview ¶
Package singlepass разводит фоновый проход между репликами: из всех процессов, подошедших к проходу одновременно, его исполняет ровно один.
Предмет ¶
Фоновая работа поднимается в КАЖДОЙ реплике: у процесса нет способа узнать, что он не один. Пока реплика одна, разницы не видно; вторую поднимает автомасштабирование по загрузке — то есть под нагрузкой, когда дублирование дороже всего, и между «работает» и «начало дублировать» нет ни решения человека, ни записи в журнале.
Когда брать замок прохода, а когда клеймить строки ¶
Замок прохода годится там, где проход — БЭКСТОП: он редок, ограничен своим пределом на партию и не обязан ускоряться от числа реплик. Тогда «исполняет один» есть ровно то, что уже написано в комментарии у предела партии, и код с развёртыванием перестают противоречить друг другу.
Он НЕ годится там, где проход — несущий путь материализации: сериализовав его на весь кластер, мы лишили бы работу возможности идти в несколько реплик. Такому проходу нужен КЛЕЙМ СТРОКИ (`FOR UPDATE SKIP LOCKED` либо условная правка-CAS): реплики берут непересекающиеся партии и работают одновременно.
Почему сессионный замок, а не транзакционный ¶
Транзакционный (`pg_advisory_xact_lock`) живёт до конца транзакции, а проход транзакцией не является: он ходит к соседям и в плоскость данных, и держать транзакцию открытой всё это время значило бы занимать соединение и удерживать снимок ради взаимного исключения. Сессионный замок занимает ОДНО соединение пула и снимается закрытием, которое возвращает функция.
Что делает проигравший ¶
НИЧЕГО — и это штатный исход, а не отказ. Проигравший пропускает свой тик и приходит на следующем. Считать это ошибкой значило бы, что N−1 реплик пишут отказ на каждом успешном проходе: наблюдаемость деградирует ровно там, где она нужна.
Index ¶
Constants ¶
This section is empty.
Variables ¶
var ErrNoPool = errors.New("singlepass: pool is required")
ErrNoPool — проход просят развести, а базы не подали. Отдельная ошибка, а не «взяли и пошли»: молчаливый пропуск замка вернул бы ровно тот дубль, ради которого пакет заведён, и выглядел бы при этом исправной работой.
Functions ¶
This section is empty.
Types ¶
type Release ¶
Release снимает замок прохода. Принимает контекст ЯВНО, потому что зовётся из defer, когда контекст прохода уже мог быть отменён.
func TryAcquire ¶
TryAcquire пытается взять именованный замок прохода.
Возвращает (release, true, nil), если проход достался нам; (nil, false, nil), если его уже исполняет другая реплика; ошибку — только на отказе базы.
name — устойчивое имя прохода, а не идентификатор реплики: замок берётся по его хэшу, поэтому два процесса с одним именем встречаются на одном ключе. Пространство ключей — общее для всей базы, поэтому имя обязано нести домен («kacho.compute.stuck-delete-finisher»), а не глагол («sweep»).