# PocketBase: умолчания, которые ломают штатный сценарий Наблюдения, снятые по ходу задачи `pocketbase-storage` уже на своём коде. От [записки разведки](pocketbase.md) отличаются предметом: та мерила, **что даёт панель**, эта — **что библиотека делает молча**, если её не переубедить. Все четыре наблюдения нашлись ревью, а не чтением документации: три из них выглядят как «значение по умолчанию — нет ограничения», а значат обратное. ## Как снималось Версия **0.39.10**, та же, что у первой записки. Прогоны — на пустом каталоге данных во временном каталоге и на поднятом сервере `127.0.0.1:18099`; боевые данные и ключи не участвовали. Числа ниже сняты 2026-08-11 и 2026-08-12. ## Нулевой потолок у поля файла значит 5 МиБ, а не «без предела» `&core.FileField{MaxSize: 0}` читается библиотекой как её собственное умолчание: ``` core/field_file.go:28 const DefaultFileFieldMaxSize int64 = 5 << 20 core/field_file.go:310 if f.MaxSize <= 0 { return DefaultFileFieldMaxSize } ``` Проверено укладкой: файл в 6 МиБ отвергается на сохранении записи — `the maximum allowed file size is 5242880 bytes`. Прогон через боевой роутер дал границу дословно: | тело запроса | ответ | | --- | --- | | 4 194 304 байта | `201` | | 5 238 784 байта | `201` | | 5 246 976 байт | `500` | | 34 603 008 байт | `413` | **Цена для сервиса:** 5 МиБ — это примерно 5,5 минут mp3 при 128 кбит/с. Отвергалась бы не только длинная запись на приёме: результат конвертации в ogg переваливает тот же порог примерно на пятой минуте, и **уже принятая** задача исчерпывала бы попытки на шаге конвертации. ## Тело запроса режется на 32 МиБ раньше обработчика `apis/base.go:36` вешает `BodyLimit(DefaultMaxBodySize)` на **корневой** роутер, то есть и на чужие маршруты; `apis/middlewares_body_limit.go:14` — `const DefaultMaxBodySize int64 = 32 << 20`. Ответ `413` уходит мимо обработчика, без строки в журнале приёма (последняя строка таблицы выше). Снимается на маршруте: `.Bind(apis.BodyLimit(<своё число>))`. ## Таймаут чтения запроса — пять минут `apis/serve.go:151` ставит `ReadTimeout: 5 * time.Minute`. Заливка шестичасовой записи по медленному каналу его переживает: соединение рвётся на середине. Снимается в хуке `OnServe` — `se.Server.ReadTimeout = 0`. ## Суффикс к имени файла дописывает конструктор, а не укладка Первая записка наблюдала `sample.ogg → sample_uztrv6wvz3.ogg` и читала это как свойство хранилища. Наблюдение верно **только когда имя строит сама библиотека**: десять случайных знаков добавляет `normalizeName`, вызываемый из `filesystem.NewFileFrom*`. Имя, положенное в поле `File.Name` после конструктора, ложится на диск дословно: ``` задано 11111111-2222-3333-4444-555555555555.mp3 на диске 11111111-2222-3333-4444-555555555555.mp3 ``` **Цена:** тот, кто задаёт имя сам, не получает от суффикса никакой неугадываемости — и защищать ссылку на файл ему приходится другим. ## Хук правки записи не различает, кто пишет `app.OnRecordUpdate(<коллекция>)` — событие **модели**: оно срабатывает на каждом `app.Save`, включая сохранение из собственного кода. Хук, написанный «для панели», правил записи конвейера: проверено прогоном — задержка, поставленная шагом вместе со сменой состояния, обнулялась тем же сохранением. Различает источник `app.OnRecordUpdateRequest(<коллекция>)`: оно поднимается только на правку запросом, а код, пишущий мимо HTTP-слоя, под него не попадает. ## Приглашение завести владельца панели живёт полчаса `apis/installer.go:31` — `systemSuperuser.NewStaticAuthToken(30 * time.Minute)`; печатается только пока владельца нет (`needInstallerSuperuser`). Проверено прогоном: при первом запуске строка со ссылкой в журнале есть, после заведения владельца при следующем запуске её нет. ## Чего эта записка не узнала - **Во что обходится потолок в 8 ГиБ на диске.** Число выбрано расчётом из шестичасовой записи с запасом на видео, а не замером: настоящего распределения длин у сервиса нет. - **Как ведёт себя укладка файла в несколько гигабайт.** Самая длинная проверенная запись — 9,6 МБ (десять минут mp3). Потоковую укладку это подтверждает, предел — нет. - **Поведение под одновременной правкой панели и конвейера в бою.** Проверено тестом на одной машине, не живой нагрузкой.