Мы выпустили поддержку beads v1.0.0. Потребовались откат, баг flock и 6 хотфиксов.
2 апреля beads выпустил v1.0.0. Главная фича — embedded Dolt: бэкенд без конфигурации, который запускает базу данных внутри процесса, без отдельного сервера. Для одиночных разработчиков это было обещание: bd init — и всё. Никаких портов, демонов, настроек.
Мы сразу начали добавлять поддержку в Beadbox. Шесть хотфиксов, публичный откат и глубокое погружение в исходный код bd — и мы вышли на другой стороне со слоем отказоустойчивости, который, вероятно, нужно было построить ещё месяцы назад.
Утро до того, как всё сломалось
День начался чисто. Мы провели охоту на мёртвый код по всей кодовой базе и выпустили v0.20.0 с удалением 5 350 строк и улучшением холодного запуска на 2 секунды. Сорок два bead закрыты. Хорошее утро.
Затем мы обновили bd до 0.63.3 — первый релиз, построенный на embedded Dolt бэкенде beads v1.0.0.
Beadbox не смог найти базу данных. Embedded-режим хранит данные в .beads/embeddeddolt/ вместо .beads/dolt/. Имя базы тоже изменилось — с захардкоженного beads на префикс проекта из metadata.json. А bd sql, который наш WebSocket-сервер использовал для O(1)-обнаружения изменений через DOLT_HASHOF_TABLE, вообще не поддерживается в embedded-режиме.
v0.20.1 добавил сохранение учётных данных через keychain ОС (шесть bead работы, уже в процессе), исправил баг фильтра пользовательских статусов и пропатчил проблемы, специфичные для Windows.
v0.20.2 научил Beadbox читать dolt_database из metadata.json, чтобы находить переименованную базу.
v0.20.3 добавил guard'ы для embedded-режима. Каждый вызов bd sql был обёрнут проверкой: если мы в embedded-режиме, откатываемся на CLI-опрос вместо прямых SQL-запросов. Функция getDoltDir научилась сначала искать в embeddeddolt/.
v0.20.4 исправил нормализацию путей --db для embedded-layout. Пути, которые работали со старой структурой каталогов, ломались с новой.
Каждое исправление вскрывало следующую проблему.
Flock
После v0.20.4 мы думали, что стабилизировались. Потом запустили простой тест на конкурентность: пять вызовов bd list одновременно.
Четыре из них упали.
Embedded Dolt захватывает эксклюзивную файловую блокировку (flock) на базу данных на всё время жизни каждой команды. От PersistentPreRun до PersistentPostRun ничто другое не может к ней обратиться. Это задумано. Без этого конкурентная инициализация движка вызывает nil-pointer panic (beads#2571). Flock предотвращает краш. Но это также означает, что в embedded-режиме bd фактически однопроцессный.
Beadbox не однопроцессный. Наш WebSocket-сервер опрашивает изменения каждую секунду. UI запускает несколько server action при загрузке страницы. Пользователь, кликающий по приложению, пока фоновый поллер работает, будет генерировать конкурентные вызовы bd. Flock блокирует все, кроме первого.
Пост в блоге DoltHub об embedded-реализации описывал предполагаемое поведение: конкурентные вызывающие должны «выстраиваться в очередь естественным образом с экспоненциальным откатом». Но arch проверил выпущенный исходный код и обнаружил, что bd использует TryLock с LOCK_NB (неблокирующий). Он не ждёт. Он немедленно падает. Есть два уровня блокировки: flock bd сверху и backoff на уровне драйвера Dolt снизу. Первый уровень замыкает второй. Логика повторов существует в кодовой базе, но никогда не выполняется, потому что flock отклоняет соединение до того, как backoff Dolt получит шанс запуститься.
Исправление (разделяемые блокировки для операций чтения через FlockSharedNonBlock) существует в исходном коде bd. Просто ещё не подключено.
Именно эту проблему решает Beadbox.
Видимость в реальном времени того, что делает весь ваш флот агентов.
Можно было продолжать выпускать хотфиксы по движущейся мишени или отступить и построить нормальный слой отказоустойчивости. Мы отступили.
Все релизы v0.20.x были убраны из публичного репозитория. v0.19.0 вернулась как рекомендуемая версия. Мы опубликовали обсуждение, объясняющее, что произошло и что делать, и добавили баннер на beadbox.app. Тридцать минут от решения до завершения.
Каждый час, пока сломанный релиз остаётся публичным — это час, когда кто-то его скачивает, натыкается на проблему flock и винит продукт. Мы предпочитаем объяснить откат, чем отлаживать чужой плохой первый опыт.
Мы были не одиноки
Пока мы отлаживали, пользователь beads по имени Kevin опубликовал beads#2938: «Beads feels painful to use». Он потратил 9,5 часов на отладку проблем, которые включали ту же путаницу embedded-vs-server, с которой столкнулись мы. Обновление до v1.0.0 молча переключило его workspace с серверного на embedded-режим (beads#2949), спрятав существующие issues за свежей пустой базой данных.
9,5 часов. Опытный пользователь, не новичок. Если такой опыт у того, кто хорошо знает beads, проблема не в пользователе. Проблема в пути миграции.
Что мы построили для v0.21.0
Вместо того чтобы патчить отдельные сбои, мы построили слой, который воспринимает конкуренцию за блокировки как нормальное рабочее условие.
Flock retry с экспоненциальным откатом. Каждый вызов CLI bd повторяет попытку до 5 раз, от 100мс до 1,6 секунды между попытками. Живёт в одном месте в lib/bd.ts, поэтому каждая команда получает это бесплатно. Покрывает типичный случай: два вызова сталкиваются, один ждёт немного, оба успешны.
UI мягкой деградации. Конкуренция за блокировки больше не означает экран ошибки. Приложение показывает устаревшие данные с индикатором обновления. Если конкуренция сохраняется больше 30 секунд, янтарный баннер объясняет ситуацию. Когда блокировка снимается, баннер исчезает и данные обновляются автоматически.
Предложение auto-promote. Повторяющаяся конкуренция запускает предложение мигрировать на серверный режим: бэкап, реинициализация с --server, восстановление. Один клик. Это правильный ответ для любого, кто запускает Beadbox вместе с другими потребителями bd, и теперь приложение само об этом говорит.
Обнаружение embedded-режима.getDoltDir проверяет embeddeddolt/ и маршрутизирует соответственно. Вызовы bd sql защищены. WebSocket-пайплайн откатывается на CLI-опрос в embedded-режиме (медленнее, но уважает однопроцессное ограничение).
Что мы поняли
Embedded Dolt однопроцессный по замыслу. Это не баг. Flock предотвращает настоящие паники. Любой инструмент, одновременно потребляющий workspace beads, должен сериализовать доступ или работать в серверном режиме. Для Beadbox серверный режим — правильное значение по умолчанию. Embedded работает при лёгком использовании, когда слой повторов поглощает случайные столкновения.
Документация описывала намерение, а не реализацию. Блог DoltHub говорил про backoff. Код говорил TryLock с LOCK_NB. Мы потратили время, предполагая, что конкурентные чтения должны работать, потому что документация так говорила. Чтение исходного кода разрешило путаницу за минуты. Когда поведение не совпадает с документацией, читайте код.
Тестируйте конкурентность до релиза. Мы не запускали конкурентные вызовы bd, пока v0.20.4 не стал публичным. for i in {1..5}; do bd list & done; wait поймал бы проблему flock до любого релиза. Пять секунд тестирования сэкономили бы нам откат.
Откатывайтесь рано. Инстинкт продолжать двигаться вперёд силён. Вы близко, видите исправление, ещё один релиз. Но каждый сломанный релиз, остающийся публичным — это изъятие доверия, которое нелегко отменить. Возврат к v0.19.0 дал нам пространство построить слой отказоустойчивости правильно, а не выпускать его паническими порциями.
Проверяйте переменные окружения. Мы потеряли часы из-за того, что BEADS_DIR указывал bd на неправильный workspace. bd обнаруживал другую базу данных, не ту, которую Beadbox мониторил, и симптомы выглядели как повреждение данных. Если ваши команды bd возвращают неожиданные результаты, env | grep BEADS перед всем остальным.
Текущее состояние
v0.21.0 вышел с поддержкой beads v1.0.0, слоем отказоустойчивости и сохранением учётных данных через keychain ОС. Обсуждение релиза содержит полные детали.
Если вы на beads v1.0.0 с embedded-режимом и сталкиваетесь с периодическими сбоями, слой повторов v0.21.0 должен справиться. Если вы запускаете Beadbox вместе с другими инструментами, обращающимися к тому же workspace, переключитесь на серверный режим. Поток auto-promote делает это в один клик.
И если вы Steve или кто-то из команды beads, кто это читает: разделяемые flock для чтений исправили бы корневую причину upstream. beads#2939 (Unix domain sockets) также сделали бы локальные соединения чище. Мы продолжим строить вокруг того, что будет выпущено.
Попробуйте сами
Начните с beads как координационного слоя. Добавьте Beadbox, когда понадобится визуальный контроль.
Бесплатно в период бета-тестирования. Учётная запись не требуется. Нативная поддержка Dolt.