banner-service
Сервис управления пользовательским динамическим контентом
В Авито есть большое количество неоднородного контента, для которого необходимо иметь единую систему управления. В частности, необходимо показывать разный контент пользователям в зависимости от их принадлежности к какой-либо группе. Данный контент мы будем предоставлять с помощью баннеров.
Задача
Реализовать сервис с одним пользовательским эндпойнтом, с помощью
которого пользователям выдаются баннеры в зависимости от
указанной группы и фичи, и несколькими админскими - для управления ими.
Что сделано
- Имитация работы сервиса авторизации при помощи 2 видов токенов
- Реализованы все эндпойнты из основной части задания
- Кэширование в Redis для оптимизации пользовательских запросов
- Выполнен интеграционный тест на сценарий получения баннера пользователем
- Схема БД адаптирована для увеличения количества тегов и фичей, а также
для компактного хранения версий
- Два эндпойнта GET и PATCH для управления версиями баннеров
- Метод удаления баннеров по фиче или тегу с использованием scheduled task
- Документация swagger (находится в ./docs в json и yaml форматах)
Не выполнено
- Нагрузочное тестирование - нет опыта с loadtesting, не уложился в сроки.
Вернусь к этому вне рамок тестового, сделаю при помощи jmeter/grafana.
- Не реализовано интеграционное или E2E-тестирование для остальных сценариев
- Отсутствует конфигурация линтера
Описание решения
- Go 1.22.1
- База данных postgres: pgx toolkit
- Redis
- Окружение с docker-compose
- Маршрутизация с помощью gorilla/mux
- Валидация запросов go-playground/validator
- Логирование при помощи zap
- Тесты: testify/suite
- Scheduled task: gocron
Возникшие вопросы и их решения
?
Как реализовать авторизацию?
!
Поскольку задание - это сервис контента, решил использовать метод,
который будет только имитировать работу авторизации, поскольку обычно для этого
реализуются отдельные сервисы. Для получения доступа к эндпойнтам требуется
заголовок X-Access-Token
. Существуют 4 префикса: aup
- authorized user prefix и
aap
- authorized admin prefix для авторизированных юзера и админа соответсвенно.
И два других uup
и uap
для неавторизованных. Все остальные будут получать 403 ответ
на каждом эндпойнте.
?
Как использовать условие с допуском выдачи баннера, актуального в течении 5 минут?
!
Использовать кеширование ключей с TTL 5 минут. Первая идея была использовать
memcached, поскольку было дано небольшое количество тегов и фичей. После дополнительного
условия в виде значительного увеличения количества сущностей БД решил, что нужно отдельное
хранилище и выбрал redis.
?
Как более эффективно выполнять удаление из БД?
!
"Помечать" удаленными вместо самого удаления и использовать запланированную
задачу для очистки удаленных записей в дальнейшем. Для этого удобно использовать Cron.
Поскольку такое единовременное удаление огромного количества
записей будет сильно нагружать сервис, таска была установлена на выполнение во время
ожидаемого простоя API.
?
Как организовать управление версиями баннеров, чтобы можно было хранить
три предыдущие и при необходимости вернуться к прошлым наполнением контента, связям с фичами, тегами?
!
Версионирование можно было бы реализовать следующим образом: перенести все
содержимое баннера в отдельную сущность, а в самом баннере хранить ссылку на версию.
Сам же контент доставать по этой ссылке. Но тогда возникают проблемы. Баннер уже не будет
центральной сущностью, какой он должен быть. А также возникает страшное количество дублирования
данных - каждая версия связана с каждым тегом, которые могут быть ровно такими же, какими были
для основного баннера. Я выбрал следующий подход: хранить версии как некий контекст для основного
баннера: фича, контент, активен ли баннер, а также список тегов в виде строки, где они перечислены
через разделитель. Когда администратор хочет вернуться к определенной версии, связи основного баннера
перезатираются из версии, а все версии, что были после той, к которой он перешел - удаляются,
поскольку хранят некорректную логику.
Инструкция по запуску
При запуске самого приложения добавляются 2000 тегов и фичей. Баннеры отсутствуют.
git clone git@github.com:mBayzigitov/dynamic-content-service.git
- Запустить приложение (не занимая окно терминала)
make run
или в явном виде
make run-ex
чтобы пересобрать:
make run-rebuild
остановить контейнеры:
make stop
удалить контейнеры:
make down
удалить вместе с томами:
make down-v
- Запустить интеграционные тесты (контейнеры останавливаются автоматически по окончании)
make test
удалить тестовые сервисы
make down-tests