Тридцать первое число, 23:40. Бухгалтерский отчёт по заказам формируется с двадцати трёх часов, потому что так договорились три года назад. Рассылка с итогами месяца уходит в полночь: маркетинг хотел, чтобы письмо пришло «в первую минуту нового месяца». А в 00:00 по расписанию стартует полное копирование каталога и базы. Три работы, которые по отдельности никому не мешали, встречаются на одном VPS в одном и том же узком окне. Каждая из них тихо считает, что сервер принадлежит только ей.

Вопрос обычно формулируют так: «Не пора ли взять тариф побольше?». Но это второй вопрос. Первый звучит иначе: а все ли три работы действительно должны выполняться именно сейчас? Ниже разберём, как это выяснить, чем перестановка работ отличается от добавления ресурсов и сколько стоит каждый путь. Все числа в примерах условные: они нужны, чтобы показать способ рассуждения, а не чтобы предсказать вашу нагрузку.

Календарь совпадений: сначала рисуем, потом решаем

Пик раз в месяц коварен тем, что его трудно наблюдать. Обычные дни выглядят спокойно, график недельной нагрузки ничего не подскажет, а за тот единственный вечер, когда всё тормозит, никто не успевает разобраться, кто именно съел ресурсы. Поэтому полезно начать не с мониторинга, а с бумажного календаря: выписать все работы, которые бывают на сервере в конце и начале месяца, и для каждой записать три вещи — когда стартует, сколько примерно идёт и какой ресурс нагружает главным образом.

Последнее важнее, чем кажется. Работы конкурируют не «вообще», а за конкретные ресурсы: процессорное время, память, диск, сеть. Две тяжёлые работы с разными профилями могут мирно ужиться, а две лёгкие с одинаковым — мешать друг другу.

Возьмём учебный пример. Интернет-магазин на одном VPS: веб-приложение, база данных, фоновый исполнитель задач.

Три работы месяца: условные длительность и профиль
РаботаСтарт по расписаниюДлительность в одиночкуЧто нагружает
Отчёт по заказам23:0090 минутCPU и память (агрегации, сборка файла)
Рассылка итогов00:0060 минутСеть и очередь исходящих писем, немного CPU
Полное копирование00:0075 минутДиск (чтение и запись), немного CPU на сжатие

Теперь нарисуем временную диаграмму. Каждый символ — пятнадцать минут, звёздочка означает, что работа идёт.

Время:       23:00 23:30 00:00 00:30 01:00 01:30 02:00
Отчёт:       ****  ****  ****  **
Рассылка:                ****  ****
Копирование:             ****  ****  *

Накладывается в 00:00–00:30: все три работы
(отчёт ещё идёт, рассылка и копирование стартовали)

Видно главное: пик не в том, что всего много, а в том, что в получасовом окне после полуночи сошлись три потребителя. Отчёт к этому моменту отработал полтора часа с лишним по плану, но на деле при конкуренции он затянется — и будет мешать дольше, чем рассчитано. Это обычное свойство совпадающих работ: каждая замедляет остальных, и совместное время оказывается больше, чем кажется по сумме.

Заметьте, что у нас нет ни одной цифры загрузки процессора. Для первого решения она и не нужна. Диаграмма уже подсказывает вопрос: что будет, если развести эти три полосы?

Срок или запас: кто действительно торопится

Любую работу можно спросить: что случится, если она закончится на три часа позже? Ответ почти всегда расслаивает список на два класса.

Задачи с жёстким сроком. Их результат нужен к определённому моменту, и опоздание имеет цену. Например, если по договору отчёт для бухгалтерии нужен до девяти утра первого числа, то у него срок жёсткий, но, заметим, срок — это утро, а не 23:00. Или если акция начинается в полночь, а рассылка рассказывает о ней, опоздание бьёт по выручке.

Задачи с запасом. Если результат понадобится завтра днём, то нет разницы, готов он в полночь или в пять утра. Копирование данных обычно здесь: важно, чтобы копия появилась и была пригодной, а не чтобы она была готова ровно в 01:15. Однако осторожно: копия должна отражать согласованное состояние, и сдвиг по времени меняет, какое именно состояние она сохранит. К этому вернёмся.

