- audiorecords вместо transcribe_jobs: приложения (texts, structures, recognitions, record_events, topics) живут своими коллекциями, ссылки на исходник и на приведённую копию перестали переставляться - рубеж называет достигнутое, отказ стал признаком остановки с причиной, а сторожей стало двое: число отказов и время в рубеже - воркеры потеряли специализацию, их число задаётся [pipeline] workers, шаг выбирается по рубежу, а захват отдаёт идентификатор и признак захвата
7.0 KiB
7.0 KiB
Конвенция: база данных и идентификаторы
Как мы устраиваем таблицы и ключи. Актуальная схема — ../database.md.
Взято из проекта jellybit целиком. Сегодняшний код transcriber следует
этому частью: ключи — UUID v4, а не ULID, и единой точки их генерации нет. Время
единой точкой читается с 2026-08-13 — internal/clock, метка в UTC, — и правило
держит линтер. Правила действуют на новый код; переписывание существующего —
отдельная работа, и до неё расхождение читается как долг, а не как нарушение.
Механизировано: сверка изменённого шага схемы с
../database.md (docs.py check), чтение времени единой точкой
(forbidigo плюс internal/clock) и согласованность колонок очереди
(тест-сканер internal/archrules). Прочие пункты — прозой; адреса —
go-linters.md, «Механизировано».
Первичные ключи — ULID, не автоинкремент
- PK сущности — TEXT ULID (26 символов Crockford base32), генерируется
приложением в момент создания записи.
Расхождение: идентификаторы записей выдаёт хранилище — 15 знаков
собственного алфавита. Своей точки генерации у приложения нет, и
ORDER BY idхронологией не является: порядок берут по колонке времени с ключом. - Почему ULID: сортируем по времени создания (
ORDER BY id= хронология), компактен и удобен в URL и логах (без дефисов — grep и двойной клик берут id целиком), глобально уникален между таблицами — поиск по голому id находит все записи сущности в логах. - Точка генерации и разбора одна: создание — при вставке записи в репозитории, разбор — на входных границах. Самодельных генераторов по месту вызова не заводим.
Канонический вид — lowercase
- Генерим и храним id в нижнем регистре. Сравнение строк в SQLite побайтовое, поэтому любой внешний id (URL, форма, поле запроса) обязательно проходит разбор до запроса к БД — разбор проверяет формат и нормализует регистр (base32 ULID нечувствителен к регистру при декодировании).
- Синтаксически неверный id считаем несуществующей сущностью (404), без похода в БД.
Естественные и составные ключи — для деталей
- У таблиц-деталей и связей допустим естественный или составной ключ вместо ULID, когда он есть по природе данных. Отдельный ULID там — мёртвый вес.
- Прочие генерируемые идентификаторы — тем же способом, что и ключи сущностей: единый формат, сортируемость, корреляция в логах.
Прочее
- Enum-поля (
state,source, …) — обычныйTEXTбезCHECK; допустимые значения держит код. Расхождение: перечни, по которым панель владельца правит запись руками, закрыты схемой (SelectField), а не кодом: правка руками не должна заводить значение, которого сервис не знает. Закрыты рубеж записи, причина её остановки, вид текста, источник и исход события журнала. Цена названа: новое значение любого из них потребует нового шага схемы, а применённый шаг не переписывается. Прочие перечни остаются обычнымTEXT. - Временные метки —
TEXTв RFC 3339, UTC (суффиксZ), например2006-01-02T15:04:05Z(секундная точность). Фиксированная ширина сохраняет лексикографическую сортировку TEXT, то есть хронологию (ORDER BY created_at). Единая точка генерации — приложение, а не умолчание в схеме: так забытая вставка падает громко. Измерение длительности — не метка времени. Расхождение: вид времени задаёт хранилище —2006-01-02 15:04:05.000Z, пробел вместоTи доли секунды (../database.md, «Время»). Правило RFC 3339 действует на то, что пишем мы сами мимо хранилища; вид хранилища не меняем — сравнение строк в сыром запросе побайтово, и разошедшийся вид молча обращает условие срока захвата в константу. - Миграции — шаги PocketBase на Go
(
internal/adapter/repo/pocketbase/migrations, файл на шаг): коллекции и их поля заводятся кодом. При изменении структуры обновляем схему ../database.md тем же изменением. - Время в сыром запросе кладётся и сравнивается тем же видом, каким
хранилище пишет свои
created/updated. Сравнение строк побайтово, и разошедшийся вид обращает условие в постоянную истину или ложь — молча. - Выборка «следующей» записи с
LIMIT 1дополняется ключом вORDER BY: сравнение по неуникальному значению делает порядок обработки невоспроизводимым.