Триаж свёл 62 сырые находки девяти проходов к 33 причинам: 3 блокера, 4 «сейчас», 2 развилки. Все закрыты регрессионными тестами. - схема точки сна определяется по самой точке, а не по индексу в исходном массиве: одна пропущенная точка меняла эпизод и сводку местами - доставка сворачивается одной транзакцией: частичное состояние было недетерминированным (восемь прогонов — семь состояний) - граница размера на распакованном теле: 400 КиБ gzip разворачивались в 400 МиБ мимо max_body_mb - столкновение — расхождение канонических форм, а не байтов; WARN с координатами объекта; payload без HTML-экранирования - выравнивание по местной метке: получасовые зоны уводили часовую выгрузку в minute - доставка из одних суточных сводок больше не отвергается целиком - единицы не переписываются молча; счётчик считает сохранённые точки - все выходы Fold логируются, исход пишется на переживающем отмену контексте Четыре развилки вынесены блокерами в беклог.
95 lines
4.4 KiB
Go
95 lines
4.4 KiB
Go
package hae
|
|
|
|
// assignLayers проставляет слой каждой группе точек.
|
|
//
|
|
// Правило проверено на всей истории (находка 33) и дважды ломалось на живых
|
|
// данных при более простых формулировках: классификация доставки целиком
|
|
// сложила минутные точки с посекундными и удвоила сумму за час, а
|
|
// классификация каждой метрики по отдельности растащила редкие метрики по трём
|
|
// слоям.
|
|
//
|
|
// Работающая формулировка в три шага:
|
|
//
|
|
// 1. плотная метрика (не меньше denseThreshold точек) классифицируется по
|
|
// собственному выравниванию;
|
|
// 2. редкая наследует самый мелкий слой среди плотных метрик этой доставки —
|
|
// её собственное выравнивание ничего не значит, одна метка на часе бывает
|
|
// и у минутного ряда;
|
|
// 3. доставка без плотных метрик вовсе наследует последний надёжно выведенный
|
|
// слой той же автоматизации, а если и его нет — берёт слой из НАДЁЖНОГО
|
|
// заголовка.
|
|
//
|
|
// Третий шаг не роскошь: измерено 2 такие доставки из 89, обе с заголовком
|
|
// `Default`. Заменить его заголовком нельзя — `Default` не означает режима.
|
|
func assignLayers(groups []group, meta Meta, res *Result) error {
|
|
delivery := deliveryLayer(groups)
|
|
|
|
if delivery == "" {
|
|
// Плотных метрик нет: наследуем от прошлых доставок этой автоматизации,
|
|
// иначе — от надёжного заголовка.
|
|
delivery = meta.FallbackLayer
|
|
if delivery == "" {
|
|
delivery = res.HeaderLayer
|
|
}
|
|
if delivery == "" && needsDerivedLayer(groups) {
|
|
return ErrLayerUnknown
|
|
}
|
|
}
|
|
|
|
res.Layer = delivery
|
|
// Расхождение сверяется только с надёжным заголовком: `Default` не
|
|
// означает режима, и сравнение с ним давало бы WARN на каждой доставке
|
|
// потока в пять минут — сигнал утонул бы в собственном шуме.
|
|
res.LayerMismatch = res.HeaderLayer != "" && delivery != res.HeaderLayer
|
|
|
|
for i := range groups {
|
|
g := &groups[i]
|
|
switch {
|
|
case g.fixed:
|
|
// Слой назначен схемой, выводить нечего.
|
|
case g.dense:
|
|
g.layer = g.alignment
|
|
default:
|
|
g.layer = delivery
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// needsDerivedLayer говорит, осталась ли в доставке хоть одна группа, которой
|
|
// слой действительно надо вывести.
|
|
//
|
|
// Доставка, состоящая из одних суточных сводок сна, слоя не требует — он у них
|
|
// назначен схемой. Отказ ради неё был бы необратим: исход детерминирован, и
|
|
// пересборка из архива повторила бы его вечно, то есть автоматизация,
|
|
// настроенная только на сон, не донесла бы данных никогда.
|
|
func needsDerivedLayer(groups []group) bool {
|
|
for _, g := range groups {
|
|
if !g.fixed {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// deliveryLayer — самый мелкий слой среди плотных метрик доставки. Пустой,
|
|
// если плотных метрик нет.
|
|
//
|
|
// Группы с назначенным слоем в голосовании не участвуют: суточных сводок сна
|
|
// бывает больше порога плотности, и их полуночные метки иначе назначили бы
|
|
// всей доставке слой hour.
|
|
func deliveryLayer(groups []group) Layer {
|
|
var out Layer
|
|
for _, g := range groups {
|
|
if g.fixed || !g.dense {
|
|
continue
|
|
}
|
|
if out == "" {
|
|
out = g.alignment
|
|
continue
|
|
}
|
|
out = finer(out, g.alignment)
|
|
}
|
|
return out
|
|
}
|