Полезно составить небольшую табличку «не позднее»:

  • Отчёт для бухгалтерии: не позднее 09:00 первого числа (запас — около семи часов после планового окончания).
  • Рассылка итогов: письма должны уходить в окне после закрытия месяца, но «в первую минуту» — пожелание, а не требование. Реальный срок — например, до 08:00, чтобы письмо не оказалось в почте рядом с рабочим потоком.
  • Копирование: не позднее начала рабочего дня, чтобы в случае чего была свежая копия.

Теперь картина другая. Из трёх работ ни одна не требует выполнения именно в 00:00–00:30. Мы унаследовали это окно от привычки, а не от бизнеса. Возможно, но не обязательно, что в вашем проекте так же; именно поэтому список «не позднее» нужно составлять вместе с людьми, которые пользуются результатами, а не только с администратором.

Есть и обратный случай. Допустим, вы продаёте билеты, и в полночь начинается продажа: тогда пик посетителей — это жёсткая синхронная нагрузка, её нельзя отложить, потому что покупатель ждёт ответа прямо сейчас. Отложить можно фоновую работу, но не ответ на запрос пользователя. Это граница применимости идеи очереди: она работает для того, что человек не ждёт у экрана.

Что даёт перестановка, а что даёт мощность

Теперь сравним два пути на нашей диаграмме.

Путь первый: развести работы по времени

Сдвинем расписание, опираясь на запас каждой работы:

Время:       22:00 22:30 23:00 23:30 00:00 00:30 01:00 01:30 02:00 02:30
Отчёт:       ****  ****  ****  **
Копирование:                         ****  ****  *
Рассылка:                                              ****  ****

Отчёт стартует раньше, но только если данные месяца к тому моменту уже закрыты — иначе отчёт получится неполным. Это важная оговорка: сдвигать вперёд можно не всё, а только то, что не зависит от конца суток. Поэтому чаще сдвигают назад: отчёт стартует в 01:00, а не в 23:00.

Переставим по-другому, более реалистично:

Время:       00:00 00:30 01:00 01:30 02:00 02:30 03:00 03:30 04:00
Копирование: ****  ****  *
Отчёт:                   ****  ****  ****  **
Рассылка:                                      ****  ****

Копирование идёт первым, пока на диск не пишут новую нагрузку отчётом. Отчёт стартует после него и забирает CPU и память. Рассылка стартует, когда отчёт уже закончился, и использует в основном сеть. Каждая работа идёт примерно за своё одиночное время. Всё заканчивается к 04:00, срок «до 08:00» и «до 09:00» выдержан с запасом. Тариф не менялся, ни одного дополнительного ядра.

Цена такого пути — не деньги, а дисциплина: нужен механизм, который гарантирует порядок. Обычный cron умеет запускать в заданное время, но не умеет «запустить после завершения предыдущей». Если копирование однажды затянется, отчёт стартует поверх него и вы вернётесь к исходной ситуации. Поэтому разумнее связать работы зависимостью или очередью, чем надеяться на удачные интервалы.

Путь второй: добавить ресурсы

Другой подход — оставить расписание и дать серверу больше мощности, чтобы три работы переварились одновременно. На VPS-тарифе ресурсы выражаются в vCPU, оперативной памяти и размере диска. В линейке MAATRIX это, например, 1 vCPU и 2 ГБ у Ba, 2 vCPU и 4 ГБ у Uab, 4 vCPU и 8 ГБ у Heka и так далее вплоть до 16 vCPU и 32 ГБ у Djed. Эти цифры — характеристики плана, а не обещание, что на нём уложится конкретное число работ или заказов; отправная точка — собственный тест.

