хранилище переехало с PocketBase на SQLite со своим каталогом файлов
- база своя: два пула, захват одним UPDATE ... RETURNING, шаги схемы на goose под файловым замком, одна миграция начальной схемы вместо семи прежних - транспорт переписан на net/http: свои слои, свой ограничитель частоты, отдача файла с проверкой владельца; панель /_/ и пространство /api/ исчезли - по находкам ревью: журнал не пишет путь под корнем приложения, ключ бюджета читается справа налево, узнавание известного идёт читающим пулом
This commit is contained in:
@@ -2,11 +2,11 @@
|
||||
|
||||
Как мы устраиваем таблицы и ключи. Актуальная схема — [../database.md](../database.md).
|
||||
|
||||
**Взято из проекта jellybit целиком.** Сегодняшний код transcriber следует
|
||||
этому частью: ключи — UUID v4, а не ULID, и единой точки их генерации нет. Время
|
||||
единой точкой читается с 2026-08-13 — `internal/clock`, метка в UTC, — и правило
|
||||
держит линтер. Правила действуют на новый код; переписывание существующего —
|
||||
отдельная работа, и до неё расхождение читается как долг, а не как нарушение.
|
||||
**Взято из проекта jellybit целиком.** Сегодняшний код transcriber следует этому
|
||||
целиком: ключи — ULID в нижнем регистре, выдаёт их единая точка `internal/ident`
|
||||
(с 2026-08-22, задача `storage-without-pocketbase`), время читает единая точка
|
||||
`internal/clock` (с 2026-08-13), и правило времени держит линтер. Расхождений у
|
||||
записи не осталось.
|
||||
|
||||
**Механизировано:** сверка изменённого шага схемы с
|
||||
[../database.md](../database.md) (`docs.py check`), чтение времени единой точкой
|
||||
@@ -17,17 +17,17 @@
|
||||
## Первичные ключи — ULID, не автоинкремент
|
||||
|
||||
- **PK сущности — TEXT ULID** (26 символов Crockford base32), генерируется
|
||||
**приложением** в момент создания записи.
|
||||
*Расхождение:* идентификаторы записей выдаёт хранилище — 15 знаков
|
||||
собственного алфавита. Своей точки генерации у приложения нет, и `ORDER BY id`
|
||||
хронологией не является: порядок берут по колонке времени с ключом.
|
||||
**приложением** в момент создания записи. Выдача монотонна внутри одной
|
||||
миллисекунды: колонка времени несёт секунды, и порядок записей одной секунды
|
||||
задаёт ключ. Порядок ленты берут парой «время заведения и ключ» — одного
|
||||
времени мало.
|
||||
- Почему ULID: сортируем по времени создания (`ORDER BY id` = хронология),
|
||||
компактен и удобен в URL и логах (без дефисов — grep и двойной клик берут id
|
||||
целиком), глобально уникален между таблицами — поиск по голому id находит все
|
||||
записи сущности в логах.
|
||||
- **Точка генерации и разбора одна**: создание — при вставке записи в
|
||||
репозитории, разбор — на входных границах. Самодельных генераторов по месту
|
||||
вызова не заводим.
|
||||
- **Точка генерации и разбора одна** — `internal/ident`: `New` выдаёт, `Parse`
|
||||
разбирает пришедшее снаружи. Самодельных генераторов по месту вызова не
|
||||
заводим.
|
||||
|
||||
## Канонический вид — lowercase
|
||||
|
||||
@@ -47,31 +47,28 @@
|
||||
|
||||
## Прочее
|
||||
|
||||
- Enum-поля (`state`, `source`, …) — обычный `TEXT` без `CHECK`; допустимые
|
||||
значения держит код.
|
||||
*Расхождение:* перечни, по которым панель владельца правит запись руками,
|
||||
закрыты схемой (`SelectField`), а не кодом: правка руками не должна заводить
|
||||
значение, которого сервис не знает. Закрыты рубеж записи, причина её
|
||||
остановки, вид текста, источник и исход события журнала. Цена названа: новое
|
||||
значение любого из них потребует нового шага схемы, а применённый шаг не
|
||||
переписывается. Прочие перечни остаются обычным `TEXT`.
|
||||
- Enum-поля (`state`, `halt_reason`, …) — обычный `TEXT` без `CHECK`; допустимые
|
||||
значения держит код. Прежде часть перечней закрывала схема — правку руками вела
|
||||
панель владельца, и она вправе была завести значение, которого сервис не
|
||||
знает. Панели нет с 2026-08-22, правка идёт только нашим кодом, и закрытый
|
||||
перечень в схеме остался бы ценой — новое значение стоило бы нового шага — без
|
||||
покупателя.
|
||||
- Временные метки — `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](../database.md), «Время»).
|
||||
Правило RFC 3339 действует на то, что пишем мы сами мимо хранилища; вид
|
||||
хранилища не меняем — сравнение строк в сыром запросе побайтово, и
|
||||
разошедшийся вид молча обращает условие срока захвата в константу.
|
||||
- Миграции — шаги PocketBase на Go
|
||||
(`internal/adapter/repo/pocketbase/migrations`, файл на шаг): коллекции и их
|
||||
поля заводятся кодом. При изменении структуры обновляем схему
|
||||
[../database.md](../database.md) тем же изменением.
|
||||
- Время в **сыром запросе** кладётся и сравнивается тем же видом, каким
|
||||
хранилище пишет свои `created`/`updated`. Сравнение строк побайтово, и
|
||||
разошедшийся вид обращает условие в постоянную истину или ложь — молча.
|
||||
Умолчаний вида `CURRENT_TIMESTAMP` в схеме нет ни у одной колонки, и вид один
|
||||
на все — включая те, что пишет только сам сервис: своего типа времени у SQLite
|
||||
нет, а колонка, заполненная то одним видом, то другим, молча обращает условие
|
||||
срока захвата в константу.
|
||||
- Миграции — шаги `pressly/goose/v3` на Go
|
||||
(`internal/adapter/repo/sqlite/migrations`, файл на шаг, версия — число в
|
||||
начале имени): таблицы, их колонки и индексы заводятся кодом. При изменении
|
||||
структуры обновляем схему [../database.md](../database.md) тем же изменением.
|
||||
- Время в запросе кладётся и сравнивается тем же видом, каким оно лежит в
|
||||
колонке. Сравнение строк побайтово, и разошедшийся вид обращает условие в
|
||||
постоянную истину или ложь — молча.
|
||||
- Выборка «следующей» записи с `LIMIT 1` дополняется ключом в `ORDER BY`:
|
||||
сравнение по неуникальному значению делает порядок обработки
|
||||
невоспроизводимым.
|
||||
|
||||
Reference in New Issue
Block a user