package entity import "time" // Work — чью работу ждёт запись, стоя на рубеже. От этого зависит предел // простоя: своя работа мерится одним числом, ожидание чужой операции — другим. // Граница проходит по исполнителю, а не по рубежу: число на каждый рубеж // назвало бы разными вещи, различающиеся только им. type Work int const ( // WorkOwn — работу делаем мы сами. WorkOwn Work = iota // WorkForeign — ждём операцию внешнего сервиса. WorkForeign ) // Stage — объявление рубежа одним местом. // // Из этого перечня выводятся все потребители: выбор следующего шага, отбор // захвата, срок протухания захвата и предел простоя. Перечислять рубежи порознь // в каждом потребителе нельзя: рубеж, забытый в отборе захвата, не выдаётся ни // одному воркеру никогда, а пустой прогон по инварианту проекта не пишется в // журнал и не считается в метрику — запись встала бы без единого следа. type Stage struct { Name string // Work — чью работу ждём, стоя на этом рубеже. Work Work // AcquireTimeout — срок протухания захвата. Едет с рубежом, а не с воркером: // воркер не привязан к шагу и не знает заранее, что вытянет. AcquireTimeout time.Duration // Terminal — рубеж, из которого запись в работу не берут. Такой рубеж не // подпадает и под предел простоя: стоять в нём запись будет вечно по // построению. Terminal bool } // Сроки захвата. Каждый не меньше того, что его шаг может занять на самом // длинном допустимом входе: расчётный потолок записи — шесть часов, и приведение // такой записи идёт дольше часа по построению. const ( normalizeAcquireTimeout = 8 * time.Hour submitAcquireTimeout = 8 * time.Hour pollAcquireTimeout = time.Hour finishAcquireTimeout = time.Hour ) // stages — цепочка рубежей в порядке прохождения. var stages = []Stage{ {Name: StateUploaded, Work: WorkOwn, AcquireTimeout: normalizeAcquireTimeout}, {Name: StateNormalized, Work: WorkOwn, AcquireTimeout: submitAcquireTimeout}, {Name: StateSubmitted, Work: WorkForeign, AcquireTimeout: pollAcquireTimeout}, {Name: StateTranscribed, Work: WorkOwn, AcquireTimeout: finishAcquireTimeout}, {Name: StateDone, Terminal: true}, } // WorkingStages — рубежи, с которых запись берут в работу. func WorkingStages() []Stage { out := make([]Stage, 0, len(stages)) for _, s := range stages { if !s.Terminal { out = append(out, s) } } return out } // StageByName находит рубеж по имени. Второе значение ложно у рубежа, которого // в цепочке нет: запись с таким рубежом до шага не доходит. func StageByName(name string) (Stage, bool) { for _, s := range stages { if s.Name == name { return s, true } } return Stage{}, false } // StuckLimits — пределы простоя, приходящие из настроек. type StuckLimits struct { // Own — предел на своей работе. Own time.Duration // Foreign — предел на ожидании чужой операции. Foreign time.Duration } // Limit — предел простоя для этого рубежа. У конечного рубежа предела нет: // запись стоит в нём вечно по построению. func (s Stage) Limit(limits StuckLimits) (time.Duration, bool) { if s.Terminal { return 0, false } if s.Work == WorkForeign { return limits.Foreign, true } return limits.Own, true }