
Умный в гору не пойдёт, умный гору обойдёт. А если это гора пользовательских данных? Тогда тоже обойдёт — но не в смысле «вокруг», а буквально заглянет под каждый камень, чтобы ничего не упустить. Разберём в статье, как быстро и эффективно обойти данные, если требуется, к примеру, перенести их в новое хранилище или сделать анализ содержимого.
Меня зовут Андрей Баталов, я старший программист в VK Музыке. Расскажу об эффективных приёмах, которые помогают нам в переобходах аудиообъектов и других данных всех пользователей ВКонтакте.
Итак, представим ситуацию: все новые аудио, которые попадают в систему VK Музыки от пользователей, проходят флоу обработки. В него добавляется этап, который предоставляет новые функции. Они будут доступны только для загружаемых аудио. Чтобы новые функции появились и у ранее загруженных файлов, нужно обойти их и применить к ним добавившийся этап обработки. Иными словами, требуется запустить некий модуль переобхода, который отправит все старые данные на обработку. Отмечу пару её особенностей: она занимает несколько секунд для одной аудюшки и происходит асинхронно.
Вот стартовая схема этого процесса, по ходу статьи будем её совершенствовать:

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

Выключатель должен срабатывать мгновенно и быть в лёгком доступе у ответственного за переобход. Это сэкономит кучу нервов и времени на исправления.
Приём 2. Очередь-посредник
Если для переобхода используются те же мощности, что и для работы основной функциональности, то может произойти замедление или полная остановка обработки текущего потока задач. Лиха беда начало! Есть ещё один приём, чтобы и с этим справиться.
Можно создать очередь‑посредника для задач из переобхода и настроить её так, чтобы она использовала ресурсы процессинга только в рамках выделенной квоты. Кстати, этот приём помогает сбалансировать нагрузку и в других задачах, связанных с использованием общего ресурса. Взглянем на актуализированную схему:

Приём 3. Rate Limiter
Когда при переобходе обнаруживаются ошибки, в нашем первом приёме предлагается использовать выключатель. Но бывает и противоположная ситуация. Переобход работает даже лучше, чем ожидалось: производится поток запросов, который система не в силах обработать. В таком случае хочется цитировать персонажа легендарного «Брата»: бери ношу по себе, чтоб не падать при ходьбе.
Такой механизм, как Rate Limiter, позволяет ограничить поток событий в единицу времени. Он широко применяется в разработке: например, можно ограничить как доступ к API, так и количество сообщений в личку. Чтобы обуздать поток задач из переобхода, он тоже отлично подойдёт. Вот схема с включением Rate Limiter:

Часто реализация Rate Limiter основывается на механизмах скользящего или фиксированного окна. Рассмотрим простой пример на основе фиксированного окна с использованием хранилища Redis. Обратите внимание, это не production‑ready код, а иллюстрация:

О разных способах реализации Rate Limiter можно почитать в статье «Рейт‑лимиты с помощью Python и Redis». Там описаны интересные подходы.
Приём 4. Исключение дублей
Представьте ситуацию: существует система с множеством аудиообъектов, у каждого своё название, владелец, указатель на аудиофайл. Но, приглядевшись, мы видим, что часть из них созданы на основе одного файла: у них разные названия, разные владельцы, но аудиодорожка одна и та же. В таком случае при переобходе не нужно обрабатывать абсолютно все аудиообъекты в системе. Если важна только аудиодорожка, то другую информацию можно игнорировать. Таким образом, в данной ситуации в качестве ключа переобхода разумно использовать не идентификатор аудиообъекта пользователя, а идентификатор аудиофайла в системе:

Процесс станет значительно быстрее, если не нужно будет делать одну работу несколько раз.
Приём 5. Блокировка
Обработка происходит не мгновенно. Пока она не закончилась, может нападать куча запросов с одинаковым контентом. Чтобы этого избежать, нужно отклонять новые повторяющиеся запросы, если задача уже в процессе выполнения. Такой приём можно назвать блокировкой, а механизм, который его предоставляет, — Locker.
Так же, как и Rate Limiter, Locker активно используется в серверных приложениях. Действует просто: перед тем как взять задачу в работу, совершается попытка блокировки. Если она успешная, можно продолжить обработку, а если завершилась неудачей — кто‑то уже взял задачу в работу и текущую обработку нужно прекратить. На схеме это выглядит так:

Иллюстративный пример блокировки с помощью Redis:

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

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

Это как Rate Limiter наоборот: фильтр будет пройден только после определённого количества попыток. Например, можно настроить порог так, чтобы в обработку пропускались лишь треки, которые послушали больше 1 000 раз в день. По мере обработки горячих данных этот порог нужно снижать, чтобы переходить к менее популярным.
Заключение
В статье удалось собрать семь универсальных приёмов для переобхода большого набора данных, которые мы в VK Музыке успешно применяем на практике. Они помогают изолировать другие работающие элементы системы от влияния переобхода, а также скорее получить эффект от самого процесса переобхода.
Вероятно, многие читатели Хабра сталкивались с задачами переобхода и повторной обработки данных. Поделитесь в комментариях: как вы организовывали этот процесс и какими приёмами пользовались?
Автор: andrey270