Пересмотр очереди, выводы из текста, наблюдаемость
Разведка job-queue-choice — очередь написана вручную: захват не транзакционен, повторов и счётчика попыток нет, очереди мёртвых задач нет. Стоит перед сменой хранилища, чтобы не переписывать захват дважды. Цель text-insights: заголовок, пересказ и темы внешним сервисом с OpenAI-совместимым интерфейсом. Граница паспорта сдвинута — «понимание сказанного» было записано как то, чем проект не является; за границей остались ответы на вопросы по записи и поиск по смыслу. Цель service-observability в сопровождении и разведка opentelemetry-fit: /metrics остаётся и развивается, способ решает замер.
This commit is contained in:
@@ -23,6 +23,7 @@
|
||||
- [🐞 Починить тесты http-обработчика, ни разу не бывшие зелёными](items/http-handler-tests-never-green.md) — go test ./... падает на master: тесты требуют файла, которого нет в репозитории, и ждут 201 от ffprobe, которому скормили строку.
|
||||
- [🧹 Сравнивать доменные ошибки через errors.As](items/errors-as-instead-of-typecast.md) — NoopJobError и JobNotFoundError проверяются приведением типа: первая же обёртка %w между слоями сломает проверку молча.
|
||||
- [🧹 Задать таймауты обращениям к внешним сервисам](items/external-call-timeouts.md) — Ни у Telegram, ни у Object Storage, ни у SpeechKit нет таймаута: молчащий собеседник держит шаг конвейера до истечения часового захвата.
|
||||
- [🔬 Очередь задач: своя таблица или готовая библиотека](items/job-queue-choice.md) — Очередь написана вручную: захват двумя запросами без транзакции, протухание временем, опрос раз в секунду вхолостую тремя воркерами.
|
||||
- [🧹 Перевести хранилище на встроенный PocketBase](items/pocketbase-storage.md) — Хранилище, учётные записи и веб-панель нужны все три, и SQLite с goqu не даёт ни второго, ни третьего.
|
||||
- [✨ Пускать в приложение только после входа через OIDC](items/oidc-login.md) — HTTP API открыт наружу без аутентификации: любой из интернета заводит задачи за наши деньги и читает чужие расшифровки по идентификатору.
|
||||
- [✨ Привязать запись к владельцу и отдавать только свои](items/record-ownership.md) — У задачи и файла нет владельца, поэтому знание UUID задачи и есть право её читать.
|
||||
@@ -34,4 +35,5 @@
|
||||
- [✨ Сделать экран списка своих записей и чтения текста](items/records-list-screen.md) — Расшифровка сегодня доходит одним сообщением и теряется в переписке; вернуться к ней через неделю нечем.
|
||||
- [✨ Сделать приложение устанавливаемым на телефон](items/installable-pwa.md) — Приложение, живущее вкладкой браузера, теряется среди прочих: ярлыка на экране у него нет.
|
||||
- [✨ Отправлять готовый текст через apprise и ntfy](items/ntfy-delivery.md) — Пользователь веба узнаёт о готовности только опросом с открытого экрана.
|
||||
- [🔬 Стоит ли брать OpenTelemetry вместо голого Prometheus](items/opentelemetry-fit.md) — Метрик одиннадцать штук на пять счётчиков, трассировки нет вовсе: путь одной записи по конвейеру собирается только чтением логов глазами.
|
||||
- [🔬 Потолки SpeechKit по длине записи и по формату](items/speechkit-limits.md) — Потолок длины записи и перечень принимаемых форматов неизвестны, а цель про долгие записи без них не начинается.
|
||||
|
||||
@@ -30,9 +30,12 @@
|
||||
|
||||
- [🎯 Принимается запись любого формата, включая дорожку из видео](items/any-audio-source.md) — Конвертер вызывается одной командой ffmpeg, проверенной на голосовых Telegram; что он берёт помимо них, никто не мерил.
|
||||
- [🎯 Запись длиной в несколько часов доходит до текста](items/long-recordings.md) — Потолок не замерен ни на одном звене: Telegram не отдаёт больше 20 МиБ, а границы модели deferred-general неизвестны.
|
||||
- [🎯 Приложение показывает, о чём запись, не читая её целиком](items/text-insights.md) — Расшифровка часового разговора — это стена текста: найти в списке нужную запись и вспомнить, о чём она, сегодня нечем.
|
||||
|
||||
## Сопровождение
|
||||
|
||||
- [🎯 Состояние сервиса видно без чтения логов](items/service-observability.md) — Отказ замечает пользователь, а не владелец: оповещения нет, а путь записи по конвейеру собирается глазами по логам контейнера.
|
||||
|
||||
## Готово
|
||||
|
||||
- 2025-08-14 `telegram-transcription` — Голосовое сообщение из Telegram возвращается текстом. Основной вход сервиса: бот принимает голосовое, аудиофайл и документ с аудио и отвечает расшифровкой.
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
# 🔬 Очередь задач: своя таблица или готовая библиотека
|
||||
|
||||
- **Тип:** research
|
||||
- **Категория:** Очередь
|
||||
- **Зачем:** Очередь написана вручную: захват двумя запросами без транзакции, протухание временем, опрос раз в секунду вхолостую тремя воркерами.
|
||||
|
||||
Сегодняшняя очередь — принцип «очередь таблицей» из
|
||||
[architecture.md](../../docs/architecture.md) плюс `FindAndAcquire` в
|
||||
репозитории. Работает она на единицах записей в день, но написана целиком
|
||||
своими руками, и вот чего в ней нет:
|
||||
|
||||
- **захват не транзакционен** — `UPDATE` проставляет `acquisition_id`, отдельный
|
||||
`SELECT` читает строку по нему; сегодня это безопасно только потому, что три
|
||||
воркера читают три разных состояния и одну строку не делят;
|
||||
- **повторов с нарастающей паузой нет** — задача либо повторяется каждую
|
||||
секунду, либо ждёт истечения захвата целый час;
|
||||
- **число попыток не считается** — задача, падающая всегда, падает вечно;
|
||||
- **опрос вхолостую** — три воркера дёргают базу раз в секунду независимо от
|
||||
того, есть ли работа;
|
||||
- **очереди мёртвых задач нет** — `is_error = 1` исключает запись из выборки
|
||||
навсегда и молча.
|
||||
|
||||
Разведка идёт **перед** `pocketbase-storage`: смена хранилища перепишет захват
|
||||
задачи в любом случае, и переписывать его дважды незачем.
|
||||
|
||||
## Вопрос
|
||||
|
||||
Берём ли готовую очередь на Go поверх той же базы, и если да — какую, или
|
||||
оставляем свою таблицу, дописав к ней повторы, счётчик попыток и очередь мёртвых
|
||||
задач.
|
||||
|
||||
## Куда ляжет ответ
|
||||
|
||||
`docs/research/job-queue.md` — сравнение кандидатов с числами: зависимости,
|
||||
какая база нужна, переживает ли перезапуск, есть ли повторы и очередь мёртвых
|
||||
задач. Решение и причина отказа от остальных — в `docs/adr/`: принцип «очередь
|
||||
таблицей» записан в архитектуре, и его пересмотр обратной правкой не
|
||||
откатывается. Следом правится раздел «Принципы» в
|
||||
[architecture.md](../../docs/architecture.md).
|
||||
|
||||
## Рамки
|
||||
|
||||
Внешнего брокера (Redis, RabbitMQ, NATS) не рассматриваем: он добавляет к
|
||||
выкладке процесс, которого там нет, ради нагрузки в единицы записей в день.
|
||||
Кандидаты — библиотеки, работающие поверх той же встроенной базы. Ответ обязан
|
||||
учесть, что хранилище скоро сменится на PocketBase.
|
||||
@@ -0,0 +1,34 @@
|
||||
# 🔬 Стоит ли брать OpenTelemetry вместо голого Prometheus
|
||||
|
||||
- **Тип:** research
|
||||
- **Категория:** Очередь
|
||||
- **Зачем:** Метрик одиннадцать штук на пять счётчиков, трассировки нет вовсе: путь одной записи по конвейеру собирается только чтением логов глазами.
|
||||
- **Теги:** goal:service-observability
|
||||
|
||||
Эндпоинт `/metrics` остаётся и развивается — это решено. Вопрос в том, чем его
|
||||
развивать: дописывать счётчики в `internal/metrics` напрямую через
|
||||
`client_golang` или перевести на OpenTelemetry и получить заодно трассировку.
|
||||
|
||||
Сегодня у записи есть путь длиной в минуты через четыре внешних сервиса и три
|
||||
воркера, и связать его звенья можно только по `job_id` в логах. Трассировка
|
||||
отвечает на это прямо, но приносит коллектор — процесс, которого в выкладке нет.
|
||||
|
||||
## Вопрос
|
||||
|
||||
Даёт ли OpenTelemetry на этом проекте больше, чем стоит: коллектор в выкладке,
|
||||
переписанные метрики и словарь имён, — или дешевле остаться на `client_golang` и
|
||||
дописать недостающие счётчики.
|
||||
|
||||
## Куда ляжет ответ
|
||||
|
||||
`docs/research/opentelemetry.md` — с числами: сколько зависимостей приносит,
|
||||
что появляется в выкладке, сколько метрик переписывается. Решение и причина
|
||||
отказа — в `docs/adr/`, если берём: переход на другой словарь метрик ломает
|
||||
собранные ряды и обратной правкой не откатывается.
|
||||
|
||||
## Рамки
|
||||
|
||||
Метрики Prometheus и путь `/metrics` из ответа не исчезают ни при каком исходе:
|
||||
OpenTelemetry рассматривается как источник тех же метрик, а не как замена
|
||||
эндпоинта. Своего хранилища метрик и своих панелей не поднимаем — это отдельная
|
||||
работа.
|
||||
@@ -0,0 +1,25 @@
|
||||
# 🎯 Состояние сервиса видно без чтения логов
|
||||
|
||||
- **Тип:** goal
|
||||
- **Секция:** Сопровождение
|
||||
- **Зачем:** Отказ замечает пользователь, а не владелец: оповещения нет, а путь записи по конвейеру собирается глазами по логам контейнера.
|
||||
|
||||
Наблюдаем **мы**, а не пользователь сервиса, — поэтому цель стоит в
|
||||
сопровождении, а не среди возможностей приложения. Пользовательская половина
|
||||
той же темы живёт отдельно: о готовности своей записи человек узнаёт по цели
|
||||
[ready-notification](ready-notification.md).
|
||||
|
||||
Эндпоинт `/metrics` остаётся и развивается. Чем именно развивать — голым
|
||||
`client_golang` или OpenTelemetry с трассировкой — решает разведка
|
||||
`opentelemetry-fit`.
|
||||
|
||||
## Завершение
|
||||
|
||||
1. По метрикам видно, что конвейер встал: задача висит в состоянии дольше
|
||||
обычного, и это отличимо от «работы нет».
|
||||
2. Каждый внешний сервис имеет метрику вызовов, отказов и длительности —
|
||||
сегодня их нет ни у одного.
|
||||
3. Путь одной записи по конвейеру собирается запросом, а не чтением логов
|
||||
глазами.
|
||||
4. Владелец узнаёт об отказе сам, а не от пользователя.
|
||||
5. Стоимость обращений к платным сервисам видна числом.
|
||||
@@ -0,0 +1,25 @@
|
||||
# 🎯 Приложение показывает, о чём запись, не читая её целиком
|
||||
|
||||
- **Тип:** goal
|
||||
- **Секция:** Направления
|
||||
- **Зачем:** Расшифровка часового разговора — это стена текста: найти в списке нужную запись и вспомнить, о чём она, сегодня нечем.
|
||||
|
||||
К расшифровке добавляется короткий заголовок, пересказ и темы. Считает их
|
||||
внешний сервис с OpenAI-совместимым интерфейсом — своей модели не держим, как не
|
||||
держим и модели распознавания.
|
||||
|
||||
**Это сдвиг границы паспорта.** До 2026-08-10 «понимание сказанного» было
|
||||
записано как то, чем проект не является: «мы отдаём текст, а не выводы из него».
|
||||
Граница сдвинута сознательно, и в паспорте от неё остался более узкий запрет —
|
||||
ответов на вопросы по записи и поиска по смыслу не делаем.
|
||||
|
||||
## Завершение
|
||||
|
||||
1. У готовой записи есть заголовок в одну строку, и он виден в списке вместо
|
||||
первых слов расшифровки.
|
||||
2. У записи есть пересказ, который читается быстрее самой расшифровки.
|
||||
3. У записи есть темы, и по ним список отбирается.
|
||||
4. Отказ или молчание внешнего сервиса не роняют задачу: расшифровка доходит до
|
||||
человека без заголовка и пересказа.
|
||||
5. Стоимость обращения к сервису видна метрикой: сколько записей обработано и
|
||||
сколько это стоило по числу токенов.
|
||||
Reference in New Issue
Block a user