Примечание. В этой статье предполагается, что вы знакомы с NUSMods, Telegram и TypeScript/JavaScript.

Это вторая часть серии из трех частей. Часть 3 здесь

Бот готов к использованию! Посмотреть здесь

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

Давайте вспомним, как работает наш бот.

Довольно просто, но довольно медленно.

Проблемы

Сначала мы можем решить более мелкие проблемы.

Проблема 1: жестко заданная стоимость учебного года

Проблема: Чтобы изменить учебный год, мы должны изменить исходный код.

Решение: Сохраните учебный год в файле .env. Это довольно просто. Я не буду освещать это в статье. Возможно, в будущем NUSMods будет отображать учебный год в конечной точке. Посмотрим.

Проблема 2. Данные в NUSMods обновляются только один раз в день, но мы делаем запрос к NUSMods каждый раз, когда кто-то выполняет поиск.

Проблема: потенциальный спам NUSMods API.

Решение. Кэшируйте результат вызова API и обновляйте его, если он старше 1 дня (настраивается).

Проблема 3. Результаты поиска и фильтрации будут одинаковыми для одних и тех же входных данных (при одинаковом кеше)

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

Решение: используйте встроенную функцию Telegram API cache_time.

Проблема 4. Поиск и фильтрация занимают очень много времени

Проблема: более длительное время загрузки может привести к неудовлетворенности пользователей.

Решение: мы рассмотрим это в следующем разделе, так как слишком долго подводить итоги здесь.

Сравнительный анализ

Давайте сначала получим некоторые цифры, чтобы увидеть, как наши изменения могут сократить время выполнения. Мы просто будем использовать встроенные методы console.time() и console.timeEnd() в NodeJS, чтобы упростить задачу. Разница во времени (в миллисекундах) будет выведена, когда мы вызовем console.timeEnd() после вызова console.time().

Поиск «1000» дает нам среднее время 488 мс за 5 запусков.

Проблема 2

Данные в NUSMods обновляются только один раз в день, но мы отправляем запрос в NUSMods каждый раз, когда кто-то выполняет поиск.

Это будет просто, мы просто создадим объект CACHE в глобальной области видимости, который содержит свойство lastUpdated и свойство, которое будет содержать результат вызова API.

Каждый поисковый запрос, сделанный боту, проверяет, истек ли срок действия кеша (старше 1 дня или не существует). Если он проходит эту проверку, данные обновляются.

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

После тестирования мы снизили среднее время загрузки до 362 мс (на 26 % быстрее).

Проблема 3

Результаты поиска и фильтрации будут одинаковыми для одних и тех же входных данных (при одинаковом кеше)

Это действительно просто, Telegram API позволяет нам кэшировать результаты определенного запроса на сервере, которые затем будут отправлены всем пользователям, которые запрашивают одно и то же. Мы установим срок действия этого кеша на 30 минут.

await ctx.answerInlineQuery(trimmedResults, {
    cache_time: 30 * 60,
});

Проблема 4

Поиск и фильтрация занимают очень много времени

В большинстве случаев это решение не уменьшит время выполнения!

В настоящее время наш алгоритм фильтрации работает за время O(n²).

filter O(n) и includes также равно O(n).

Мы можем оптимизировать это в некотором роде.

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

Для этого мы можем хранить массив, содержащий объекты с 2-мя свойствами: searchString и results. Каждый раз, когда пользователь что-то ищет, мы проверяем, является ли свойство searchString каждого объекта в массиве подстрокой текущего поискового запроса. Если это так, то мы просто выполняем поиск и фильтрацию results этого объекта вместо повторной фильтрации всего списка модулей.

Чтобы ограничить использование памяти, мы можем ограничить количество сохраненных прошлых поисков до 25.

В случае, когда пользователь побуквенно вводит «GEA1000», мы видим, что среднее время сокращается до 336 мс (улучшение на 7 %) (запросы с 3 по 7). .

Полнотекстовый поиск не входит в мои обязанности (и не входит в мои знания). Надеюсь, я вернусь к этому в другой раз.

Окончательный поток

Хотя окончательный процесс выглядит намного сложнее, экономия времени суммируется, а внесенные нами изменения сделают бота более приятным в использовании, а также помогут предотвратить сбои в работе NUSMods из-за потенциального спама API.

Заключение

Решение задачи №1 позволит нам легко изменить учебный год. Проблемы с № 2 по № 4 приводят к чистому сокращению времени на 31,4% с 488 мс до 336 мс в среднем.

Большая часть задержки на самом деле происходит из-за ожидания отправки ответа на серверы Telegram, а затем его отправки на ваш телефон. Если бы нам нужно было сравнить фактическое время выполнения несетевого кода, мы бы увидели, что оно незначительно (‹ 10 мс) по сравнению с сетевым временем.

Это одна из основных причин того, почему Проблема № 2 привела к такому большому улучшению, потому что мы убрали затратный по времени сетевой вызов.

Несмотря на то, что функция фильтрации и поиска — O(n²), нужно искать только ~14 000 записей. При таком небольшом масштабе время, затрачиваемое на это, все еще относительно невелико. Тем не менее, попытки уменьшить временную сложность по-прежнему имеют смысл. Надеюсь вернуться к этому вопросу в будущем.

Наконец, кеширование и отказ от сетевых вызовов API NUSMods поможет снизить нагрузку на их серверы. Внимательное использование таких общедоступных API поможет обеспечить их общедоступность и бесплатность для всех :)

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

Ознакомьтесь с исходным кодом здесь.

Смотрите другие мои проекты здесь.