✨Dvurechensky✨
AdPlatformService — высокопроизводительный in-memory веб-сервис для хранения и поиска рекламных площадок по локациям.
📄 Документация по тестовому заданию доступна также в PDF: TASK.NET.pdf
📄 Тестовый файл с входными данными: test_input_data.txt
- Старт
dotnet run --project AdRegionService-
Debug
👉 API будет доступен по адресу: http://localhost:5411
👉 Swagger UI: http://localhost:5411/swagger -
Release
👉 API будет доступен по адресу: http://localhost:5411
👉 Swagger UI: http://localhost:5411/swagger -
API
Раскрыть подробности API
POST /api/load
Content-Type: multipart/form-data
file=@platforms.txt{
"message": "Data loaded",
"loaded": 123,
"skipped": 5
}GET /api/search?location=/ru/svrd[
{ "name": "Крутая реклама", "locations": ["/ru/svrd"] },
{
"name": "Ревдинский рабочий",
"locations": ["/ru/svrd/revda", "/ru/svrd/pervik"]
}
]- 📂 Загрузка списка рекламных площадок из файла (
Stream). - ⚡ Хранение в Immutable коллекциях для потокобезопасности.
- 📊 Подсчёт статистики загрузки (
загружено / пропущено). - 🔍 Поиск площадок по иерархическим локациям:
/loc1найдёт все площадки по этому уровню./loc1/loc2учитывает и/loc1.
- 🛡 Обработка ошибок:
OperationCanceledException(отмена загрузки).OutOfMemoryException(слишком большие файлы).- Общие ошибки логируются, состояние не портится.
- Метод
LoadFromStreamAsync(Stream stream, CancellationToken cancellationToken = default)загружает данные о рекламных площадках из текстового потока.
При успешной загрузке обновляется текущее состояние сервиса (список платформ и индекс по локациям).
Если загрузка не удалась, старые данные сохраняются.
- ✅ Принимает поток (
Stream) — можно загружать как локальные файлы, так и данные из сети. - ✅ Игнорирует некорректные строки:
- пустые строки;
- строки без разделителя
:; - пустое имя площадки;
- отсутствие валидных локаций.
- ✅ Индексирует локации
Каждая площадка привязывается к своим локациям, которые складываются вImmutableDictionary<string, ImmutableHashSet<AdPlatform>>. - ✅ Статистика загрузки
Сохраняется количество загруженных и пропущенных строк (LoadStats). - ✅ Логирование прогресса
Каждые100 000строк выводится лог «Обработано N строк…». - ✅ Защита от ошибок
OperationCanceledException→ корректная отмена загрузки;OutOfMemoryException→ логируется, состояние не меняется;- другие ошибки → логируются, состояние не меняется.
Яндекс.Директ:/ru
Ревдинский рабочий:/ru/svrd/revda,/ru/svrd/pervik
Газета уральских москвичей:/ru/msk,/ru/permobl,/ru/chelobl
Крутая реклама:/ru/svrd- ✅ Метод
Search(string location)возвращает все площадки, совпадающие с заданной локацией. - ✅ Используется индекс по локациям для мгновенного поиска.
- ✅ Поддержка частичных совпадений в имени и локациях.
- ✅ Возвращает
IEnumerable<AdPlatform>без лишнего копирования данных. - ✅ Потокобезопасный и защищён от битых объектов (
null Nameилиnull Locations). - ✅ Комфортно работает с
_platformsдо 1–2 млн элементов; для >10 млн элементов. - ⚡ При росте >10 млн площадок или >1–2 GB данных потребуется переход на внешние решения (например, PostgreSQL + полнотекстовый поиск или специализированные индексы).
-
Immutable коллекции (
ImmutableArray,ImmutableDictionary,ImmutableHashSet)- После загрузки данные фиксируются в неизменяемых структурах.
- Это обеспечивает потокобезопасность: поиск можно выполнять из разных потоков без блокировок.
-
Индекс по локациям с иерархией
- Локации индексируются в
ImmutableDictionary<string, ImmutableHashSet<AdPlatform>>. - Поиск учитывает все уровни иерархии: запрос
/a/b/cпроверяет/a,/a/b,/a/b/c.
- Локации индексируются в
-
Логирование через ILogger
- Каждые 100 000 строк загрузки выводится прогресс.
- Логируются ошибки (
OutOfMemoryException,OperationCanceledException, общие исключения). - Логи можно подключить к системе мониторинга (например, Seq, Kibana, Zabbix).
-
Устойчивость к ошибкам
- Некорректные строки файла игнорируются.
- Если загрузка не удалась, предыдущее состояние (
_platformsи индекс) сохраняется. - Поиск никогда не кидает исключения наружу, при ошибке возвращается пустой результат.
-
Поиск через объединение множеств
- Для найденных уровней локации собирается
HashSet<AdPlatform>. - Дубли платформ автоматически исключаются.
- Возврат результата происходит сразу как
IEnumerable<AdPlatform>.
- Для найденных уровней локации собирается
-
⚡ Высокая производительность загрузки и поиска
Оптимизировано для работы с файлами до 1–2 млн строк (100–500 MB).
Индексация по локациям обеспечивает быстрый поиск без полного перебора. -
🛡 Устойчивость и надёжность
Некорректные строки автоматически игнорируются.
При ошибке загрузки текущее состояние не теряется.
Поиск никогда не выбрасывает исключения наружу. -
🔗 Простая интеграция с REST API
Логика сервиса изолирована, endpoints легко строятся на её основе (/api/load,/api/search). -
🧵 Готовность к многопоточности
ИспользованиеImmutableArrayиImmutableDictionaryгарантирует потокобезопасность без явных блокировок.
Несколько запросов поиска могут выполняться одновременно.
- Используются модульные тесты (xUnit) для проверки корректности поиска и индексации.
- Покрытие включает: загрузку данных, поиск по названию и локациям, работу с пустыми и некорректными входными данными.
- Команда запуска:
dotnet test
- Автоматическая сборка Docker-образа при пуше в
main. - Публикация образа в GitHub Container Registry (
ghcr.io). - Сборка и пуш всех сервисов через Docker Compose.
- Минимальные проверки через
dotnet buildиdotnet test.
Генерация сертификата
openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout key.pem -out cert.pem -subj "/CN=localhost"Запуск контейнера
docker-compose up --build- Адрес сервера после запуска в контейнере:
http://localhost:5411/swagger/index.htmlhttps://localhost:5412/swagger/index.html
Загрузка
docker pull ghcr.io/dvurechensky/net_junior_ads_test_task/adservice:latestЗапуск напрямую, пробрасывая порт
docker run -it --rm -p 5411:5411 ghcr.io/dvurechensky/net_junior_ads_test_task/adservice:latestПосле запуска можно проверить:
http://localhost:5411/swagger
✨Dvurechensky✨
