Documentation
¶
Overview ¶
Package transform объявляет преобразования пакета и правила их сочетания.
Здесь нет ни одной реализации — только имена шагов, порядок между ними и запреты. Реализации живут в packet, processors и командах CLI; этот пакет отвечает на два вопроса: можно ли включить такой набор и в каком порядке его выполнять.
Зачем отдельный пакет ¶
Порядок был закодирован очередью вызовов chain.Add и комментариями вида "MUST run before ComputeIntegrity". Это работает ровно до тех пор, пока каждый, кто добавляет шаг, прочитал все комментарии. За один месяц так набралось три бага, и все — про порядок, а не про сами преобразования:
- разложить по колонкам после ComputeIntegrity, но присвоив Data целиком, значило выбросить уже проставленный xxh3;
- разложить по колонкам ДО хеширования значило бы, что писатель хеширует колонки, а читатель строки, — расхождение на каждом пакете;
- materializeForWrite гасила намерение разложить, потому что к моменту её вызова строки уже построчные, — и --columnar --integrity молча писал обычный пакет.
Ни один не виден на одиночном флаге, и ни один не виден на паре. Они живут в стыке между шагами.
Почему объявление, а не перечень сочетаний ¶
Перебор N флагов — это 2^N наборов, попарный — N². Написанная руками матрица отстанет от кода на первом же новом преобразовании, а дописывать в неё строку под каждый новый флаг — это и есть костыль, только оформленный таблицей. Ограничения объявляются у шага один раз; наборы для проверки порождаются из них (см. stages_test.go).
Правило для нового преобразования ¶
Добавляя формат или преобразование, объявите его здесь и ничего не добавляйте в вызывающий код руками:
- Заведите Stage с именем и ограничениями: After/Before — если порядок важен, Conflicts — если сочетание бессмысленно или опасно.
- Reason пишите как ответ пользователю, а не как записку себе: он попадёт в текст отказа.
- Не ставьте ограничение "на всякий случай". Каждое из перечисленных существует потому, что его нарушение уже ломало данные, и в комментарии сказано как именно. Ограничение без такой причины запретит рабочее сочетание.
- Шаг обязан быть идемпотентным либо применяться ровно из одного места. Раскладка по колонкам применялась из двух — писателем и шагом сжатия, — и второй проход читал колонки как строки, отдавая значения чужих записей. Молча: такой пакет конвертируется без замечаний.
Index ¶
Examples ¶
Constants ¶
const ( // StageRowProcessors — цепочка pre-export: маскирование, нормализация, // валидация. Идёт первой: всё дальнейшее обязано считать уже изменённые // значения, иначе хеш накроет то, чего в пакете не будет. StageRowProcessors = "row-processors" // StageCompact — carry-forward кодирование фиксированных полей (v1.3.1). StageCompact = "compact" // StageColumnar — колоночная раскладка Data (layout="columns"). StageColumnar = "columnar" // StageIntegrity — xxh3-хеши Schema, Data и отпечаток пакета (v1.4). StageIntegrity = "integrity" // StageCompress — zstd или kanzi. StageCompress = "compress" // StageEncrypt — AES-256-GCM посекционно (v1.5). StageEncrypt = "encrypt" )
Имена шагов. Строки, а не iota: они попадают в сообщения об ошибках и в имена подтестов, и должны читаться человеком.
Variables ¶
This section is empty.
Functions ¶
func MustRunBefore ¶
MustRunBefore сообщает, обязан ли a идти раньше b при обоих включённых. Нужен проверкам и диагностике; на горячем пути не используется.
func Plan ¶
Plan упорядочивает включённые шаги и отвергает несовместимый набор.
Порядок детерминирован: при равных ограничениях шаги идут в порядке объявления в registry. Это не косметика — недетерминированный план означал бы, что один и тот же набор флагов даёт разные байты от запуска к запуску.
Example ¶
plan, _ := Plan([]string{StageCompress, StageColumnar, StageIntegrity, StageCompact})
fmt.Println(strings.Join(plan, " → "))
Output: compact → integrity → columnar → compress
func ReadMustPrecede ¶
ReadMustPrecede сообщает, обязан ли a идти раньше b при чтении.
func ReadStepIndex ¶
ReadStepIndex возвращает позицию шага чтения, или -1.
Types ¶
type ReadStep ¶
ReadStep — шаг чтения и причина его места в порядке.
func ReadOrder ¶
func ReadOrder() []ReadStep
ReadOrder возвращает порядок шагов чтения.
Это описание, а не исполнитель: шаги разбросаны по ParseBytes, DecompressData, VerifyIntegrity и командам CLI. Список существует затем, чтобы место каждого было записано вместе с причиной — три бага здесь случились ровно потому, что причина жила в чьей-то голове.
Example ¶
for _, s := range ReadOrder() {
fmt.Println(s.Name)
}
Output: parse decrypt decompress expand-columnar expand-compact verify-rowcount verify-integrity expand-dictionary
type Stage ¶
type Stage struct {
Name string
// After перечисляет шаги, которые обязаны отработать РАНЬШЕ этого.
After []string
// Conflicts перечисляет шаги, с которыми этот несовместим. Отношение
// симметрично: объявлять его достаточно с одной стороны.
Conflicts []string
// Reason объясняет ограничение словами, пригодными для показа
// пользователю. Пустой Reason допустим только у шага без ограничений.
Reason string
}
Stage — одно преобразование и правила его сочетания с другими.