Ситуация
Клиент пришёл за техническим аудитом
Часть пользовательских операций выполнялась слишком долго или не завершалась корректно. Особенно это касалось удаления и копирования данных.
Если процесс обрывался, система теряла целостность: что-то уже удалено, а что-то ещё нет. Для проекта с 10 000+ пользователей это означало нестабильность в критичных сценариях.
Проблема
Асинхронность только спрятала сбой
Команда пыталась вынести операции в асинхронный режим. Внешне это выглядело как улучшение: пользователь меньше ждал и реже видел ошибку. Но причина осталась.
- пользователь не понимал, что операция выполнилась не до конца;
- система вела себя непредсказуемо;
- ошибки становились тише, но не исчезали.
Что показал аудит
Узкое место было в самой структуре операции
- 20 000+ SQL-запросов на одну операцию;
- последовательные каскадные удаления вместо пакетной работы;
- критичные связи без нужных индексов;
- линейная деградация при росте объёма данных;
- дополнительные задержки из-за вызовов внешних API.
Стало ясно: проблема не в том, что процесс синхронный или асинхронный, а в том, как построен сценарий и как устроены связи данных.
Предложение
Я предложил разделить сценарии и убрать лишнюю работу
- разделить простые и глубокие сценарии удаления;
- использовать пакетные операции там, где это безопасно;
- сократить число запросов до нескольких вместо тысяч;
- добавить индексацию на критических связях;
- оставить каскад только там, где он реально нужен;
- использовать асинхронность как инструмент, а не как маскировку дефекта.
Итог
Система стала предсказуемой
После внедрения командой операции перестали зависеть от объёма данных и больше не создавали скрытых ошибок.
- время выполнения сократилось с 10+ минут до примерно 3 секунд;
- количество запросов упало с 20 000+ до нескольких;
- поведение под нагрузкой стало стабильным;
- пользователь перестал сталкиваться с «тихими» сбоями.
Ключевая мысль
Если одна операция кладёт систему, проблема в архитектуре
Асинхронность полезна, когда сценарий уже спроектирован правильно. Если она нужна только затем, чтобы скрыть перегруженную операцию, это сигнал пересмотреть структуру данных и сам процесс.