Skip to content

Latest commit

 

History

History
451 lines (324 loc) · 21.7 KB

TECHNICAL_TASK.md

File metadata and controls

451 lines (324 loc) · 21.7 KB

Оглавление

Последовательность действий на HighLoad Cup 2017

Чтобы приступить к выполенению задания, вам необходимо скачать архив с тестовыми данными в формате JSON (данные вы также можете скачать с сайта проведения конкурса https://highloadcup.ru).

Вам необходимо сначала создать, а затем и развернуть производительный сервер приложения, который будет реализовывать необходимое Web API к этим данным.

Мы рекомендуем сначала отладить своё решение локально на тестовых данных. Когда вы будете готовы, соберите из него docker-контейнер и залейте его в хранилище системы проведения конкурса. Специально для вас мы выложили пример данной процедуры. После этого на странице с задачей на сайте HighLoad Cup появится запись о принятом решении и о постановке его в очередь на предварительный обстрел.

Будет происходить следующее:

Шаг 1.

Решение будет отправлено на тестирующую машину с процессором Intel Xeon (4 ядра по 2 GHz) и RAM 4GB. Доступная память = 10 GB HDD.

Шаг 2.

Решение будет поднято как docker-контейнер (docker run). Если при запуске появятся ошибки, то они будут показаны на странице с логами обстрела.

Шаг 3.

После запуска контейнера в папке /tmp/data будет доступен файл data.zip с архивироваными "боевыми" данными (примерно 200 килобайт для предварительного и 20 мегабайт для полного обстрела), а также в папке лежит options.txt. В архиве будут лежать файлы с названиями вида "<имя сущности>_<номер файла>.json". Например:

        ├── users_1.json
        ├── users_2.json
        ├── locations_1.json
        ├── locations_2.json
        ├── visits_1.json
        ├── visits_2.json
        └── visits_3.json

Внутри таких файлов - данные в формате JSON. К примеру, данные о путешественниках будут иметь такую структуру:

    {
        "users": [
            {
                "id": 1,
                "email": "[email protected]",
                "first_name": "Данила",
                "last_name": "Стамленский",
                "gender": "m",
                "birth_date": 345081600
            }, {
                "id": 2,
                "email": "[email protected]",
                "first_name": "Аня",
                "last_name": "Шишкина",
                "gender": "f",
                "birth_date": -1571356800
            }
        ]
    }

Для посещений и достопримечательностей - аналогично.

Внутри файла options.txt будут две строчки.

        1502881955     # timestamp время генерации данных
        0              # Значение 0 - тестовый обстрел, значение 1 - рейтинговый обстрел.

Таким образом, можно определить, данные тестовые или рейтинговые.

Шаг 4.

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

Шаг 5.

По истечении времени Шага 4 начинается обстрел сервера запросами из списка API, который длится 70 секунд для предварительного и 7 минут для рейтингового. Важно - сервер должен слушать 80-й порт, чтобы обстрел прошел успешно! Запросы идут с заголовком Host: travels.com по протоколу HTTP/1.1, один запрос - одно соединение. Сетевые потери полностью отсутствуют.

Шаг 6.

Результаты и логи обстрела вы увидите на сайте, на странице с деталями решения в секциях "Обстрел" и "Результаты" соответственно.

Обратите внимание! Предварительный обстрел запускается автоматически и нужен для тестирования решения на малой нагрузке. По такому обстрелу показываются результаты в виде графиков, но не считается рейтинг. Для участия в рейтинге, необходимо вручную запустить рейтинговый обстрел, который проводится в гораздо более хардкорных условиях. Подробнее

Результаты рейтинговых обстрелов всех участников будут сводиться в таблицу на сайте. Подробнее


При замеченных попытках хакерских атак на сервера проведения конкурса Highload Cup 2017 участнику выдаётся бан, а результаты обстрела не засчитываются.

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

Лучшие из лучших получат призы! Подробнее

Описание задачи HighLoad Cup 2017.

Что нужно сделать?

Необходимо сначала создать, а затем и развернуть производительный сервер приложения, который будет реализовывать необходимое задачей Web API.

Структура данных

Как в тестовых, так и в "боевых" данных имеются записи о трех сущностях: User, Location и Visit. Эти сущности описывают путешествия людей по разным достопримечательностям и могут быть основой для небольшого сервиса «В помощь путешественнику». Они содержат данные о собственно профиле пользователя, достопримечательности и посещении конкретным пользователем конкретного места.

В User (Профиль) записаны следующие данные:

  • id - уникальный внешний идентификатор пользователя. Устанавливается тестирующей системой и используется для проверки ответов сервера. 32-разрядное целое беззнаковое число.
  • email - адрес электронной почты пользователя. Тип - unicode-строка длиной до 100 символов. Уникальное поле.
  • first_name и last_name - имя и фамилия соответственно. Тип - unicode-строки длиной до 50 символов.
  • gender - unicode-строка m означает мужской пол, а f - женский.
  • birth_date - дата рождения, записанная как число секунд от начала UNIX-эпохи по UTC (другими словами - это timestamp).

В Location (Достопримечательность) записаны следующие данные:

  • id - уникальный внешний id достопримечательности. Устанавливается тестирующей системой. 32-разрядное целое беззнаковоее число.
  • place - описание достопримечательности. Текстовое поле неограниченной длины.
  • country - название страны расположения. unicode-строка длиной до 50 символов.
  • city - название города расположения. unicode-строка длиной до 50 символов.
  • distance - расстояние от города по прямой в километрах. 32-разрядное целое беззнаковое число.

В Visit (Посещение) записаны следующие данные:

  • id - уникальный внешний id посещения. Устанавливается тестирующей системой. 32-разрядное целое беззнакое число.
  • location - id достопримечательности. 32-разрядное целое беззнаковое число.
  • user - id путешественника. 32-разрядное целое беззнаковое число.
  • visited_at - дата посещения, timestamp.
  • mark - оценка посещения от 0 до 5 включительно. Целое число.

Поля не содержат null

Все поля являются обязательными

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

Один и тот же путешественник может много раз посещать одни и те же достопримечательности с разными оценками.

Описание API

API - это методы, которые должен обслуживать разработанный участником сервер, по протоколу HTTP. Маршруты URL строятся в соответствии с парадигмой REST и должны соблюдаться! В угловых скобках представлены части URL, которые могут и будут меняться от запроса к запросу. GET-параметры могут свободно комбинироваться друг с другом либо отсутствовать.

Во всех ответах от сервера учитываются заголовки Content-Type, Content-Length, Connection.

Методы выборки данных (GET):

1. Получение данных о сущности: /<entity>/<id>

<id> - строка, которая может принимать любые значения.

В ответе ожидается код 404, если сущности с таким идентификатором нет в данных. Иначе, все собственные поля, включая идентификатор. <entity> принимает одно из значений - users, locations или visits.


Пусть пользователь с id = 1 существует. Пример ответа на запрос: GET: /users/1

HTTP Status Code: 200

    {
        "id": 1,
        "email": "[email protected]",
        "first_name": "John",
        "last_name": "Doe",
        "gender": "m",
        "birth_date": -1613433600
    }

Пример ответа на запрос: GET: /users/string

HTTP Status Code: 404


Пример ответа на запрос: GET: /users/string/somethingbad

HTTP Status Code: 404


Пример ответа на запрос: GET: /users/

HTTP Status Code: 404


Пример ответа на запрос: GET: /user/

HTTP Status Code: 404


И так далее...

2. Получение списка мест, которые посетил пользователь: /users/<id>/visits

В теле ответа ожидается структура {"visits": [ ... ]}, отсортированная по возрастанию дат, или ошибка 404/400. Подробнее - в примерах.
GET-параметры:

  • fromDate - посещения с visited_at > fromDate
  • toDate - посещения до visited_at < toDate
  • country - название страны, в которой находятся интересующие достопримечательности
  • toDistance - возвращать только те места, у которых расстояние от города меньше этого параметра

Пусть пользователь с id = 1 существует. Пример ответа на запрос: GET: /users/1/visits

HTTP Status Code: 200

    {
        "visits": [
            {
                "mark": 2,
                "visited_at": 958656902,
                "place": "Кольский полуостров"
            },
            {
                "mark": 4,
                "visited_at": 1223268286,
                "place": "Московский Кремль"
            }
         ]
    }

Пример ответа на запрос: GET: /users/1/visit

HTTP Status Code: 404


Пример ответа на запрос: GET: /users/1/visits?fromDate=

HTTP Status Code: 400


Пример ответа на запрос: GET: /users/1/visits?fromDate=abracadbra

HTTP Status Code: 400


Пример ответа на запрос: GET: /users/somethingstringhere/visits?fromDate=1

HTTP Status Code: 404


Пусть пользователь с id = 1 существует. Пример ответа на запрос: GET: /users/1/visit?fromDate=915148800&toDate=915148800

HTTP Status Code: 404

В этом примере ошибка в visit.


В случае если пользователя с переданным id нет - отдавать 404. Если просто нет посещений, то {"visits": []}

3. Получение средней оценки достопримечательности: /locations/<id>/avg

В ответе ожидается одно число, с точностью до 5 десятичных знаков (округляется по стандартным математическим правилам округления(round)), либо код 404.

GET-параметры:

  • fromDate - учитывать оценки только с visited_at > fromDate
  • toDate - учитывать оценки только до visited_at < toDate
  • fromAge - учитывать только путешественников, у которых возраст (считается от текущего timestamp) строго больше этого параметра
  • toAge - учитывать только путешественников, у которых возраст (считается от текущего timestamp) строго меньше этого параметра
  • gender - учитывать оценки только мужчин или женщин

Пример ответа на запрос: GET: /locations/1/avg

    {
        "avg": 3.43
    }

Пример ответа на запрос: GET: /locations/somethingsomething/avg

HTTP Status Code: 404

В случае если места с переданным id нет - отдавать 404. Если по указанным параметрам не было посещений, то {"avg": 0}

Небольшой пример проверки дат в этом запросе на python (fromAge - количество лет):

        from datetime import datetime
        from dateutil.relativedelta import relativedelta
        import calendar

        now = datetime.now() - relativedelta(years = fromAge)
        timestamp = calendar.timegm(now.timetuple())

Дальше проверяется birthdate < timestamp либо birthdate > timestamp соответственно.

Методы обновления данных (POST)

1. Обновление данных о сущности: /<entity>/<id>

В ответе ожидается код 200 с пустым json-ом в теле ответа {}, если обновление прошло успешно, 404 - если запись не существовала в данных или 400, если в теле запроса некорректные данные.

Только обновляемые поля и их значения содержатся в теле запроса в формате JSON. id никогда не содержится среди обновляемых полей. В таких запросах может быть GET-параметр query_id, который надо игнорировать.


Пусть пользователь с id = 214 существует. Пример тела запроса: POST: /users/214

    {
        "email": "[email protected]",
        "first_name": "Jessie",
        "last_name": "Pinkman",
        "birth_date": 616550400
    }    

Ответ: HTTP Status Code: 200

   {}

Пусть пользователь с id = 214 также существует. Изменено поле с почтой. Пример тела запроса: POST: /users/214

    {
        "email": null,
        "first_name": "Jessie",
        "last_name": "Pinkman",
        "birth_date": 616550400
    }    

Ответ:

HTTP Status Code: 400

Пусть пользователь с id = 214 не существует. Пример тела запроса: POST: /users/214

    {
        "email": "[email protected]",
        "first_name": "Jessie",
        "last_name": "Pinkman",
        "birth_date": 616550400
    }    

Ответ:

HTTP Status Code: 404

2. Добавление новой сущности: /<entity>/new

В ответе ожидается код 200 с пустым json-ом в теле ответа ("{}"), если создание прошло успешно. В случае некорректных данных - код 400.

Обновляемые поля и их значения содержатся в теле запроса в формате JSON. В таких запросах может быть GET-параметр query_id, который надо игнорировать.

При создании сущности все поля являются обязательными.

В случае попытки создания сущности с id, уже существующим в текущих данных, ожидается код ошибки 400.


Пример запроса POST: /users/new

    {
        "id": 245,
        "email": "[email protected]",
        "first_name": "Маша",
        "last_name": "Пушкина",
        "gender": "f",
        "birth_date": 365299200
    }

Ответ:

HTTP Status Code: 200

   {}

Пример запроса POST: /users/new

    {
        "id": 245,
        "email": null,
        "first_name": "Маша",
        "last_name": "Пушкина",
        "gender": "f",
        "birth_date": 365299200
    }

Ответ:

HTTP Status Code: 400