Skip to content

Gvo3d/rest_task

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

26 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

REST TASK Build Status

!Все запросы были проверены на 100000 объектов. !Регексы написанные в результатах перед исполнением прогоняются через PatternResolver, который изменяет их для соответствия заданию. Тесты проводились с помощью флага: -Xloggc:garbage-collection.log

Алгоритм: Выгрузка данных из БД в распределёный кэш Hazelcast(одна нода), получение результата с помощью предикатов, отправка через Jackson. Старт приложения: 188196 ms.

Branch: hazelcast-jackson Запрос: localhost:8080/hello/contacts?nameFilter=^[\d].$ Итерации: Результаты: Regex: ^[\d].$ returns list of 83964 elements. With processing time: 3121 ms. Время доставки на postman: 13719 ms. Сборщик мусора: 229.113: [GC (Allocation Failure) 424679K->193715K(500736K), 0.0921534 secs] 229.745: [GC (Allocation Failure) 411827K->196541K(498176K), 0.0994548 secs] 230.484: [GC (Allocation Failure) 414653K->204174K(491008K), 0.1198201 secs] 231.294: [GC (Allocation Failure) 398734K->205121K(496128K), 0.1038071 secs] 232.702: [GC (Allocation Failure) 399681K->208339K(487936K), 0.1045611 secs] 238.629: [GC (Allocation Failure) 404947K->207679K(493056K), 0.0369970 secs] 239.590: [GC (Allocation Failure) 404287K->207767K(498688K), 0.0329656 secs] 239.623: [Full GC (Ergonomics) 207767K->141069K(494080K), 0.6937932 secs] Результаты: Regex: ^[\d].$ returns list of 83964 elements. With processing time: 3873 ms. Время доставки на postman: 21501 ms. Сборщик мусора: 352.374: [GC (Allocation Failure) 347405K->142261K(496128K), 0.0414930 secs] 353.172: [GC (Allocation Failure) 348597K->147752K(497152K), 0.0192655 secs] 354.071: [GC (Allocation Failure) 356136K->154543K(497664K), 0.0366456 secs] 354.939: [GC (Allocation Failure) 362927K->170587K(493568K), 0.0762614 secs] 356.342: [GC (Allocation Failure) 382555K->185886K(495616K), 0.1248126 secs] 367.165: [GC (Allocation Failure) 397854K->186070K(504320K), 0.0318179 secs] 368.038: [GC (Allocation Failure) 409814K->186086K(503808K), 0.0048489 secs] Результаты: Regex: ^[\d].$ returns list of 83964 elements. With processing time: 2911 ms. Время доставки на postman: 21567 ms. Сборщик мусора: 455.111: [GC (Allocation Failure) 409830K->188594K(505856K), 0.0108092 secs] 455.728: [GC (Allocation Failure) 417970K->194724K(506368K), 0.0235942 secs] 456.323: [GC (Allocation Failure) 424100K->202136K(508416K), 0.0502043 secs] 456.912: [GC (Allocation Failure) 435277K->225753K(508928K), 0.0302938 secs] 456.942: [Full GC (Ergonomics) 225753K->167857K(531968K), 0.6585061 secs] 469.745: [GC (Allocation Failure) 404913K->188729K(536576K), 0.0380987 secs] 470.697: [GC (Allocation Failure) 432441K->188929K(537600K), 0.0386981 secs]

