В этой статье я расскажу о первых результатах работы приложения для хранения прочитанных книг в первый месяц жизни. Всем привет. Чуть более месяца назад я выпустил релиз своего приложения BookDesk: Читательский дневник для хранения всех своих прочитанных книг. Почитать про историю создания можно в первой части. После публикации статьи об истории создания приложения я столкнулся с некоторыми проблемами. Получил «проверку» моего API на стрессоустойчивость и высоконагруженность. Собственно, по‑умолчанию все виды тестирования API должны быть проведены до релиза, но я конечно‑же этого не сделал и получил проблемы: нестабильную и медленную работу приложения и плюс ко всему сервер падал, а в частности падала база данных из‑за огромной нагрузки. Я держал в голове тот момент, что после запуска такое вполне себе может быть, но подумал, что это задача точно не первой необходимости т.к. большого количества пользователей я не ожидаю да и кому я нужен, хотя очень просчитался. В режиме продакшена пришлось лезть и изучать как сделать API защищенным. В качестве веб‑сервера API у меня express.js и я выполнил официальные рекомендации от разработчика и стал спать спокойнее. А если конкретнее, то первым делом поставил rate‑limiter, который устанавливает лимит на определенное количество запросов за определенный отрезок времени. Также, поставил ряд пакетов для защиты. Конечно, от чего‑то серьезного это не спасет, но для школьников‑ддосеров чуть прикроет двери. Дальше буду решать проблемы по мере их поступления. По логам запросов я видел странные значения в query и body параметрах запросов к API, и тут меня осенило, что идут активные попытки инъеккций в базу данных. На стороне API у меня была простейшая самописная валидация в стиле проверки на пустоту, однако, этого конечно‑же недостаточно на сегодняшний день. Ведь есть такое понятие как SQL/NoSQL инъекции, когда через API в вашу базу могут записать нежелательные данные в виде запросов через которые злоумышленники могут получить доступ к защищенным данным или просто положить Вам сверер и базу данных. И от этого надо иметь защиту. я поставил пакет express‑validator и создал валидаторы для каждой конечной точки и для каждого параметра. Например, у меня есть параметр bookStatus, который может иметь только 4 String  параметра и я делаю на это проверку, если значение будет отличное от одного из этих параметров, API вернет 500 ошибку. Я рад, что за столь небольшое количество времени, получил такие уроки. У меня уже был готовый настроенный VPS, который я арендую в одной из хостинговых компаний. На нем я размещаю свои контентные сайты на WordPress. На сервере 3 ядра и 8 Гб оперативной памяти, система Debian 9. Ну и туда же я поставил MongoDB + node.js для развертывания API, т.к. решил, что не имеет смыла покупать новый VPS чтобы отдельно на нем держать приложение и платить дополнительные деньги, ведь приложение будет полностью бесплатным (но тут я глубоко ошибался). Я мониторил нагрузку и видел (использовал htop), что приложение работает медленно. Загрузка книг, авторизация и все другие операции работали не так как хотелось бы. Но самым долгим процессом была работа загрузки каталога с книгами. Тому виной было использование и так нагруженного сервера, ну и конечно же ошибки в аггрегации данных и в логике работы (о чем я расскажу ниже). Напомню, что это мое первое полноценное приложение с бэк‑эндом, базой данных и API с большим количеством данных. В первой версии приложения база содержала около 60 тыс. записей книг, с этим количеством все запросы справлялись неплохо. Поиск и фильтрация, да и просто подгрузка контента при скроллинге. В среднем, время выполнения запроса составляло около 1–2 секунд, что конечно‑же тоже далеко от идеала. Получив первые отзывы, я понял, что база слишком мала для такого рода проекта. Ведь 60 тыс. книг это капля в море. На сегодняшний момент, суммарное количество выпущенных книг на русском языке составляет миллионы экземпляров. Я решил заняться расширением базы. После расширения, база составила более 250 тыс. книг. Таким образом база приросла на, без малого, 200 тыс. книг. Я начал тестировать скорость работы и получив первые результаты, был немного шокирован. Скорость выполнения составляла ~7–10 секунд, это огромная цифра. Для вытаскивания данных из базы я использую аггрегацию. Это значит, что у меня есть коллекция под названием книги где, собственно, хранятся сами книги и есть коллекция пользовательские_книги где хранится информация о книгах которые пользователи добавляют себе, и эта коллекция содержит id книги, статус и другие служебные поля. И чтобы вывести в общем списке книги мне необходимо соединить эти 2 коллекции в одну по разным условиям и показать пользователю, а для этого, база должна пройти по этим двум коллекциям и вернуть данные. Погуглив я нашел полезную статью от самой MongoDb Aggregation Pipeline Optimization довольно полезный материал, я применил ряд советов. C MongoDB я работаю первый раз, да и вообще с базами данных на таком уровне. У MongoDB есть такая возможность как создание индексов, которая помогает быстрее возвращать результаты при сортировках, фильтрации и т. д. Посидев и подумав, меня осенила интересная мысль, а зачем делать манипуляции со всеми книгами в базе, чтобы потом отдать пользователю только первые 50 результатов. По умолчанию, сейчас подгрузка идет по 50 результатов. И тогда я решил, что можно ограничить все это дело только первыми 10 тыс. результатов, ведь мало пользователей будет скроллить так глубого каталог с рекомендациями (это надо совершить 200 подгрузок по 50 резульатов), ну а дальше буду решать по ходу дела если возникнут вопросы. Так и сделал, по итогу я получил скорость выполнения запроса в ~1 сек, что в ~7–10 раз быстрее чем было. Я понимал, что помимо нагрузки от приложения, существует нагрузка от других сайтов, которые хранятся на сервере а они используют MySQL, что дает суммарно немалую нагрузку. Обычно процессор и оперативная память нагружены на 70–95%. И поэтому, я решил арендовать отдельный VPS под приложение. Выбрал сервер с 3 ядрами + 8 Гб памяти + NVME диск на Debian 11. Да и вообще, если начал что‑то делать надо делать это хорошо. И после переезда на новый сервер скорость выполнения запросов выросла до 500 мс в среднем с высокой стабильностью. Я давно пользуюсь разными хостингами, но отдельно хотелось бы упомянуть данного хостера, т.к. у ребят все организовано очень четко, впервые за 10 лет аренды серверов сталкиваюсь с таким отношением к клиенту и продуманным кабинетом. Хостер timeweb, это не реклама, просто люблю давать заслуженные рекомендации. Сейчас, судя по логам — нагрузка на сервер минимальная. За месяц работы приложения я успел выпустить достаточное количество улучшений, исправлений и новых функций. Отдельно хочу рассказать о функции добавления своих книг. Эта функция была одной из первых для добавления, т.к. я понимал, что невозможно держать базу в актуальном состоянии да еще и хранить все книги. И единственным решением являлась реализация функции добавления своих книг. Сейчас любой желающий может добавлять свои любые книги которых нет в базе. Удобная форма добавления имеет пошаговую логику.Также есть возможность автоматического подбора обложек. На данный момент приложение имеет 95 активных установок. Ежедневно добавляется 1–3 аккаунта (это я вижу по базе данных), есть активные пользователи, которые пользуются приложением часто. Я не планирую добавлять рекламу и платные функции, хочу сделать приложени полностью бесплатным и последить за результатами.Работаем дальше и следим за результатами. Мой горизонт планирования 1–2 года чтобы получить какие‑то первые понятные результаты. Я не планирую пока закупать рекламу, ориентируюсь только на органический траффик. Под вашй проект/приложение сразу лучше оранизовать отдельный сервер/VPS чтобы избежать проблем с увеличением проекта и/или увеличением нагрузки. И неважно, платное оно или бесплатное. Я считаю, что если Вы взялись что‑то делать, а еще если это 'что‑то' нацелено на общий доступ и подразумевает использование его реальными людьми, то Ваша главная обязанность обеспечить бесперебойную и быструю работу приложения. Если запускаете свой проект/приложение — в первую очередь позаботьтесь о защите Вашего API от злоумышленников. Ставьте rate‑limiter чтобы ограничить количество запросов в единицу времени с одного IP. Делайте жесткую валидацию входяших параметров, это поможет избежать нежелательных инъекций в базу данных. На UI также нужна хорошая валидация для всех форм, чтобы избежать XSS атак (если речь идет о веб‑приложениях). Всем спасибо за внимание и буду очень рад если кому‑нибудь мое приложение BookDesk будет полезно.