Что реально улучшает больший тариф для нашей диаграммы? Не все ресурсы помогают одинаково:

  • Больше vCPU ускоряет отчёт и сжатие при копировании, потому что они упираются в процессор. Но сам по себе отчёт, написанный как один последовательный процесс, может не воспользоваться несколькими ядрами: он будет занимать одно, а остальные простоят.
  • Больше памяти снижает риск того, что отчёт и приложение вытеснят друг друга. Это важно, если нехватка памяти приводит к подкачке или аварийной остановке процессов.
  • Скорость диска не растёт пропорционально числу ядер. Если копирование упирается в ввод-вывод, то добавление vCPU его почти не ускорит, а вот одновременное чтение базы отчётом и копированием по-прежнему будет их тормозить. В предложении MAATRIX используется NVMe, что обычно полезно для дисковой нагрузки, но это не отменяет общей конкуренции за диск между работами.
  • Сеть рассылки ограничена не нашим процессором, а скоростью приёма внешними почтовыми сервисами и их лимитами. Мощный сервер не заставит чужой сервер принимать письма быстрее.

Иными словами, мощность лечит те проблемы, которые связаны с нехваткой процессора и памяти, и слабо лечит те, что связаны с диском и внешними ограничениями. Диаграмма профилей, которую мы нарисовали в начале, подсказывает, какой случай ваш.

Общее правило сравнения

Перестановка бесплатна по деньгам, но требует работы и аккуратности. Увеличение тарифа почти бесплатно по усилиям, но оплачивает постоянный запас ради нескольких часов в месяц. Если пик короткий и все работы имеют запас, перестановка выигрывает. Если часть работ имеет жёсткий срок и они действительно тяжёлые и одновременные, без мощности не обойтись. Чаще всего правильный ответ смешанный: переставить то, что можно, и посмотреть, какой остаток пика всё ещё не помещается.

Запас ресурсов в процессе, а не только на сервере

Между «ничего не менять» и «купить тариф крупнее» есть промежуточный слой: управлять тем, как работы делят уже имеющиеся ресурсы. Если приложение работает в контейнерах, стоит помнить, что по документации Docker контейнер по умолчанию не имеет ресурсных ограничений и может занять столько ресурсов хоста, сколько доступно; ограничения процессора и памяти настраиваются отдельно (Docker resource constraints).

Для нашего примера это значит вот что. Если отчёт запущен в том же контейнере или хотя бы без ограничений рядом с веб-приложением, то в момент аппетита он способен отнять у сайта ресурсы. Покупатель в ночные часы немногочислен, но он есть, и его запрос должен выполняться. Ограничение процессора и памяти для контейнера отчёта защищает сайт ценой того, что сам отчёт пойдёт медленнее. Для работы с запасом по сроку это разумный обмен: отчёт, который закончится в четыре утра вместо трёх, никого не огорчит, а сайт, замерший в полночь, огорчит.

Осторожно с обратным эффектом: слишком жёсткий лимит памяти для отчёта может привести к тому, что процесс будет остановлен при нехватке, а не просто замедлится. Лимит процессора замедляет, лимит памяти способен убить. Поэтому лимиты стоит подбирать на пробном прогоне с реалистичным объёмом данных, а не из общих соображений. Здесь ограничение ресурса — инструмент разделения, а не обещание производительности приложения.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации Россия, США, Франция и Великобритания. Оплата картой РФ и по СБП.

АРЕНДОВАТЬ СЕРВЕР

Очередь: что происходит с отложенной работой

Когда мы переносим работу «на потом», мы, по сути, превращаем её из действия в обещание: вот задача, исполнитель возьмёт её, когда освободится. Это удобно, но у отложенной работы есть собственные последствия, и их стоит проверить заранее, пока вы не оказались в четыре утра перед странным письмом.

Долгие и короткие задачи — разным исполнителям

В документации Celery рекомендуется направлять долгие и короткие задачи разным исполнителям (Celery: выполнение и доставка задач). Причина понятна на нашем примере. Если в одной очереди с одним набором исполнителей стоят и стотысячная рассылка, и мгновенное «отправить пользователю код подтверждения», то код встанет в хвост за письмами. Покупатель ждёт СМС или письмо с кодом, а исполнители заняты месячной рассылкой. Поэтому месячные тяжёлые работы получают отдельную очередь и отдельных исполнителей, а срочные короткие — свою. Тогда пик месяца не крадёт мгновенность у обычной работы сайта.

Это тоже вопрос ресурсов VPS: два набора исполнителей конкурируют за те же vCPU и память, и без ограничений тяжёлый набор всё равно вытеснит лёгкий. Разделение очередей отвечает за порядок, а лимиты ресурсов — за справедливость в расходе процессора.

