Переход на более крупные рабочие пространства без ожидания для кого-либо
У каждого проекта Mythex есть собственное рабочее пространство: небольшой облачный компьютер, на котором агент пишет код и запускается превью вашего приложения. Мы подняли объём памяти новых рабочих пространств с 1 ГБ до 2 ГБ, но старые остались на 1 ГБ — в день проверки таких было примерно четверть. Наше первое исправление переносило проект в более крупное рабочее пространство при следующем открытии. В первый же день в продакшене оно заставило одного человека ждать семь минут. Мы поставили жёсткий лимит времени, а затем вынесли перенос из открытия проекта вообще.
Почему старые рабочие пространства остались маленькими
Наш провайдер песочниц задаёт размер рабочего пространства при создании и сохраняет его через все паузы и возобновления. Изменение шаблона, то есть рецепта, по которому создаются новые рабочие пространства, влияет только на рабочие пространства, созданные после изменения. Существующие никто не меняет.
Это имело значение. Одно из маленьких рабочих пространств исчерпало память во время git push, пока запускалось его превью, и 11 минут не отвечало. Из-за этого публикация завершилась с ошибкой.
Первая попытка: переносить проект при пробуждении
Рабочие пространства приостанавливаются, когда ими никто не пользуется, и просыпаются, когда открывают проект. Пробуждение казалось подходящим моментом для переноса, потому что в проекте ещё никто не работает.
Перенос работал так:
- Проверить размер. Если рабочее пространство меньше 2 ГБ, перенести его. Если размер неизвестен, ничего не делать.
- Не мешать публикации. Публикация читает файлы рабочего пространства, поэтому если она идёт, перенос пропускается.
- Упаковать проект. Заархивировать всё содержимое проекта, включая историю git, то есть все контрольные точки проекта, и файл
.envприложения, который принадлежит владельцу, но не включать установленные зависимости (node_modules), которые можно переустановить. - Скопировать напрямую. Передать архив из старого рабочего пространства в новое, на 2 ГБ, потоком, чтобы он никогда не помещался в память нашего собственного сервера.
- Сравнить перед переключением. В обоих рабочих пространствах посчитать файлы, прочитать последнюю контрольную точку и посчитать контрольные точки. Переключаться, только если все три значения совпали.
- При любой ошибке оставить старое. Любая ошибка или несовпадение удаляет новое рабочее пространство и открывает проект в старом, как и раньше.
Одна деталь сравнения оказалась важнее, чем выглядит. Проверка выполняется от имени административной учётной записи, а git отказывается читать репозиторий, принадлежащий другому пользователю. В обеих сторонах он не выводил ничего — и «ничего» совпало с «ничего», так что проверка прошла, ни разу не сравнив историю. Мы указали git доверять этой директории и добавили тест, чтобы такое не повторилось.
Что пошло не так в первый день
В первый день в продакшене упаковка одного проекта упёрлась в таймаут команды в пять минут — прямо во время пробуждения. Владелец ждал семь минут, пока его рабочее пространство откроется. А поскольку перенос провалился, проект остался маленьким, и при следующем открытии перенос попробовал бы снова и снова заставил их ждать.
Миграция, которая заставляет человека ждать при открытии собственного проекта, — плохая сделка. Весь смысл был в том, чтобы делать это при пробуждении, и никто ничего не заметит.
Вторая попытка: жёсткий бюджет времени
В тот же день мы поменяли четыре вещи:
- Одна минута на весь перенос. Каждый шаг — проверка размера, упаковка, создание нового рабочего пространства, копирование, распаковка, сравнение — делит один бюджет в 60 секунд. Когда он заканчивается, проект открывается в старом рабочем пространстве.
- Останавливать работу, а не только ожидание. Сдаться с нашей стороны недостаточно, если архивация продолжает работать внутри рабочего пространства, которым вот-вот начнёт пользоваться владелец. Каждая команда архивации теперь убивается внутри самого рабочего пространства незадолго до конца бюджета. Если новое рабочее пространство появилось после того, как мы сдались, его выбрасывают.
- Не заставлять владельца ждать очистки. Удаление временного архива теперь происходит в фоне.
- Не пытаться снова при каждом открытии. Проект, который не удалось перенести, не трогают 24 часа, так что сбой стоит не более одной задержки в день, а не по одной на каждое открытие.
Третья попытка: вынести из пробуждения
Это сделало худший случай равным одной минуте. Но минута всё равно минута, и мы поняли, что перенос вообще не должен стоять на чьём-либо пути.
Теперь ни одно рабочее пространство не создаётся на 1 ГБ. Значит, это не постоянный процесс, а разовая задача с фиксированным списком рабочих пространств. Выполнять её внутри пробуждения означало, что каждый медленный перенос оплачивал реальный человек, а узнать, что рабочее пространство не переносится, мы могли только тогда, когда кто-то только что на него ждал.
Поэтому мы убрали перенос из пробуждения полностью. Открытие проекта работает как и раньше. Оставшиеся маленькие рабочие пространства обрабатываются как контролируемая пакетная операция: никто на неё не ждёт, а рабочее пространство, которое не переносится, попадает в отчёт пакета, где мы можем на него посмотреть, а не перед глазами владельца.
Что мы из этого вынесли
- Изменить размер не всегда возможно. Если платформа фиксирует размер при создании, повышение значения по умолчанию помогает только новым объектам. Планируйте для старых.
- «Пока никто не использует» всё равно значит, что кто-то ждёт. Работа, выполняемая на пути к продукту, — это работа, которую видит человек.
- Ставьте бюджет на всё, что стоит на пути пользователя, и останавливайте работу, когда бюджет закончился. Таймаут на нашей стороне, пока команда продолжает работать в другом месте, лишь переносит проблему.
- Разовая миграция относится к разовой задаче. Спрятанная в повседневных действиях, она дешевле в написании и дороже в эксплуатации.
- Тестируйте свои проверки безопасности. Сравнение, которое ничего не сравнивает, выглядит точно так же, как успешное.
Если вы строите на Mythex, делать ничего не нужно: цель — просто меньше рабочих пространств, которым не хватает памяти, и открытие проекта такое же быстрое, как прежде. Как рабочие пространства приостанавливаются и пробуждаются, описано в документации в разделе приватное рабочее пространство. Ещё одну историю про нехватку памяти читайте в статье сбой из-за нехватки памяти, спрятанный в подписи прогресса.