Чтобы приступить к выполенению задания, вам необходимо скачать архив с тестовыми данными в формате JSON (данные вы также можете скачать с сайта проведения конкурса https://highloadcup.ru).
Вам необходимо сначала создать, а затем и развернуть производительный сервер приложения, который будет реализовывать необходимое Web API к этим данным.
Мы рекомендуем сначала отладить своё решение локально на тестовых данных. Когда вы будете готовы, соберите из него docker-контейнер и залейте его в хранилище системы проведения конкурса. Специально для вас мы выложили пример данной процедуры. После этого на странице с задачей на сайте HighLoad Cup появится запись о принятом решении и о постановке его в очередь на предварительный обстрел.
Будет происходить следующее:
Решение будет отправлено на тестирующую машину с процессором
Intel Xeon (4 ядра по 2 GHz)
иRAM 4GB
. Доступная память =10 GB HDD
.
Решение будет поднято как docker-контейнер (
docker run
). Если при запуске появятся ошибки, то они будут показаны на странице с логами обстрела.
После запуска контейнера в папке
/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": "robosen@icloud.com",
"first_name": "Данила",
"last_name": "Стамленский",
"gender": "m",
"birth_date": 345081600
}, {
"id": 2,
"email": "tameerne@yandex.ru",
"first_name": "Аня",
"last_name": "Шишкина",
"gender": "f",
"birth_date": -1571356800
}
]
}
Для посещений и достопримечательностей - аналогично.
Внутри файла
options.txt
будут две строчки.
1502881955 # timestamp время генерации данных
0 # Значение 0 - тестовый обстрел, значение 1 - рейтинговый обстрел.
Таким образом, можно определить, данные тестовые или рейтинговые.
У сервера есть фиксированное время до начала обстрела, чтобы залить эти данные в собственную базу данных и подготовить их к обработке (
30 секунд
для предварительного и3 минуты
для полного обстрела). Подробнее
По истечении времени Шага 4 начинается обстрел сервера запросами из списка API, который длится
70 секунд
для предварительного и7 минут
для рейтингового. Важно - сервер должен слушать80-й порт
, чтобы обстрел прошел успешно! Запросы идут с заголовкомHost: travels.com
по протоколуHTTP/1.1
, один запрос - одно соединение. Сетевые потери полностью отсутствуют.
Результаты и логи обстрела вы увидите на сайте, на странице с деталями решения в секциях “Обстрел” и “Результаты” соответственно.
Обратите внимание! Предварительный обстрел запускается автоматически и нужен для тестирования решения на малой нагрузке. По такому обстрелу показываются результаты в виде графиков, но не считается рейтинг. Для участия в рейтинге, необходимо вручную запустить рейтинговый обстрел, который проводится в гораздо более хардкорных условиях.
Подробнее
Результаты рейтинговых обстрелов всех участников будут сводиться в таблицу на сайте. Подробнее
При замеченных попытках хакерских атак на сервера проведения конкурса 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 - это методы, которые должен обслуживать разработанный участником сервер, по протоколу HTTP. Маршруты URL строятся в соответствии с парадигмой REST и должны соблюдаться! В угловых скобках представлены части URL, которые могут и будут меняться от запроса к запросу. GET-параметры могут свободно комбинироваться друг с другом либо отсутствовать.
Во всех ответах от сервера учитываются заголовки Content-Type, Content-Length, Connection.
/<entity>/<id>
<id>
- строка, которая может принимать любые значения.
В ответе ожидается код 404, если сущности с таким идентификатором нет в данных. Иначе, все собственные поля, включая идентификатор.
<entity>
принимает одно из значений -users
,locations
илиvisits
.
Пусть пользователь с
id
= 1 существует. Пример ответа на запрос:GET: /users/1
HTTP Status Code: 200
{
"id": 1,
"email": "johndoe@gmail.com",
"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
И так далее…
/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
= 1 существует. Пример ответа на запрос:GET: /users/1/visits?country=?
HTTP Status Code: 400
В случае если пользователя с переданным
id
нет - отдавать 404. Если просто нет посещений, то{"visits": []}
/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
соответственно.
/<entity>/<id>
В ответе ожидается код 200 с пустым json-ом в теле ответа
{}
, если обновление прошло успешно, 404 - если запись не существовала в данных или 400, если в теле запроса некорректные данные.
Только обновляемые поля и их значения содержатся в теле запроса в формате JSON. id никогда не содержится среди обновляемых полей.
Пусть пользователь с
id
= 214 существует. Пример тела запроса:POST: /users/214
{
"email": "johndoe@gmail.com",
"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": "test@gmail.com",
"first_name": "Jessie",
"last_name": "Pinkman",
"birth_date": 616550400
}
Ответ:
HTTP Status Code: 404
/<entity>/new
В ответе ожидается код 200 с пустым json-ом в теле ответа ("{}"), если создание прошло успешно. В случае некорректных данных - код 400.
Обновляемые поля и их значения содержатся в теле запроса в формате JSON. В таких запросах может быть GET-параметр queryId, который надо игнорировать.
При создании сущности все поля являются обязательными.
В случае попытки создания сущности с id, уже существующим в текущих данных, ожидается код ошибки 400.
Пример запроса
POST: /users/new
{
"id": 245,
"email": "foobar@mail.ru",
"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