внутренняя модель перестроена вокруг аудиозаписи
- audiorecords вместо transcribe_jobs: приложения (texts, structures, recognitions, record_events, topics) живут своими коллекциями, ссылки на исходник и на приведённую копию перестали переставляться - рубеж называет достигнутое, отказ стал признаком остановки с причиной, а сторожей стало двое: число отказов и время в рубеже - воркеры потеряли специализацию, их число задаётся [pipeline] workers, шаг выбирается по рубежу, а захват отдаёт идентификатор и признак захвата
This commit is contained in:
@@ -7,18 +7,59 @@ import (
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
|
||||
"git.vakhrushev.me/av/transcriber/internal/entity"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Server ServerConfig `toml:"server"`
|
||||
Storage StorageConfig `toml:"storage"`
|
||||
Pipeline PipelineConfig `toml:"pipeline"`
|
||||
Yandex YandexConfig `toml:"yandex"`
|
||||
Telegram TelegramConfig `toml:"telegram"`
|
||||
Auth AuthConfig `toml:"auth"`
|
||||
}
|
||||
|
||||
// PipelineConfig — настройки конвейера расшифровки.
|
||||
type PipelineConfig struct {
|
||||
// Workers — число одинаковых воркеров. Ноль — законное значение: сервис
|
||||
// поднимается, записи принимаются и не двигаются. Это режим, а не поломка.
|
||||
Workers int `toml:"workers"`
|
||||
// OwnWorkLimitMinutes — предел простоя записи там, где работу делаем мы
|
||||
// сами. Сторож ловит **зависание**, а не долгую работу: живой шаг
|
||||
// наблюдается по самому процессу, а остановка обратима — снятие признака
|
||||
// возвращает запись на её рубеж.
|
||||
OwnWorkLimitMinutes int `toml:"own_work_limit_minutes"`
|
||||
// ForeignWorkLimitMinutes — предел простоя там, где ждём чужую операцию.
|
||||
// Сколько идёт распознавание долгой записи, никто не мерил, поэтому ошибка
|
||||
// идёт в сторону долгого: ложная остановка хуже поздней.
|
||||
ForeignWorkLimitMinutes int `toml:"foreign_work_limit_minutes"`
|
||||
}
|
||||
|
||||
// StuckLimits переводит настройки в пределы простоя.
|
||||
func (c PipelineConfig) StuckLimits() entity.StuckLimits {
|
||||
return entity.StuckLimits{
|
||||
Own: time.Duration(c.OwnWorkLimitMinutes) * time.Minute,
|
||||
Foreign: time.Duration(c.ForeignWorkLimitMinutes) * time.Minute,
|
||||
}
|
||||
}
|
||||
|
||||
// Validate проверяет числа конвейера. Отрицательное число воркеров — ошибка
|
||||
// настройки, а не режим: ноль объявлен законным значением, и отличать его от
|
||||
// опечатки обязан старт.
|
||||
func (c PipelineConfig) Validate() error {
|
||||
if c.Workers < 0 {
|
||||
return errors.New("pipeline: число воркеров не может быть отрицательным")
|
||||
}
|
||||
if c.OwnWorkLimitMinutes <= 0 || c.ForeignWorkLimitMinutes <= 0 {
|
||||
return errors.New("pipeline: пределы простоя задаются положительным числом минут")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type ServerConfig struct {
|
||||
Port int `toml:"port"`
|
||||
ShutdownTimeout int `toml:"shutdown_timeout"`
|
||||
@@ -143,6 +184,15 @@ func defaultConfig() *Config {
|
||||
Storage: StorageConfig{
|
||||
DataDir: "data",
|
||||
},
|
||||
Pipeline: PipelineConfig{
|
||||
Workers: 3,
|
||||
// Час на свою работу и сутки на чужую. Час меньше времени, которое
|
||||
// многочасовая запись занимает на приведении, и это принято
|
||||
// сознательно: сторож ловит зависание, живой шаг наблюдается по
|
||||
// самому процессу, а остановка обратима.
|
||||
OwnWorkLimitMinutes: 60,
|
||||
ForeignWorkLimitMinutes: 24 * 60,
|
||||
},
|
||||
Yandex: YandexConfig{
|
||||
FolderID: "",
|
||||
SpeechKitAPIKey: "",
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Проверка входа — единственная страховка от того, чтобы сервис поднялся с
|
||||
@@ -274,3 +275,71 @@ func TestLoadConfigMalformedBeforeAnyKeyHidesValue(t *testing.T) {
|
||||
t.Fatalf("место отказа не названо, чинить нечего: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Числа конвейера приезжают из файла, а не выдумываются кодом.
|
||||
func TestLoadConfigReadsPipelineSettings(t *testing.T) {
|
||||
path := writeConfig(t, `
|
||||
[pipeline]
|
||||
workers = 7
|
||||
own_work_limit_minutes = 90
|
||||
foreign_work_limit_minutes = 720
|
||||
|
||||
[telegram]
|
||||
enabled = false
|
||||
`)
|
||||
|
||||
cfg, err := LoadConfig(path)
|
||||
if err != nil {
|
||||
t.Fatalf("конфиг не прочитан: %v", err)
|
||||
}
|
||||
|
||||
if cfg.Pipeline.Workers != 7 {
|
||||
t.Errorf("число воркеров не прочитано: %d", cfg.Pipeline.Workers)
|
||||
}
|
||||
|
||||
limits := cfg.Pipeline.StuckLimits()
|
||||
if limits.Own != 90*time.Minute {
|
||||
t.Errorf("предел своей работы не прочитан: %v", limits.Own)
|
||||
}
|
||||
if limits.Foreign != 720*time.Minute {
|
||||
t.Errorf("предел чужой работы не прочитан: %v", limits.Foreign)
|
||||
}
|
||||
}
|
||||
|
||||
// Умолчания есть у всех трёх чисел: файл без секции конвейера годен, и сервис
|
||||
// поднимается с рабочими значениями.
|
||||
func TestPipelineSettingsHaveDefaults(t *testing.T) {
|
||||
path := writeConfig(t, "[telegram]\nenabled = false\n")
|
||||
|
||||
cfg, err := LoadConfig(path)
|
||||
if err != nil {
|
||||
t.Fatalf("конфиг не прочитан: %v", err)
|
||||
}
|
||||
|
||||
if cfg.Pipeline.Workers <= 0 {
|
||||
t.Errorf("умолчание числа воркеров негодно: %d", cfg.Pipeline.Workers)
|
||||
}
|
||||
if err := cfg.Pipeline.Validate(); err != nil {
|
||||
t.Errorf("умолчания не проходят собственную проверку: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Ноль воркеров — объявленный режим, а отрицательное число и нулевой предел —
|
||||
// опечатка: подниматься с ней значит остановить всякую запись первым же
|
||||
// захватом.
|
||||
func TestPipelineValidateSeparatesModeFromTypo(t *testing.T) {
|
||||
valid := PipelineConfig{Workers: 0, OwnWorkLimitMinutes: 60, ForeignWorkLimitMinutes: 1440}
|
||||
if err := valid.Validate(); err != nil {
|
||||
t.Errorf("ноль воркеров объявлен законным значением: %v", err)
|
||||
}
|
||||
|
||||
for name, cfg := range map[string]PipelineConfig{
|
||||
"отрицательное число воркеров": {Workers: -1, OwnWorkLimitMinutes: 60, ForeignWorkLimitMinutes: 1440},
|
||||
"нулевой предел своей работы": {Workers: 1, OwnWorkLimitMinutes: 0, ForeignWorkLimitMinutes: 1440},
|
||||
"нулевой предел чужой работы": {Workers: 1, OwnWorkLimitMinutes: 60, ForeignWorkLimitMinutes: 0},
|
||||
} {
|
||||
if err := cfg.Validate(); err == nil {
|
||||
t.Errorf("%s принято за режим", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user