Тот же запрос, но ответ сериализуется конкатенацией строк через StringBuilder: Branch: hazelcast-string Запрос: localhost:8080/hello/contacts?nameFilter=^[\d].$ Итерации: Результаты: Regex: ^[\d].$ returns list of 83964 elements. With processing time: 3564 ms Время доставки на postman: 8203 ms. Сборщик мусора: 189.261: [GC (Allocation Failure) 342294K->201922K(425472K), 0.1604166 secs] 189.819: [GC (Allocation Failure) 349378K->202971K(444928K), 0.1156603 secs] 190.236: [GC (Allocation Failure) 334043K->206522K(448512K), 0.1262006 secs] 190.634: [GC (Allocation Failure) 337594K->210170K(437760K), 0.1081517 secs] 191.020: [GC (Allocation Failure) 344314K->215582K(443392K), 0.0846705 secs] 191.105: [Full GC (Ergonomics) 215582K->138909K(451072K), 0.5890395 secs] 192.211: [GC (Allocation Failure) 273053K->152792K(461824K), 0.0302687 secs] 192.821: [GC (Allocation Failure) 302808K->167312K(464384K), 0.0796026 secs] 193.417: [GC (Allocation Failure) 317328K->173335K(459264K), 0.3036940 secs] 194.258: [GC (Allocation Failure) 328983K->179705K(462848K), 0.1484843 secs] 195.147: [GC (Allocation Failure) 331337K->179809K(472064K), 0.8438500 secs] 196.612: [GC (Allocation Failure) 349281K->192373K(473600K), 0.0109413 secs] Результаты: Regex: ^[\d].$ returns list of 83964 elements. With processing time: 3468 ms. Время доставки на postman: 21849 ms. Сборщик мусора: 296.753: [GC (Allocation Failure) 361845K->211703K(474624K), 0.0291043 secs] 296.782: [Full GC (Ergonomics) 211703K->142151K(482304K), 0.3969014 secs] 297.666: [GC (Allocation Failure) 318279K->146982K(482816K), 0.0152063 secs] 298.365: [GC (Allocation Failure) 323110K->151734K(488960K), 0.0315578 secs] 298.974: [GC (Allocation Failure) 335030K->168622K(424448K), 0.0545567 secs] 299.707: [GC (Allocation Failure) 345979K->176101K(489472K), 0.0434398 secs] 300.416: [GC (Allocation Failure) 362981K->184360K(487424K), 0.0248704 secs] 301.333: [GC (Allocation Failure) 371240K->190762K(493056K), 0.0247905 secs] 301.967: [GC (Allocation Failure) 387370K->203278K(490496K), 0.0134740 secs] Результаты: Regex: ^[\d].$ returns list of 83964 elements. With processing time: 3566 ms. Время доставки на postman: 21873 ms. Сборщик мусора: 371.956: [GC (Allocation Failure) 399886K->203958K(496128K), 0.0148206 secs] 371.971: [Full GC (Ergonomics) 203958K->140686K(496128K), 0.3563688 secs] 372.915: [GC (Allocation Failure) 347022K->146264K(496128K), 0.0169890 secs] 373.795: [GC (Allocation Failure) 352600K->153077K(504832K), 0.0297807 secs] 374.647: [GC (Allocation Failure) 372213K->169625K(504832K), 0.0605391 secs] 375.624: [GC (Allocation Failure) 388761K->186447K(507392K), 0.0855140 secs] 376.457: [GC (Allocation Failure) 415311K->197504K(506880K), 0.0338117 secs] 377.325: [GC (Allocation Failure) 426368K->210021K(516608K), 0.0123178 secs]

Алгоритм: Выгрузка данных из БД на ходу, парсинг с помощью DataCollector, отправка через Jackson. Старт приложения: 20469 ms.

Branch: standart-jackson Запрос: localhost:8080/hello/contacts?nameFilter=^[\d].$ Итерации: Результаты: Regex: ^[\d].$ returns list of 83964 elements. With processing time: 1923 ms. Время доставки на postman: 35423 ms. Сборщик мусора: 24.569: [GC (Allocation Failure) 202699K->31598K(245248K), 0.0340312 secs] 26.942: [GC (Allocation Failure) 218478K->39217K(303616K), 0.0433903 secs] Результаты: Regex: ^[\d].$ returns list of 83964 elements. With processing time: 3679 ms. Время доставки на postman: 18797 ms. Сборщик мусора: 196.173: [GC (Allocation Failure) 280369K->106907K(348160K), 0.3354763 secs] 196.509: [Full GC (Ergonomics) 106907K->91830K(416768K), 1.7863974 secs] Результаты: Regex: ^[\d].$ returns list of 83964 elements. With processing time: 1554 ms. Время доставки на postman: 18842 ms. Сборщик мусора: сборки мусора не производилось.