Повторы и идемпотентность

Отложенная работа может быть выполнена повторно: исполнитель упал на середине, очередь вернула задачу, и она ушла другому. Документация Celery подчёркивает, что идемпотентность задачи важна именно при повторном выполнении, и что момент подтверждения сообщения отличается от фактического завершения бизнес-операции; настройка acks_late меняет момент подтверждения, но у неё есть исключения при завершении процесса.

Для рассылки это не абстракция. Представим: исполнитель отправил четыре тысячи писем из десяти, затем процесс оборвался. Задача вернулась в очередь. Если она не идемпотентна, часть подписчиков получит итоговое письмо дважды, а ночью месячной рассылки это самая неприятная ошибка. Поэтому у рассылки должна быть отметка «этому получателю письмо уже отправлено», хранящаяся вместе с данными, а не только в памяти задачи. Тогда повторный запуск пропустит уже обслуженных и дойдёт до остальных.

Заметьте тонкость: просто присвоить каждой попытке новый идентификатор недостаточно. Идентификатор попытки не говорит, что именно эта операция уже была сделана раньше; нужен устойчивый ключ самой бизнес-операции — например, пара «получатель и период рассылки».

Для отчёта и копирования идемпотентность проще: повторный запуск перезапишет файл или создаст новую копию с другой меткой. Но и здесь стоит проверить, что недописанный файл не принимается за готовый: результат лучше записывать под временным именем и переименовывать по завершении.

Таймауты внешних сетевых операций

Рассылка зависит от внешнего почтового сервиса, и тот иногда отвечает медленно или не отвечает. В документации Celery сказано, что сетевые операции нуждаются в собственных таймаутах, иначе исполнитель может зависнуть. На диаграмме это превращается в неприятный эффект: если десять исполнителей рассылки подвисли на ожидании, очередь не движется, а ресурсов при этом никто не использует. Сервер выглядит свободным, письма стоят. Увеличение тарифа здесь не поможет совсем — поможет таймаут и повторная попытка с паузой.

Свежесть данных у отложенной работы

Ещё одно последствие — смысловое. Отчёт, запущенный в 01:00, видит данные на 01:00, а не на 23:59. Если между ними кто-то оформил заказ, отчёт его включит или нет в зависимости от того, как вы определили границу периода. Правильный приём — фиксировать границу явно: «заказы до 00:00 первого числа», а не «всё, что есть в базе в момент запуска». Тогда сдвиг старта не меняет содержимое отчёта. То же с копированием: если его отложили на ночь, копия отразит состояние ночи, и это нужно учитывать, когда вы рассчитываете, какой объём данных можно потерять в худшем случае.

Проверка до пика: репетиция вместо надежды

Расписание на бумаге ничего не доказывает. Надо проверить, что новый порядок действительно укладывается в нужные сроки, причём под нагрузкой, похожей на боевую. Здесь полезны инструменты нагрузочного тестирования с критериями прохождения. Например, в Grafana k6 есть thresholds: они задают условия прохождения теста по метрикам вроде задержки или доли ошибок, а при невыполнении условий тест завершается неуспешно (Grafana k6 thresholds).

Как это применить к нашей задаче. Пока в тестовой копии окружения идут отчёт, копирование и рассылка в новом порядке, параллельно подаётся нормальный поток запросов на сайт, и вы заранее формулируете условие: например, «доля ошибок не выше выбранной вами границы, задержка ответа в вашем допустимом пределе». Конкретные значения — ваши, они определяются бизнесом, а не взяты из документации; цифры из примеров документации не являются нормой для вашего сайта. Если условие нарушено, тест красный, и вы знаете, что расписание ещё не годится, а не выясняете это в ночь на первое число.

Репетиция отвечает на три вопроса сразу. Первый: укладываются ли работы в срок «не позднее». Второй: не страдает ли обычная работа сайта на время пика. Третий: что происходит, если одна из работ затянулась вдвое. Третий вопрос самый ценный: расписание, которое выдерживает только идеальный сценарий, слабое. Затяните копирование искусственно и посмотрите, не начнёт ли отчёт стартовать поверх него.

