Проведение конкурсов для IT-специалистов сейчас в моде: Kaggle с его задачами по Data Science, сплоченная тусовка олимпиадного программирования, набирающие популярность площадки для конкурсов по искусственному интеллекту, всевозможные хакатоны для мобильных разработчиков, олимпиады для админов, capture the flag для безопасников. Казалось бы, специалисту любой сферы несложно найти себе подходящую движуху, поучаствовать, прокачаться и что-нибудь выиграть.


Обделенными в этом плане остались лишь web-разработчики. Мы в Mail.Ru Group решили исправить это досадное недоразумение и теперь с радостью представляем вам HighLoadCup — конкурсную площадку на стыке backend-разработки и администрирования web-сервисов.


Если считаете себя хорошим web-разработчиком, умеете в deploy и highload — добро пожаловать!


Сроки, призы


Сразу о главном — первый, пилотный чемпионат стартовал вчера, 10-го августа, и продлится вплоть до конца лета — 31-го августа мы подведем итоги и вручим призы. Призовой фонд включает Apple iPad Air 2 Cellular 16 GB за первое место, WD MyCloud 6 TB за второе и третье места, WD MyPassport Ultra 2 TB за места с 4 по 6 включительно. По традиции, ТОП-20 участников получат футболки с символикой чемпионата.


Механика чемпионата


Участникам дается задание на написание небольшого web-сервиса, работающего с данными определенной структуры и реализующего API к этим данным. Контейнер с реализованным сервисом загружается к нам на сервера, там мы его стартуем и начинаем обстреливать HTTP-запросами. По результатам таких обстрелов мы подсчитываем количество правильных и неправильных ответов, RPS и скорость ответа, и по заранее определенной метрике формируется рейтинговая таблица. Автор наиболее быстрого и отказоустойчивого сервиса и оказывается победителем.


Как это устроено


Решения отправляют с помощью локально установленного docker-клиента в специальное хранилище. Затем отправленный нам сервис проверяется автоматически системой CodeHub-CodeRunner, разработанной сотрудниками лаборатории Технопарка Mail.Ru Group.


При проектировании решения участник не ограничен ничем, можно использовать абсолютно любые языки и стеки технологий, от классических схем со скриптовым языком и СУБД до самописных велосипедов на C, держащих все нужные данные просто в памяти.


Итак, фактически нужно сделать следующее:


  • создать автономное отзывчивое серверное приложение;
  • собрать его в docker-контейнер и залить в хранилище;
  • обстрелять приложение на выданных боевых данных;
  • … победить.

Все решения запускаются как docker-контейнеры на одинаковых серверах с Intel Xeon x86_64 2 GHz 4 ядра, 4 GB RAM, 10 GB HDD.


Система обстрела



Система проверки изначально создавалась для другого чемпионата (который нам еще предстоит запустить ;) и была доработана для проведения Highload-соревнований. Внутри запускается yandex-танк с движком phantom, который ведет обстрел в несколько потоков с линейно растущим профилем нагрузки. Предварительно, до начала обстрела у пользовательского решения есть время порядка нескольких минут, чтобы обработать данные из подложенного нами в контейнер JSON-файла. Корректная работа с этими данными — необходимое условие победы. Существует два типа проверки — экспресс-проверка и рейтинговая проверка. Об этом ниже.


Как проверяются решения


Типы проверки решения: экспресс-обстрел и рейтинговая проверка.


Экспресс-обстрел доступен неограниченное число раз в сутки, составляет по объему примерно 1/10 от рейтинговой проверки решения. Экспресс-проверка не отличается от рейтинговой по структуре, но использует другой набор данных и является способом узнать, готово ли решение для рейтинговой проверки. Предполагается, что экспресс-обстрел занимает не более 3 минут.


Рейтинговая проверка проводится так же, как и экспресс-проверка, просто на большем количестве данных и запросов. Примерно вот так:


  • перед обстрелом запланировано 180 секунд ожидания для того, чтобы решение участника могло проанализировать переданные тестовые данные и подготовиться к обстрелу;
  • 180 секунд длится первая фаза с линейным профилем от 1 до 200 RPS — будет выпущено: integral (199/180x + 1) dx from 0 to 180 = 18090 GET-запросов;
  • 120 секунд длится вторая фаза с постоянным профилем в 100 RPS — будет выпущено: 100 * 120 = 12 000 POST-запросов, меняющих данные;
  • 120 секунд длится третья фаза с линейным профилем от 200 до 2 000 RPS — будет выпущено: integral (1800/120x + 200) dx from 0 to 120 = 132 000 GET-запросов;
  • валидация ответов ~20 секунд — анализ результатов на сайте ~30 секунд.