Тот же запрос, но ответ сериализуется конкатенацией строк через StringBuilder: Branch: standart-string Запрос: localhost:8080/hello/contacts?nameFilter=^[\d].$ Итерации: Результаты: Regex: ^[\d].$ returns list of 83964 elements. With processing time: 4225 ms. Время доставки на postman: 20521 ms. Сборщик мусора: 15.389: [GC (Allocation Failure) 213766K->33297K(327680K), 0.0421523 secs] 40.842: [GC (Allocation Failure) 302097K->68901K(353792K), 0.2024574 secs] 41.045: [Full GC (Ergonomics) 68901K->56307K(402432K), 0.8367093 secs] Результаты: Regex: ^[\d].$ returns list of 83964 elements. With processing time: 3099 ms. Время доставки на postman: 20181 ms. Сборщик мусора: 111.658: [GC (Allocation Failure) 340979K->128503K(379392K), 0.3514655 secs] 112.010: [Full GC (Ergonomics) 128503K->78966K(428544K), 1.1431988 secs] Результаты: Regex: ^[\d].$ returns list of 83964 elements. With processing time: 1582 ms. Время доставки на postman: 20363 ms. Сборщик мусора: 199.638: [GC (Allocation Failure) 326372K->121495K(442880K), 0.1964062 secs]

Выводы: Тесты показали, что постраничная выгрузка данных для парсинга не имеет практического смысла для решения таких задач как экономия памяти и уменьшения времени работы CPU, наоборот - чаще производится Garbage collection, больше процессорного времени требуется для обработки запроса, хотя при выгрузке неполной коллекции не приходится загружать её одномоментно в память и проводить парсинг. Теоретически постраничная выгрузка может спасти приложение от OutOfMemoryError, но только при выгрузке не всех данных, а их части, т.е. не решает проблему целиком. Ручная сериализация через StringBuilder, несмотря ни на что, проигрывает в производительности прогону объектов через Jackson mapper. Я заглянул в GraphQL, но там та же проблема - данные не выгружаются из БД сразу пользователю, они проходят через сервис-layer, что означает немалые расходы памяти на хранение и сериализацию объектов. Самым лучшим алгоритмом считаю стандартную выгрузку данных одним запросом из БД с последующим прогоном через DataCollector и сериализацией с помощью Jackson mapper.

Регексы согласно задания: Начинается: ^[a].$ Содержит: ^.[a].$ Кончается: ^.[a]$ Не начинается: ^[^a].$ Не содержит: ^.[^a].$ Не кончается: ^.[^a]$

Регексы использующиеся внутри приложения для валидации строк(отличающиеся от таких же согласно задания): Не начинается: ^[^a].$ Не содержит: ^[^a]$

Предыдущая информация:

Замечание: Докер-контейнер ссылается своим портом 5432 на порт 5433 хост-машины, т.к. Travis данный порт считает используемым: docker: Error response from daemon: driver failed programming external connectivity on endpoint rest (3fabc0d80ce23b65140193d9c5e7e8fa298f578acff92e77dbe53905bf605eef): Error starting userland proxy: listen tcp 0.0.0.0:5432: bind: address already in use.

Запуск: 1)Запаковать jar-архив с помощью maven 2)установить docker 3)скопировать в одну директорию ROOT.jar и start.sh, запустить скрипт(как вариант - запустить докер-команду из скрипта и просто запустить приложение из IDE). *В случае использования MS Windows переименовать start.sh в start.bat, если докер устанавливается на Win 7, то в application.yml (dataSource.url) вписать валидный адресс docker-контейнера(Например: jdbc:postgresql://192.168.99.100:5433/rest). Эти же настройки надо внести в TestJpaConfig(для тестов) и pom.xml(для запуска мавен-таски update).

Запуск тестов: 1)Тестовые настройки соединения находятся в TestJpaConfig.java. 2)У Liqubase модуля под Maven есть goal "update" который отвечает за создание БД на тот случай, если тесты проводятся до первого запуска приложения.

