хранилище переехало с PocketBase на SQLite со своим каталогом файлов
- база своя: два пула, захват одним UPDATE ... RETURNING, шаги схемы на goose под файловым замком, одна миграция начальной схемы вместо семи прежних - транспорт переписан на net/http: свои слои, свой ограничитель частоты, отдача файла с проверкой владельца; панель /_/ и пространство /api/ исчезли - по находкам ревью: журнал не пишет путь под корнем приложения, ключ бюджета читается справа налево, узнавание известного идёт читающим пулом
This commit is contained in:
@@ -11,11 +11,10 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"git.vakhrushev.me/av/transcriber/internal/clock"
|
||||
"git.vakhrushev.me/av/transcriber/internal/contract"
|
||||
"git.vakhrushev.me/av/transcriber/internal/entity"
|
||||
"git.vakhrushev.me/av/transcriber/internal/ident"
|
||||
"git.vakhrushev.me/av/transcriber/internal/metrics"
|
||||
)
|
||||
|
||||
@@ -155,9 +154,12 @@ func (s *TranscribeService) CreateJobFromApi(ctx context.Context, file io.Reader
|
||||
return nil, contract.ErrOwnerRequired
|
||||
}
|
||||
|
||||
// Идентификатор назначается здесь, до укладки файла: копии записи лежат её
|
||||
// подкаталогом, названным этим идентификатором, и знать его надо раньше, чем
|
||||
// класть первую копию.
|
||||
record := &entity.AudioRecord{
|
||||
Id: ident.New(),
|
||||
State: entity.StateUploaded,
|
||||
Source: entity.SourceApi,
|
||||
OwnerID: ownerID,
|
||||
}
|
||||
|
||||
@@ -176,10 +178,10 @@ func (s *TranscribeService) createRecord(ctx context.Context, r *entity.AudioRec
|
||||
ext = fmt.Sprintf(".%s", defaultAudioExt)
|
||||
}
|
||||
|
||||
// Собственное имя записи: идентификатор с расширением. Имя, данное
|
||||
// отправителем, в хранилище не попадает — от него взято только расширение.
|
||||
fileId := uuid.NewString()
|
||||
storageFileName := fmt.Sprintf("%s%s", fileId, ext)
|
||||
// Собственное имя копии: идентификатор с расширением. Имя, данное
|
||||
// отправителем, в каталог данных не попадает — от него взято только
|
||||
// расширение.
|
||||
storageFileName := fmt.Sprintf("%s%s", ident.New(), ext)
|
||||
|
||||
// Содержимое ложится в рабочую копию потоком: в память запись целиком не
|
||||
// читается, расчётный потолок — шесть часов.
|
||||
@@ -217,7 +219,7 @@ func (s *TranscribeService) createRecord(ctx context.Context, r *entity.AudioRec
|
||||
DurationMs: int64(info.Seconds) * 1000,
|
||||
}
|
||||
|
||||
fileRecord, err := s.repos.Files.Create(storageFileName, work, meta, r.OwnerID)
|
||||
fileRecord, err := s.repos.Files.Create(r.Id, storageFileName, work, meta, r.OwnerID)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to create file record", "error", err, "file_ext", ext)
|
||||
return nil, err
|
||||
@@ -474,9 +476,9 @@ func (s *TranscribeService) normalize(ctx context.Context, r *entity.AudioRecord
|
||||
|
||||
metrics.OutputFileSizeHistogram.WithLabelValues("ogg").Observe(float64(destSize))
|
||||
|
||||
destFileName := fmt.Sprintf("%s%s", uuid.NewString(), ".ogg")
|
||||
destFileName := fmt.Sprintf("%s%s", ident.New(), ".ogg")
|
||||
destMeta := contract.FileMeta{Format: "ogg", DurationMs: srcFile.DurationMs}
|
||||
destFileRecord, err := s.repos.Files.Create(destFileName, dest, destMeta, r.OwnerID)
|
||||
destFileRecord, err := s.repos.Files.Create(r.Id, destFileName, dest, destMeta, r.OwnerID)
|
||||
if err != nil {
|
||||
s.logger.Error("Failed to create normalized file record", "error", err, "record_id", r.Id)
|
||||
return outcomeDone, err
|
||||
@@ -780,8 +782,8 @@ func (s *TranscribeService) storeOutcome(r *entity.AudioRecord, outcome *entity.
|
||||
}
|
||||
|
||||
// finish доводит запись до конечного рубежа. Наружу шаг не обращается: доставки
|
||||
// ответа отправителю у сервиса нет, и свой исход отправитель узнаёт опросом
|
||||
// готовности.
|
||||
// ответа отправителю у сервиса нет, и свой исход владелец записи узнаёт её
|
||||
// карточкой.
|
||||
func (s *TranscribeService) finish(ctx context.Context, r *entity.AudioRecord, holder string) (stepOutcome, error) {
|
||||
r.MoveToState(entity.StateDone)
|
||||
if err := s.repos.Records.Save(r, holder); err != nil {
|
||||
@@ -806,7 +808,7 @@ func (s *TranscribeService) failStep(r *entity.AudioRecord, holder, step string,
|
||||
// событий.
|
||||
//
|
||||
// Отправителю отсюда ничего не уходит: инвариант проекта «Принятая запись не
|
||||
// теряется молча» держится теперь опросом готовности — остановка видна там
|
||||
// теряется молча» держится теперь карточкой записи — остановка видна там
|
||||
// признаком — и журналом владельца, где у неё стоит причина.
|
||||
//
|
||||
// Счётчик растит **сама остановка**, а не воркер, и это не стилистика.
|
||||
|
||||
Reference in New Issue
Block a user