Таким образом, сначала проверяется умение участника разложить данные в нужные ему структуры. В первой фазе обстрела проверяется работа сервиса с начальной небольшой нагрузкой. Во второй — корректность обновления сервисом данных и возможная инвалидация кешей (если они понадобятся решению). В третьей же фазе обстрела мы постепенно наращиваем нагрузку, чтобы пощупать решение на прочность.


По результатам рейтинговой проверки участник занимает определенные места в лидерборде текущего чемпионата. Всего обстрел длится порядка 15 минут (при отсутствии очереди). Всего запросов в обстреле: 162 090. Возможно, в ходе чемпионата это число будет увеличиваться.


Решение участника является контейнером docker, который получен с помощью команды docker build. Максимальный размер контейнера на диске не должен превышать 5 Гб. Система проверки выполнит сначала docker pull и затем docker run. В случае успеха, начнется обстрел решения. Участник может использовать любые серверные технологии, языки, фреймворки по своему усмотрению (C++, Java + Tomcat, Python + Django, Ruby + RoR, JavaScript + NodeJs, Haskell или что-то еще). Также и для хранения данных: MySQL, Redis, MongoDB, Memcached — всё, что получится запихнуть в docker.


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


  • основные метрики;
  • корректность ответа;
  • время ответа на запрос;
  • количество ответов в секунду.


Рейтинг решения считается следующим образом: мы берем время всех верных ответов, которые успел дать API во время обстрела. Прибавляем к этому штрафное время для каждого неправильного ответа или запроса, ответ на который мы не смогли получить (штрафное время всегда равно общему таймауту запроса). Участник, суммарное время которого окажется меньше прочих, оказывается выше в лидерборде и имеет шанс стать победителем чемпионата.


Задача


В задаче первого чемпионата участникам нужно написать быстрый сервер, который будет предоставлять Web-API для сервиса путешественников. В начальных данных для сервера есть три вида сущностей: User (Путешественник), Location (Достопримечательность), Visit (Посещения). У каждой свой набор полей. Необходимо реализовать следующие запросы:


  • GET /<entity>/<id> для получения данных о сущности;
  • GET /users/<id>/visits для получения списка посещений;
  • GET /locations/<id>/avg для получения агрегированных данных;
  • POST /<entity>/<id> на обновление сущности;
  • POST /<entity>/new на создание сущности.

Максимальное штрафное время на запрос равно таймауту и составляет 2 секунды (2кк микросекунд). Сразу перед запуском мы подкладываем в контейнер с сервисом данные в формате JSON (они будут доступны в /tmp/data). Решению дается некоторое время для того, чтобы вычитать эти данные и разнести их по внутренним структурам (допустим, разложить в БД).


HTTP-запросы приходят в поднятый контейнер на 80 порт, с заголовком Host: travels.com по протоколу HTTP/1.1, один запрос — одно соединение. Сетевые потери полностью отсутствуют.
Более подробное описание задачи, мини-tutorial для быстрого старта и прочие вспомогательные материалы вы найдете на сайте чемпионата. Кроме того, заходите к нам в Telegram, там всегда рады ответить на вопросы.


Регистрируйтесь, выигрывайте! Удачи!