Благодаря preConditions в db-creation.xml ошибок не будет если будет повторная попытка запуска, однако если компилировать на одной машине, а запускать на другой, то надо применять флаг -Dliquibase.should.run=false, иначе будет error при отсутствии instance postgre.

Доп. инфо: http://www.liquibase.org/documentation/maven/generated/update-mojo.html#skip

Опции: В application.yml есть настройки, которые отвечают за количество добавляемых строк и длинну имени. application: dataQuantity - количество строк, которые будут добавлены в БД charsCount - количество символов параметра name(не больше 1000 - ограничение в db-creation.xml)

*Первоначальное условие про миллион строк я проверял на 10 тысячах, а потом и на 100, т.к. мой компьютер не тянет миллион строк. Предположительное количество оперативной памяти, которое нужно - около 8 гб(только под jvm).

Виртуализация: Билдим контейнер из dockerfilе, при этом вставляем в свойство url(application.yml, pom.xml) название нашего сетевого докер-контейнера: "restgres" вместо "localhost" и меняем порт 5433 на 5432, т.к. внутри виртуального контейнера этот порт не занят и связь будет идти по нему, после чего в одной директории ложим ROOT.jar и Dockerfile, запускаем консоль и из под директории где хранятся оба этих файла вводим команды.

1)Первым делом создаём сеть: docker network create -d bridge restnet 2)После этого запускаем постгрес-контейнер: docker run --name rest --network=restnet --network-alias restgres -p 5433:5432 -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=root -d postgres:9.6 3)После этого запускаем наш образ: docker build --network=restnet /path/to/a/Dockerfile 4)Вводим docker images и находим в списке наш новосозданный образ. 5)Запускаем: docker run --name rest_task -network=restnet -p 8080:8080 ${id образа}

Всё, запуск приложения в двух совмещённых контейнерах завершён. Благодаря тому что порт 8080 прокинут на нашу внешнюю машину, мы можем стучаться прямо на следующий эндпоинт(в случае Win 7 cтучимся на сетевое имя докер-хоста).

Endpoint: localhost:8080/hello/contacts?nameFilter=

Тестирование методик сравнения На моих настройках(ParsersTest.java): SimpleMatcher(100 повторений - в м/с): 21-24 PatternMatcher(100 повторений - в м/с): 7-8 ApacheMatcher(100 повторений - в м/с): 7-8

Был выбран метод основанный на библиотеке Apache.

P.S.: По причине CVE-2016-6816 Tomcat отказывается работать с символами "^", "$" и "" в строке запроса. У меня было три варианта: 1)Перевести стринги запросов в RequestBody и отправлять данные в json методом POST. 2)Запустить приложение на embedded Jetty 3)В сервис-слое заменять символ "^" на другой символ, например "!" и аналогично для других.

Был протестирован и выбран второй вариант и Jetty успешно работает с символами: ^, , $.

P.S.2: Были опробованы две методики итерации - с помощью парралельного стрима и самописный на основе ThreadExecutorPool и Future:

1)List contacts = data.parallelStream().filter(c -> regexValidator.isValid(c.getName())).collect(Collectors.toList());

2)Текущая методика на основе класса DataCollector.

Запрос: localhost:8080/hello2/contacts?nameFilter=d.* Тайминги (в м/с): Parralel Stream: 426,567,552,1023,364 DataCollector: 253,390,298,196,302

По общей оценке скорости выполнения текущий вариант оказался более быстрым, хотя и более прожорливым к памяти за счёт большего количества объектов.

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published