Тест нужно проводить на ресурсах, соответствующих боевым. Репетиция на сервере вдвое мощнее не доказывает ничего про боевой.

Расчёт: сколько стоит каждый путь

Теперь соберём решение в виде расчёта. Цены здесь не приводятся, поэтому будем считать в относительных величинах, а вы подставите свои из тарифной сетки. Условные исходные данные для нашего учебного магазина:

  • Обычный режим: достаточно плана средней ёмкости, назовём его уровнем А.
  • На пик по расписанию «всё в полночь» уровня А не хватает, всё замедляется, срок «до 09:00» под угрозой.
  • Есть уровень Б — следующий план, где ресурсов хватает даже для совпавших работ.

Три варианта:

Три способа пережить месячное окно (условное сравнение)
ВариантЧто платимЧто получаемЧто остаётся рискованным
1. Оставить всё в полночь, взять план Б на постоянной основеРазница между Б и А за каждый месяц, включая 29 спокойных днейЗапас на совпадение работ без переделокДиск и внешние лимиты рассылки всё равно общие; рост данных съест запас
2. Развести работы очередью и сдвигом, остаться на АВремя на настройку зависимостей, раздельных очередей, лимитов, репетициюВсе работы в срок без изменения тарифаНужна дисциплина: затянувшаяся работа ломает порядок, если нет зависимостей
3. Переставить работы и временно перейти на Б только на период пикаРазница между Б и А за период, на который переключились, при условиях смены тарифа у провайдераЗапас на случай, если развести не вышло полностьюНужно знать, как и когда меняется тариф, и успеть переключить заранее

О третьем варианте нужно говорить особенно аккуратно. Он выглядит привлекательно: платим за запас только в месяц пика. Но это работает лишь в той мере, в какой провайдер и ваши условия позволяют менять тариф и как именно тарифицируется переход. Нельзя исходить из того, что ресурсы масштабируются автоматически и мгновенно в момент пика, или что оплачиваются только часы нагрузки. На VPS вы заранее выбираете план, и пик не поднимает его сам. Если смена плана возможна, это решение, которое вы принимаете и исполняете заранее, с запасом времени, а не реакция на пик. Конкретные условия смены, минимальный оплачиваемый период и возможность вернуться на меньший план стоит уточнить в условиях заказа до того, как строить на этом расчёт.

Теперь прикинем в условных «единицах стоимости». Пусть план А стоит 1 единицу в месяц, план Б — 2 единицы.

  • Вариант 1: 2 единицы × 12 месяцев = 24 единицы в год.
  • Вариант 2: 1 единица × 12 = 12 единиц в год плюс разовые усилия на настройку.
  • Вариант 3: при условии, что смена тарифа возможна, а оплачивается вся минимальная единица расчёта (допустим, целый месяц), получается 11 × 1 + 1 × 2 = 13 единиц. Если же минимальный период длиннее, выигрыш сокращается.

Эти числа показывают порядок рассуждения, не реальные цены. Но вывод у них общий: если пик можно разнести, лишние двенадцать единиц в год — это плата за то, чтобы не заниматься расписанием. Иногда эта плата оправдана: если у команды нет времени, а проект приносит больше, чем стоит запас. Но нужно понимать, что вы покупаете удобство, а не необходимость.

Когда разнести нельзя

Есть случаи, где перестановка не поможет, и мощность нужна без вариантов. Признаки:

  • Сама по себе одна работа не укладывается в окно между двумя ограничениями. Например, отчёт один идёт девять часов, а нужен через шесть. Сдвигать некуда, надо ускорять работу: либо оптимизировать запросы, либо давать ей больше процессора.
  • Работы зависят друг от друга цепочкой: рассылка требует готового отчёта, отчёт требует закрытых данных, закрытие данных наступает в полночь. Цепочка длиннее, чем срок, и разводить параллельные ветки нечем.
  • Пик пользовательский, синхронный. Человек ждёт ответа сайта, очередь ему не поможет.
  • Память упирается в потолок: работа просто не помещается в оперативную память плана, и это не вопрос времени, а вопрос размера.