Комментарии (67)


  1. EjIlay
    11.08.2017 12:36

    Участник может использовать любые серверные технологии, языки, фреймворки по своему усмотрению (C++, Java + Tomcat, Python + Django, Ruby + RoR, JavaScript + NodeJs, Haskell или что-то еще). Также и для хранения данных: MySQL, Redis, MongoDB, Memcached — всё, что получится запихнуть в docker.
    То есть, нужно все зависимости вложить в один контейнер? Это, хотя бы, не красиво. Допустимо, конечно. Почему не композ, например?


    1. sat2707 Автор
      11.08.2017 12:39
      +5

      Честно говоря, с докером и так много повоевали, на compose просто сил не хватило. А с compose мы получили бы какое-то убер-преимущество?


      1. EjIlay
        11.08.2017 13:10
        +1

        Участники бы получили: сейчас нужно все собрать в одном докере и решить проблемы зависимостей, если вдруг случатся, ну и потратить на это врем гарантированно. Либо засунуть докер в докер, чтобы не воевать с зависимостями.
        Еще вариант — написать all-in-one решение, те отнестить к вашему капу как к олимпиадной задаче, а не похожей на реальнй хайлоад


        1. sat2707 Автор
          11.08.2017 14:02
          +2

          Принято. Если этот пилотный чемпионат взлетит — до следующего проведения подумаем о пороге входа с точки зрения докера )


  1. vird
    11.08.2017 14:08

    Я нигде не нашёл информации про допустимый размер JSON. Он умещается в память?


    1. KirEv
      11.08.2017 14:25
      +1

      вот же пишут:

      будет доступен файл data.zip с архивироваными «боевыми» данными (примерно 200 килобайт для предварительного и 20 мегабайт для полного обстрела)


      скачал тестовый архив, 196кб, в нем 3 json«a в сумме дают чуть больше 1мб, если эта информация поможет :)


  1. KirEv
    11.08.2017 14:31
    +2

    Недопонимание

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

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


    из location_1.json

    «distance»: 6, «city»: «Москва», «place»: «Набережная», «id»: 1, «country»: «Аргентина»


    distance — расстояние от города по прямой в километрах, расстояние к чему?

    если
    country — название страны расположения. unicode-строка длиной до 50 символов.

    и
    city — название города расположения. unicode-строка длиной до 50 символов.


    то почему «city»: «Москва» и «country»: «Аргентина» а не «Россия»?

    с толку сбивает, или я что-то не понимаю?


    1. q1t
      11.08.2017 15:39
      +3

      Да по моему просто рандомно сгенерированные данные, за действительность брать не нужно. А distance — от города до достопримечательности.


      1. sat2707 Автор
        11.08.2017 15:42
        +1

        Да, именно так. У нас там есть Москва внутри Аргентины, Кремль в 100 км от Москвы, 130-летние люди, все это посещающие… Это все прелести генеренных тестовых данных, отнеситесь к ним с юмором :)


        1. Alexeyco
          11.08.2017 17:10
          +2

          С каким юмором? Человеку 130 лет, он вынужден ехать в Аргентину, чтобы попасть в Москву. Обычное дело.


      1. KirEv
        11.08.2017 15:55

        Как воспринимать от «города до достопримечательности»?

        Если есть конечный пункт (достопримечательность), должен быть начальный (город). И согласитесь, странно в голове поместить, когда начальный пункт заканчивается к конечном (от города до достопримечательности, но достопримечательность находиться в городе).

        Город как некая единица A к которой может относится достопримечательность B?

        Город как некая единица Z с множеством достопримечательностей [A`,B`,..,Z`]? тогда от города, скорее всего, понимать как от некоторой начальной точки с чего город начинается, ну не знаю, нулевой километр, например.

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

        Не хочу показаться придирчивым =)

        Задача интересная, не то что «посчитать комбинации проколов трамвайного талона NxK компостером IxJ» и всякое такое, было както у нас )


        1. sat2707 Автор
          11.08.2017 17:24

          Наверное логичнее это воспринимать как нулевой километр города, да :)
          Город -> Достопримечательность -> Визит < — Юзер
          Что-то такое


  1. ReklatsMasters
    11.08.2017 15:30
    +3

    Вот и посмотрим насколько решения на nodejs конкурентноспособны. Хотя, если nodejs решение войдёт в топ, хейтеры js найдут к чему придраться.


    1. sat2707 Автор
      11.08.2017 15:43

      Некоторые участники пишут кастомные плюсовые велосипеды :)
      Так что возможно нас всех ждет сюрприз. В любом случае, очень интересно какой стек попадет в топ


      1. dmpas
        11.08.2017 16:04
        +3

        вот увидите, это будет 1С!


    1. Laney1
      11.08.2017 16:13

      парсить запросы сейчас все умеют достаточно быстро. Думаю, основной фокус будет в других вещах: где и как хранить данные, как это все синхронизировать, нужно ли кэширование, и если нужно — то опять же какое. Ни разу не удивлюсь, если в топе окажется грамотно настроенная система на node


    1. overmes
      11.08.2017 17:35
      +1

      не увидим, если авторы сами об этом не напишут. Сейчас в таблице даже привязки по языкам нету.


      1. sat2707 Автор
        11.08.2017 20:01
        +1

        напишем конечно. верю, что вам не менее интересно, чем нам


  1. Laney1
    11.08.2017 16:04

    Все решения запускаются как docker-контейнеры на одинаковых серверах с Intel Xeon x86_64 2 GHz 4 ядра, 4 GB RAM, 10 GB HDD

    лучше указать точную марку процессоров, чтобы можно было судить о поддержке технологий — AVX, AVX2 и т.п. Также, еще больше не помешает указать версию ядра


  1. alexeyknyshev
    11.08.2017 16:37

    Оно хоть с --net=host будет запускаться?


  1. Mehdzor
    11.08.2017 16:43
    +7

    А можно стрелять в ответ?


  1. SeriousDron
    11.08.2017 16:44
    +13

    Вобщем-то задача не совсем про highload получилась. В реальном highload нужен большой RPS, и возможность легко масштабироваться, а latency не так критична. А 2000 RPS выдаст вообще что угодно не сильно скриптовое.

    Опять же, важно чтобы когда у вас вместо 5000 RPS стало 50000 RPS вы просто добавили серверов. А когда надо добавить фичу — просто ее добавили. А здесь победит C/C++-шный велосипед, с libevent, hashmap и btree в памяти и конечным автоматом вместо полноценного парсера JSON. Как это относится к highload — сложно сказать.
    Я даже думал написать такой на Scala (на плюсах 10 лет не писал), но объективно понимаю что код который хочется писать (production-ready) даже близко к топу не окажется.

    Может стоило скажем запускать 2-5-10 слабых контейнеров чтобы частью задачи было распределение нагрузки и синхронизация хранилища? Тут уже встал бы вопрос делать одно выделенное хранилище или распределять запросы, скажем по id. И как тогда считать запросы на аггрегацию. Желательно чтобы еще посреди теста рандомные контейнеры дохли и новые поднимались.
    И объем данных увеличить чтобы не влезал в оперативную память, а в идеале и на винт одной единственной машины. В принципе тоже понятно как решать такую задачу, но уже скорее использовали бы какие-то СУБД чтобы не изобретать кластеризацию на коленке.


    1. sat2707 Автор
      11.08.2017 17:27
      +1

      Дааа! Всё это у нас в планах :) Просто в первом, тестовом чемпионате мы не стали так упарываться. Чтобы, во-первых, порог входа, а во-вторых, сложность отладки для нас самих. Сами знаете, как это бывает :)
      Но упороться очень хочется! Если первый чемпионат пройдет хорошо, обязательно обмеряем и durability, и распределенные игрища, и контейнеры будет рандомно шатдаунить, чтобы посмотреть на балансировку…
      Короче, у нас много веселых идей, но все они не для первого запуска, поэтому и делаем пока что вот такой странный хайлоад, который больше просто про нормальный backend :)


  1. knstqq
    11.08.2017 16:46

    Я что-то не понял из интерфейса и описания: сколько будет раундов?
    Первый раунд — это первый чемпионат? Или в этом чемпионате будет много раундов?


    1. sat2707 Автор
      11.08.2017 20:01

      нет, чемпионат состоит ровно из одного раунда. возможно мы добавим еще немного факультативных раундов (для желающих посложнее например). но перечисленные 6 призов — по результатам одного раунда


  1. svr_91
    11.08.2017 16:53

    А запросы идут один за другим или несколько параллельно?


    1. sat2707 Автор
      11.08.2017 17:29

      Несколько. Процитирую с сайта:

      Перед обстрелом запланировано 180 секунд ожидания для того чтобы решение участника могло проанализировать переданные тестовые данные и подготовиться к обстрелу.
      180 секунд длится первая фаза с линейным профилем от 1 до 200 RPS.
      120 секунд длится вторая фаза с постоянным профилем в 100 RPS
      120 секунд длится третья фаза с линейным профилем от 200 до 2000 RPS.


      1. svr_91
        11.08.2017 18:27

        А как тогда обрабатывать запросы на модификацию одновременно с запросами на чтение?


        1. SeriousDron
          11.08.2017 18:46

          А в чем проблема-то? Так же как вы и вы жизни обрабатываете. Либо БД сама разрулит, либо, если все в памяти — использовать конкурентные структуры данных.
          Но на самом деле еще проще, на самом деле одновременных запросов на запись и чтение вроде не намечается. Вся запись в фазе 2


        1. Hardcoin
          11.08.2017 19:08

          Как обычно. Модификация должна быть атомарная, что б данные не бились.


        1. sat2707 Автор
          11.08.2017 20:02
          +1

          В первом чемпионате мы решили эту проблему за вас — первая фаза это readonly-запросы, вторая — post с редактированием/добавлением данные, третья — снова get-запросы


  1. voidnugget
    11.08.2017 18:09
    -2

    собрать его в docker-контейнер и залить в хранилище

    Эм… докер уже сам по себе большое бутылочное горлышко.


    Ок


    1. Overlay / macvlan / OVS драйвер докера уже имеет довольно высокие накладные расходы и не подходит для чего-то серьёзного.
    2. Есть очень много настроек уровня ядра: шедуллер iommu vfio большие страницы афинность
    3. Если речь идёт о 10/40Gbit трафика и 1М+ RPS с poll-mode драйверами на DPDK ?

    При проектировании решения участник не ограничен ничем

    Вы уже поставили довольно много палок в колёса участникам используя посредственное окружение и очень субъективную оценку.


    \o/
    Прикольно когда организаторы чемпионата по highload'у не знают какой нынче highload бывает.
    У меня малость пригорает.


    1. square
      11.08.2017 19:07
      +1

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


      1. voidnugget
        11.08.2017 19:27

        Это не "мой привычный подход", просто есть user-space решения которые позволяют обрабатывать больше одного миллиона запросов в секунду. Существующие kernel-space решения имеют довольно много ограничений: лишних копирований буферов с kernel-space в user-space, смен контекстов выполнения, избыточного менеджмента памяти etc


        Меня кумарит немного факт что highload рассматривается только в рамках общепринятых прикладных решений которые не работают с железом напрямую — там уже задача ставится как "давайте обработаем запрос клиента за 800 процессорных тактов с когерентностью кэша и использованием cache streaming подходов".


    1. sat2707 Автор
      11.08.2017 20:04

      Видим, что пригорает :)
      Всем не угодишь, к сожалению. Мы рассматривали вариант дать прям живых тачек, отказались от него по понятным причинам


      1. voidnugget
        11.08.2017 20:25

        Спасибо, предположил "понятные причины".
        Понял что можно особо не распыляться — всё равно никто не поймёт "масштабов бедствия".


      1. babylon
        11.08.2017 20:41

        Почему не рассмотрели моё задание? Оно не такое тривиальное как может показаться:)


  1. Chemist
    11.08.2017 22:05

    Не нашел. В качестве id в json используется беззнаковое 32-битное число или знаковое?


    1. sat2707 Автор
      11.08.2017 22:11

      беззнаковое


  1. fahreeve
    11.08.2017 22:06
    +9

    У нас был один сервер, 5 дисков, 20 пакетиков с растворимым кофе и 48 часов до начала чемпионата. Не то чтобы это много, но раз уж решил быть организатором, то надо провести его до конца. Единственное, что беспокоило — это докер. В мире нет никого более беспомощного, безответственного и безнравственного, чем человек запускающий все в одном контейнере. И я знал, что довольно скоро мы в это окунёмся…


    1. sat2707 Автор
      11.08.2017 22:12

      Огоооонь!!! :)


  1. e_Hector
    11.08.2017 22:16

    Не работает кнопка войти на https://highloadcup.ru, ничего не происходит после нажатия (Chrome 59.0.3071.115)


    1. sat2707 Автор
      11.08.2017 22:34

      Возможно, какой-то блокировщик? Пока не было ни одной жалобы, вы первый. В консоль браузера ничего не падает?


      1. e_Hector
        11.08.2017 22:50

        да, видимо дело в AdBlock`e
        image


  1. nekt
    11.08.2017 23:04

    Прочитал эту статью днем, пока ехал с работы, подумал. И решил спросить — а можно ли от одного участника несколько образов выставить? Интересно было бы посмотреть распределение в итоговых результатах различных подходов.


    1. sat2707 Автор
      12.08.2017 00:40

      Можно, по дефолту мы даем каждому участнику два тега в docker registry, можно параллельно тюнить два стека технологий. В принципе, ничто не мешает и в один контейнер два стека запихать, и включать один либо второй :)


  1. vird
    12.08.2017 03:50

    Вот такая команда
    cd /tmp/data && ls -la && unzip data.zip && rm data.zip
    Не выполняется на проде т.к. /tmp подмонтирован почему-то как read.
    Передаю привет docker-compose


    1. sat2707 Автор
      12.08.2017 22:10

      Да, распаковывать нужно к себе куда-нибудь, /tmp/ readonly


  1. nekt
    12.08.2017 17:53

    Жаль в рейтинге нет указания технологии используемой…


    1. sat2707 Автор
      12.08.2017 22:11

      Мы скорее всего попросим самих авторов заполнить некое поле «Стек технологий» у публичных решений. Самим интересно понаблюдать :)


  1. antonksa
    12.08.2017 23:09

    Тарантул с нжинксом интересно выложит кто-то?
    А вообще бредовый конечно квест. 2000 rps выдержит что угодно, а мерять latency… Осипов сказал, например, что в тарантуле пожертвовали latency ради throughput.


    1. voidnugget
      12.08.2017 23:52

      Есть lock-free и есть wait-free подходы, а в системах реального времени используется временное разделение…
      По CAP теореме можем пожертвовать либо консистентностью либо доступностью, а в случае с высоконагрузом это значит задержка vs пропускная способность.


      На DPDK, к примеру, решения работают напрямую с железом — там нет распространённых накладных расходов на коммуникацию и трансляцию вызовов, высоконагрузы уже начинаются с 1М RPS.


      В приложениях на golang'e с использованием fasthttp тот же nginx или haproxy уже является бутылочным горлышком при RPS 80K+. Сторонние кэши типа redis/memcached тоже не вписываются из-за больших накладных расходов на коммуникацию. C fasthttp и тюнингом настроек ядра можно выжать 300К rps… опять же, тут никто не даст поправить sysctl или ядро пересобрать с rt патчами.


      Было бы лучше смотреть на flame-graph'ы под нагрузкой и метрики сложности CFG, чем проводить эстимации в RPS'e — это была бы более точная эстимация при совместном использовании распространённых производительных решений.


    1. vird
      13.08.2017 04:52

      Там тесты на 70k+ RPS


  1. ewgRa
    14.08.2017 01:59

    1. А можно как-нибудь получить более детальное описание, на какие запросы 400 надо отвечать?

    В результатах только пишется что Code не совпадает, посмотреть тело запроса нельзя.

    «Фаза обстрела # 2 (POST)», Locations — «Ответов с неверным http-кодом 3», и непонятно как это исправлять.

    Уже все перебрал, и id <= 0, и добавление location уже существующего, и какие-то органичения, следующие из описания сущностей, кстати, там непонятна формулировка: «unicode-строка длиной до 50 символов.», часто сталкивался что люди по-разному такую формулировку понимают, "<" или "<=".
    Плюс к этому непонятно, какие поля надо проверять на обязательность заполнения, какие нет, какие поля на что проверять (существование связанных сущностей и т.п.), а каким можно доверять.
    В общем хотелось бы более четкого описания на что отвечать 400.

    2. Еще по user.birth_day, в описании «Ограничено снизу 01.01.1930 и сверху 01.01.1999-ым.». В тестовом data.zip, первый же user имеет "-1720915200", это 1915 год. Что делать с таким юзером?

    3. В фазе 1 остался один неверный ответ из 125 — 1. /locations/353/avg?toAge=34&fromAge=24. Тоже непонятно как исправлять с черным ящиком. Сильно сомнительно, что где-то ошибка в алгоритме, или в округлениях, которая вылазит только в одном случае.


    1. ewgRa
      14.08.2017 02:22

      4. distance может быть 0? Как в запросах, так и в сущностях?


    1. ewgRa
      14.08.2017 11:57

      по 1. увидел что можно лог сервера смотреть, попробую print запроса делать, надеюсь это не будет считаться нарушением правил?


  1. robert_ayrapetyan
    15.08.2017 08:31

    Оч. прошу пушнуть https://github.com/sat2707/hlcupdocs/issues/26, три дня убил на чемпионат.