MetaQuotes test task - .Net Developer position
It is a Russian company thats why the task in Russian.
Задача:
Разработать веб-приложение для получения координат пользователя по его IP-адресу и получения списка местоположений по названию города.
Средства разработки:
- C#, ASP.NET MVC 4 или 5 версии
- MS Visual Studio
- HTML5, CSS3, JavaScript
- Без использования СУБД
Требование к архитектуре и исходному коду:
- Веб-приложение должно быть спроектировано и разработано с расчетом на 10 000 000 уникальных пользователей в день и от 100 000 000 запросов в день.
- Клиентская часть должна быть выполнена в виде Single Page Application
- Исходный код должен быть оформлен в едином стиле и содержать необходимые комментарии.
- Аккуратность исходного кода будет оцениваться наряду с функциональностью приложения.
- Для клиентского кода нет требований по минимально поддерживаемой версии браузера. Можно использовать возможности последних версий браузеров (Chrome, Firefox, IE11, Edge).
Техническое описание приложения
-
База данных хранится в файле geobase.dat, которые содержится в прикрепленном к письму архиве.
-
База данных не будет изменятся она предназначена только для чтения.
-
База данных имеет бинарный формат. В файле последовательно хранятся:
-
60 байт - заголовок int version; // версия база данных sbyte name[32]; // название/префикс для базы данных ulong timestamp; // время создания базы данных int records; // общее количество записей uint offset_ranges; // смещение относительно начала файла до начала списка записей с геоинформацией uint offset_cities; // смещение относительно начала файла до начала индекса с сортировкой по названию городов uint offset_locations; // смещение относительно начала файла до начала списка записей о местоположении 12 байт - Header.records (количество записей) - cписок записей с информацией об интервалах IP адресов, отсортированный по полям ip_from и ip_to
uint ip_from; // начало диапазона IP адресов uint ip_to; // конец диапазона IP адресов uint location_index; // индекс записи о местоположении
-
96 байт - Header.records (количество записей) - cписок записей с информацией о местоположении с координатами (долгота и широта)
sbyte country[8]; // название страны (случайная строка с префиксом "cou_") sbyte region[12]; // название области (случайная строка с префиксом "reg_") sbyte postal[12]; // почтовый индекс (случайная строка с префиксом "pos_") sbyte city[24]; // название города (случайная строка с префиксом "cit_") sbyte organization[32]; // название организации (случайная строка с префиксом "org_") float latitude; // широта float longitude; // долгота
-
4 байта * Header.records (количество записей) - список индексов записей местоположения отсортированный по названию города*
-
База данных грузится полностью в память при старте приложения.
-
Время загрузки базы в память должно быть не более 200 мс (для информации: есть решение, которое позволяет загрузить базу в память быстрее 30 мс).
-
Необходимо реализовать быстрый поиск по загруженной базе по IP адресу и по точному совпадению названия города с учетом регистра.
-
В приложении должны быть реализованы два метода HTTP API:
- GET /ip/location?ip=123.234.432.321
- GET /city/locations?city=cit_Gbqw4 ответ сервера на каждый из запросов должен быть представлен в формате JSON.
-
Клиентская часть приложения должны быть выполнена в идеологии Single Page Application.
-
Страница должна состоять из двух частей: в левой части меню переключения экранов, в правой части отображается выбранный экран.
-
Клиентская часть должна реализовать два экрана: поиск гео-информации по IP, поиск списка местоположений по названию города.
-
Экран поиска гео-информации содержит: поле для ввода IP адреса, кнопку "Искать" и область для вывода результата.
-
По нажатию кнопки "Искать" на сервер отправляется запрос GET /ip/location?ip=123.234.432.321 Обработанный ответ от сервера выводится в область вывода результатов.
-
Экран поиска списка метоположений содержит: поле для ввода названия города, кнопку "Искать" и область для вывода результата. По нажатию кнопки "Искать" на сервер отправляется запрос GET /city/locations?city=cit_Gbqw4 Обработанный ответ от сервера выводится в область вывода результатов.
Дополнительно
- Проявление инициативы сверх основного задания приветствуется.
Её же можно найти на странице About.
Реализована работа в трёх режимах:
- Dirrect - прямой доступ к памяти. Работа на уровне байтов и арифметикой позиционирования. Плюсы:
- Практически мгновенная готовность к работе. 5мс. на подготовку к работе
- Масштабируемость Минусы:
- Сложность поддержки
- Уступает при поиске в скорости Marshal режиму
Примечания: Описание размерности полей вынесено как CustomAttribute, что позволяет динамически менять размер полей, все сдвиги пересчитаются автоматически. Размерность можно получить и из простых типов (кроме stirng), но лучше везде использовать один подход для однообразия.
- Marshal - маршаллинг всей структуры при загрузке. **Плюсы: **
- Простая поддержка
- Лучшая скороть работы при поиске Минусы
- Проигрывает в скорости подготовки к работе - 45 мс
- Плохая масштабируемость
- Сombined - смешанный режим. Попытка взять лучшее от обоих методов и выжать выше скорость. Плюсы:
- Выигрывает в скорости на некоторых тестах у Dirrect
- Проще в поддержке, чем Dirrect способ
- Высокая скорость подготовки к работе (инициализация) Минусы:
- Проигрывает на мультипоточных тестах двум другим методам
Переключение режимов в настроечном файле "appsettings.json".
Скорость на мультипоточном тесте: Поиск по IP адресу: 1000 потоков x 6 запросов
-
Dirrect : 2 мс.
-
Marshal : 0.5 мс.
-
Combined: 3 мс.
Поиск по городу: 1000 потоков x 8 запросов
-
Dirrect : 122 мс.
-
Marshal : 46 мс.
-
Combined: 266 мс
При многопоточном режиме выигрывает в скорости тип Marshal, но тратит больше времени на загрузку Combined - выигрывает на некоторых тестах, но в целом проигрывает. Dirrect - загружается в 9 раз быстрее, чем Marshal (5 мс. против 45 мс), проще масштабируется, но поиск медленнее.
Реализованы тесты в отдельном проекте:
- Performance: анализ различных подходов для одной и той же задачи
- На правильность работы процедур поиска по IP и по городу
- Работа в многопоточном режиме
Замечания, допущения:
- В задание забыли добавить структуру индексов городов (но может это тоже часть задания на находчивость).
- Большую задержку вносит вывод в режиме Debug. При тестах нужно переключиться в режиме Release.
- Во многих городах на конце стоит пробел. Нужно их вводить также с пробелом (обрезка строк не производится). Согласно заданию - должны совпадать все символы. Например, город "cit_A " должен так и передаваться - с пробелом на конце.
- Не фронтенде было испольвание по минимуму: только JQuery для удобства работы с DOM и Bootstrap для красоты.
- Инициализация объекта в веб приложении в виде dependence injection от mvc core, singleton. Проверка производительности DI, сравнение со static, и другими библитеками DI - не проводилась.
- Немногочисленные комментарии на английском языке. Убеждёт, что хороший код должен быть самодокументируемым.
- WebApi находится в веб приложении. В реальном приложении имеет смысл подумать о выносе API в виде отдельного приложения/проекта
- Перевод ip в int есть две реализации с разным порядком байтов, проверял по следующим сервисам: https://www.browserling.com/tools/dec-to-ip http://www.smartconversion.com/unit_conversion/IP_Address_Converter.aspx http://www.silisoftware.com/tools/ipconverter.php Если нужно в другом порядке, то нужно раскомментировать строки в IpHelper.cs Мой ответ по теме: https://stackoverflow.com/questions/36831/how-do-you-parse-an-ip-address-string-to-a-uint-value-in-c