В этих случаях переход на план с большим числом vCPU и объёмом памяти обоснован. Но и тут тест нужен: сначала убедитесь, что именно процессор или память были узким местом, иначе заплатите за ядра, которые будут простаивать.

Третий путь: сделать работу дешевле

Остаётся способ, который редко приходит в голову, пока не посчитаешь: сократить саму работу. Отчёту, который читает всю таблицу заказов, не обязательно перечитывать её целиком каждый месяц, если можно копить промежуточные итоги в течение месяца. Копированию не обязательно каждый раз делать полную копию, если разумно сочетать полные и инкрементальные. Рассылке не обязательно отправлять письма всем в один момент, если разбить её на порции по часам. Каждое такое сокращение уменьшает пик в самом источнике. Оно требует доработок, но иногда одна доработка заменяет и перестановку, и повышение плана.

Организационные мелочи, которые решают исход

Несколько практических вещей, которые часто упускают, хотя они определяют, сработает ли расписание.

Во-первых, назначьте работам собственные учётные записи или хотя бы метки в логах, чтобы после пика можно было восстановить, кто и когда занимал ресурсы. Без этого вы снова будете гадать.

Во-вторых, оставьте человека, который в ночь пика знает, что ожидается. Автоматика хороша, но пустая очередь и очередь, которая просто не движется из-за зависшего исполнителя, выглядят одинаково до утра, если никто не смотрит. Достаточно одной проверки в середине окна: идёт ли хоть одна из работ.

В-третьих, проверьте, что хранилище для копии справляется. Копирование на тот же диск, где лежат данные, защищает от ошибок пользователя, но не от потери самого сервера, поэтому место назначения копии обычно выбирают отдельно. Отдельно подтверждать наличие автоматических резервных копий на стороне тарифа не стоит: в публичном описании такой функции для всех планов не заявлено, так что полагайтесь на собственное копирование и проверяйте, что копию можно восстановить.

В-четвёртых, если пик у вас привязан к смене месяца, а деплой новой версии тоже происходит «раз в месяц», не позволяйте им совпасть. Ручные выкладки в ночь закрытия месяца добавляют лишнюю переменную; о ловушках таких выкладок хорошо написано в материале про ручной деплой по SSH раз в месяц.

Где в этом VPS

Теперь можно связать задачу с ресурсами. На VPS с root-доступом вы сами выбираете, как разделить исполнителей, какие очереди завести, какие лимиты поставить контейнерам и когда запускать работы: это не навязано платформой. Для проекта из нашего примера это значит возможность начать с перестановки и ограничений на уже имеющемся плане, а затем, если репетиция показала нехватку, перейти на план с большим запасом. В линейке MAATRIX разные планы отличаются числом vCPU, памятью и объёмом NVMe-диска, так что можно соотнести результат репетиции с конкретным шагом: если упирались в процессор и память, ищите следующий план по этим параметрам; если в диск или внешние лимиты, прирост ядер ничего не изменит. Размещение возможно в нескольких локациях, а новый VPS создаётся автоматически после оплаты, что удобно, когда нужно подготовить копию окружения для репетиции. Но каждую цифру плана воспринимайте как отправную точку для собственного замера, а не как гарантию ёмкости.

Расписание, которое выдерживает срок

Вернёмся в тридцать первое число. Вместо трёх работ, стартующих вокруг полуночи и толкающихся за одни и те же ресурсы, теперь выстроенная последовательность: копирование стартует сразу после закрытия суток и идёт на отдельном лимите; отчёт стартует после его завершения, с явной границей периода «до 00:00»; рассылка ждёт готового отчёта, идёт в своей очереди порциями, с таймаутами и устойчивым ключом «получатель плюс период». Все три работы заканчиваются заметно раньше крайних сроков 08:00 и 09:00, а если одна затянется вдвое, у остальных остаётся запас. Репетиция с порогами по ошибкам и задержке показала, что сайт в это время отвечает в приемлемых для вас пределах.

Если же репетиция показала, что даже в новом порядке отчёт не успевает до срока из-за нехватки процессора или памяти, то вы получили не догадку, а обоснование: нужен план побольше, и вы знаете, по какому именно ресурсу. Расписание плюс запас ровно там, где он нужен — решение, которое выдержит срок не только в этом месяце.