Как я понимаю, Вы выполняете одни и те же запросы на одной базе данных. И по "странному" стечению обстоятельств первый тест медленнее второго, а второй медленнее третьего. Это может говорить о том, что база данных "прогрелась" и одни и те же запросы достаются из кэша.
Вы точно читали статью? Стечение обстоятельств отнюдь не странное и я детально объяснил откуда это берется - из за лишнего сетевого запроса на закрытие транзакции. Можете написать на чистом JDBC запрос с транзакцией и без, тогда возможно станет яснее.
Результаты 1-го запроса отличаются от 2-го и 3-го примерно в два раза. 2 и 3 отличаются в 1.1 раза т.е. можно сказать что они примерно равны. Особенно если запускать тест несколько раз. Т.е. вывод такой что одиночный запрос без транзакции в два раза быстрее чем с ней. Мой тест прогревает базу, если посмотреть внимательно. Я именно этого и добивался чтобы данные закешировались и показать влияние сетевой задержки.
Кроме того, объёмы данных совсем минимальные, чтобы вообще судить о производительности.
О какой производительности? у меня не было цели тестировать производительность БД. И статья не о производительности БД, а про то как мы получаем лишнюю сетевую операцию на пустом месте. Я как раз и ставил цель протестировать около идеальные условия когда данные закэшированны и нет конкуренции и БД отвечает мгновенно. В случае нагрузок БД может отвечать еще дольше и тогда разница будет еще нагляднее, но совершенно очевидно что быстрее сетевые запросы к бд выполняются точно не будут.
Кроме того, не вижу у вас индекса.
Плохо смотрите. Констрейнт уникальности автоматически создает уникальный индекс. Я все больше начинаю сомневаться в вашей компетенции. Все ваши реплики абсолютно невпопад.
Рекомендую для большей наглядности тестов, добавить в базу 10 миллионов записей, выполнять тесты в 100 потоков и каждый тест выполнять на новом инстансе базы данных. Тогда возможно получите результаты более близкие к тем, о которых Вам говорили.
Спасибо, но я воздержусь от вашего предложения тестировать перфоманс БД, вы судя по всему совсем не поняли посыл статьи. И совсем непонятно о каких результатах мне говорили?
Вы делаете очень странные выводы, которые основаны не понятно на чем. Я вам написал, что основной задачей которой я занимался - была оптимизация скл запросов и скорости их выполнения. Действительно спор без смысла. Какой смысл ставить транзакции везде, даже там где они не нужны, в надежде на то что далее кто то не допустит "мифическую ошибку". Как я писал в ответах на другие комментарии, практика говорит об обратном, когда в такие методы не глядя начинают добавлять вызов внешних сервисов.
Понятно что транзакции как таковые тут не причем. Но если они стоят везде, и как пишут выше что лучше их ставить чтобы перебдеть, то вероятность такого случая весьма не низкая и как показывает практика такое случается довольно часто
Кто будет помнить, что на этом методе специально выключили транзакции?
Определение транзакции достаточно простое, чтобы понять случаи где она не нужна.
А потом этот метод разрастается и мы уже там и читаем и пишем, но все уверены, что он же написан стандартно, с транзакциями и должен работать правильно
Как показывает практика именно это и приводит к реальным ошибкам. Когда на методе стоит транзакция, а в него потом пихают все что не попадя, не смотря на транзакцию.
Невыдуманные примеры из этого проекта:
1) над классом стоит транзакция с readOnly=true, пришел разраб добавил метод который меняет данные. Спринг тест это не отловил т.к. тоже был помечен транзакцией. В итоге сервис упал с ошибкой - изменение данных в читающей транзакции. По итогу было очень большое разбирательство.
2) есть метод с транзакцией без readOnly. Он потихоньку разрастается и в него добавляют потом пуш сообщения в кафку. В итогу имеем случаи когда консьюмер получит сообщение с ид сущности, которая не была еще закомичена в исходном методе. И флоу полностью сломано. И отладить такое и найти проблему было очень тяжело.
Мне кажется автор излишне драматизирует. При одном вызове я лично в своих проектах не нашел оборачивания в транзакцию
Может потомучто вы не используете там ручное управления транзакциями?)
ничего критического не вижу.
Ну как сказать.. когда самые горячие методы апи это как раз одиночные запросы к БД, и если убрать транзакцию то скорость работы и пропускная способность этих методов возрастет вдвое.
Да тут я наверное погорячился. Но я вот что то сходу не могу придумать реального примера когда мне потребуется Isolation Levels#Serializable для читающих транзакций, чтобы по сути все работало в однопоток, и остальные потребители ждали завершения чтения. Как правило все необходимые данные можно получить в один запрос или в такой изоляции нет смысла исходя из задач предметной области. У нас такой потребности точно не было.
В статье нет ни слова что скорость не важна, а даже есть цитаты что они опрадвывали это перфомансом. Да я не прописал это явно, но основная проблема которой приходилось заниматься это как раз оптимизация запросов, чтобы меньше удерживать коннекты.
Возможно мне стоило указать это прямо, но статья как раз о моих ошибках
Название статьи намекает на некий "разбор решения", что подразумевает то о чем я уже писал. Что после окончания собеса, можно уже с холодной головой осмыслисть это еще раз и сделать вывод о том что правильнее было бы сделать вот так, хотя бы для Habr, чтобы показать людям какие есть грабли и как делать правильно. Но в итоге разбор очень поверхностный, который соответсвует начальному уровню.
Задача при всей ее кажущейся простоте имеет большой маневр для копания в глубь. О чем я также написал в своем комментарии - "А суть пробелмы в том что большую часть времемни приложение затрачивает на то чтобы ждать ответ по сети". А у вас нет ни малейшего намека на это. Я так думаю (на самом деле уверен) что работадель при оценке этой задаче смотрит именно на это - на этапе устного разбора решения - увидит ли кандитат основную проблему или нет. И предложит ли хотя бы на словах примерные пути решения. И именно от этого зависит размер оффера и позиция которую предложат соискателю.
В том-то и дело, что если мы говорим об ExecutorService, то graceful shutdown для него нужно прописывать руками безотносительно того, внедряется ли он через контекст или напрямую
Вы проверяли? Специально для вас сделал коммит, который показывает что это не так. Graceful shutdown также прекрасно работает.
По пунктам 2 и 3 согласен, по 1 скорее нет, т.к. предложенное вами решение очень громоздкое и трудночитаемое
Выбирать решение это уже дело вкуса. Главное чтобы оно решало основную проблему и было неблокирующим. По поводу избыточности замечу что мой метод combineHttpRequests, что ваш getJokesInBatch, которые отвечают за отправку запросов в параллель, занимают примерно одинаково место. И с тем же Spring Reactor будет также - кобинирование операторов Mono в fluent стиле, также как у меня с CompletableFuture. Я написал код так, чтобы показать что решить задачу можно на чистом JDK(11 и выше) с минимумом сторонних фреймворков.
Если есть сильное стремление использовать реактивщину, то почему бы не использовать reactive feign
Как я уже писал - это дело вкуса. Можно использовать абсолютно любой клиент какой нравится, главное чтобы он был неблокирующим.
Очень поверхностный разбор ошибок и анализ проблемы приведен в вашей статье. Обычно после собеса есть время еще раз обдумать на холодную голову и хотя бы для анализа на Habr продемонстрировать более совершенное решение. На основании которого можно показать другим как делать правильно. Но в статье сплошные антипаттерны на тему как делать не надо, подаются под соусом "вот так правильно".
Начнем по порядку:
Кастомный ExecutorService
Если есть необходимость создать свой собтсвенный пул потоков в spring, то делать это небходимо через его контекст. Тогда не надо будет думать над тем чтобы его завершить и делать костыли вида - @PreDestroy. Т.к. этот бин находить в контексте spring, то он умеет управлять его жизненым циклом и вызовет у него при завершении метод shutdown. Вот тут сделал пример, где показано как это надо делать правильно и в консоль при завершении приложения будет выведено - customShutdown.
Graceful shutdown
Spring уже сам все умеет и не надо делать опять костыли вида - executor.awaitTermination. достаточно добавить настройку (server.shutdown=graceful). Пример опять тут. Если запустить приложение и вызвать http://localhost:8080/test и пока сервис уснул на 5сек, остановить приложение, то в логах увидим:
2024-01-21 00:00:07.728 WARN 448719 --- [ customThread-1] c.e.demo.DemoApplication$DemoController : Execute start method - customThread-1
2024-01-21 00:00:10.687 INFO 448719 --- [ionShutdownHook] o.s.b.w.e.tomcat.GracefulShutdown : Commencing graceful shutdown. Waiting for active requests to complete
2024-01-21 00:00:12.728 WARN 448719 --- [ customThread-1] c.e.demo.DemoApplication$DemoController : Execute end method - customThread-1
2024-01-21 00:00:12.744 INFO 448719 --- [tomcat-shutdown] o.s.b.w.e.tomcat.GracefulShutdown : Graceful shutdown complete
customShutdown
откуда видно:
07.728: началось выполнение метода
10.687: поступил сигнал на заврешение приложение. запустился GracefulShutdown
12.728: дождались завершения уже запущенного метода
12.744: приложение завершилось и в консоль вывелось customShutdown (Spring сам "погасил" наш пул)
Executors.newCachedThreadPool()
Я понимаю, что за час трудно написать идеальное решение. но мне кажется этого и не требовалось. По моему мнению идея была в том, что за час вы напишете простое решение - примерно как вы и сделали, а далее уже непосредственно на устном собеседовании придете к около идеальному решению. Сам бывал на подобного рода собеседованиях, только там уже давали исходный код/проект который нужно было улучшить. Но вы и близко не приблизились к нормальному решению, даже сделав разбор задания уже после окончания собеседования. И оффер скорее всего вам не сделали. Решение вида newCachedThreadPool это извините... как стрелять из пушки по воробьям. А суть пробелмы в том что большую часть времемни приложение затрачивает на то чтобы ждать ответ по сети, а процессорные такты тратит только на маппинг входных данных и респонса, а это очень простые операции, время выполнения которых ничтожно мало и им можно пренебречь условно принять его за 0. И с этим прекрасно справится один поток - да, да.. тот самый event loop.
Правильное решение должно было быть в применении одной из технологий:
1) обычные java CompletableFuture и ассинхроный неблокирующего хттп клиент который уже есть начиная с jdk11
2) spring reactor/webClient
3) coroutines/loom
Ну и чтобы не сотрясать воздух, привожу простое решение с использованием CompletableFuture где на одном потоке свободно обрабатывается 1000 запросов в параллель за 1сек. Я сделал простые два веб метода: 1) http://localhost:8080/testHttpAsync?n=1000 - запускает в паралель n запросов и 2) http://localhost:8080/testHttpAsyncBatch?batchSize=10&n=10 - выполняет последовательно batchSize пачек, в каждой пачке по n запросов в параллель(как у вас в задании), только мне лень было заниматься маппингом и точным расчетом батчей, поэтому просто вынес их в параметры метода.
Итого на примере метода testHttpAsync видно что запрос с n=1 отрабатывает 1сек и с n=1000 тоже примерно 1сек.
И все это спокойно работает на одном потоке customThread-1 (но в реальном мире надо еще следить за памятью чтобы не было OOM)
P.S. Никого не хотел обидеть, я за конструктивную критику. Тесты тоже было делать лень, можете сами поиграться на моих примерах.
Помимо "локальности" данных, новые inline/primitive типы позволяют аллоцировать данные "на регистрах процессорах", не делая дополнительных обращений к опереативной памяти и экономить такты процессора. В целом и сейчас такие кейсы могут упрощаться на этапе escape анализа. За то "скидывать" новые типы данные в ОП или нет, отвечает JVM
Но, справедливости ради, Kotlin-корутины не обязывают использовать только их для эффективного IO.
Это понятно. Но проблема как была так и остается - с использованием блокирующего кода внутри корутин - неважно IO или что то другое. И хорошо если IDE умеет распознавать такие случаи и подсказывать. А так это потенциально опасное место чтобы выстрелить себе в ногу. Именно этот минус я и имел ввиду.
По второму пункту вы сами ответили на свой вопрос - какой смысл использовать корутины, если есть возможность использовать JDK21 и выше.
Я бы к минусам корутин еще отнес - чтобы их эффективно использовать все IO вызовы внутри также должны уметь поддерживать корутины(быть не блокирующими), иначе при попытке вызова обычного блокирующего java метода внутри suspend функции это ни к чему хорошему не приведет. И об этом надо помнить постоянно(в отличии от Loom), а попытки решить эту проблему "разукрашивают" код еще сильнее. Похожий тред есть на stackoverflow.
которые, на минуточку в 20-м веке проиграли мировую войну и подверглись атомным бомбардировкам
Такое мог ляпнуть только необразованный человек, который либо не учился в школе или учился плохо. Получается что вторая мировая война сказалась на демографии Япония хуже чем на СССР? Потери Японии 2.5 млн, против 26.5 у СССР если что. Все это легко проверяется в интернете. Более того у Японии нет почти никакого провала по демографии в этот период в отличии от СССР. На начало войны 1941г - 72.7млн на 1945 72.4млн по данным с вики.
которых в начале 20-го века было в ТРИ раза меньше чем русских
к началу 21-го века стало БОЛЬШЕ чем русских
Если говорить про "совок", то надо сравнивать данные первой переписи 1926 г и последней в СССР 1991, а не начало и конец века. Так если что, в первую мировую войну потери России были одними из самых больших (тоже наверное "совок" виноват). И с этим "наследием" уже пришлось жить дальше СССР. Если говорить только проРоссию/РСФСР то на 1926г было 100млн человек, на 1991г - 148млн. И отрицательное сальдо 41г-45г -23млн, вместо 0.3млн у Японии. Итого на 1926г Русских было в 1.6 раз больше, на 91г в 1.2 раза больше. Где тут три раза? я специально брал данные только по РСФСР, а не по СССР в целом. А если убрать провал по демографии России вызванный второй мировой, то получается что разница +- осталась такой же. И по данным из вики что я приводил, видно что например в тех же 80ых, рост численности был выше чем в Японии.
О как! оказывается все дело в законе. Удобная позиция, найти какой-то факт и полагать что дело только в нем и озвучивать факт только на половину. Что его отменили только в лихие 90-ые. А то что его ввели в ноябре 1941 года, то да, эта дата ни о чем не говорит. А население росло потому что оно было уверено в завтрашнем дне. А заводы и вся инфраструктура которая была построена в СССР этому способствовала. И росло население с середины 20-ых годов(можете посмотреть в википедии), не смотря на первые самые тяжелые пятилетки и репресии второй половины 30х.
Здравствуйте. На сколько мне известно, по моим источникам, внутренний разбор по моей ситуации уже был. Что там у вас было решено по итогу если честно мне не очень интересно ни тогда (т.к. на тот момент я уже определился с новым местом работы), ни тем более сейчас, полтора года спустя. Но статьи с похожим содержанием нет нет, да периодически появляются, это о чем то, да говорит. Вообщем если вам интересно, мои ФИО есть в профиле, по ним со 100% вероятностью вы сможете найти данные обо мне внутри компании. И как я уже говорил в Тинькофф трудоустраиваться больше не собираюсь, поэтому полтора года спустя мне не очень интересна вся эта история. Но вы можете попытаться это исправить, для будующих потенциальных сотрудников.
Совершенно верно. Как я понял это мало кого интересовало - мой прошлый опыт в этой компании. Сначало надо было пройти сито обязательных секций, может быть далее при общении на конкретный проект, мой опыт бы и учли, но я до этого не дошел, т.к. после двух первых секций получил оценку мидл, и никакого далее разговора о пизиции senior речи быть не могло)
Кстати если в других компаниях что я указал, в процессе собеса, чувсвуешь что имешь разговор с квалифицированным инженерорм, то тут явно было ощущение что тебя гонят по опроснику и сверяют с эталоном. и есть только одно правльное решение и никак иначе.
upd: как сказали мои знакомые что там работают - мне просто не повезло с интервьюерами
В ali была классическая задача на поиск в ширину - найти кратчайший путь. В huawei проектировали структуру данных, для нахождения строк, по начальной подстроке. на знание trie - деревьев.
Вы точно читали статью? Стечение обстоятельств отнюдь не странное и я детально объяснил откуда это берется - из за лишнего сетевого запроса на закрытие транзакции. Можете написать на чистом JDBC запрос с транзакцией и без, тогда возможно станет яснее.
Результаты 1-го запроса отличаются от 2-го и 3-го примерно в два раза. 2 и 3 отличаются в 1.1 раза т.е. можно сказать что они примерно равны. Особенно если запускать тест несколько раз. Т.е. вывод такой что одиночный запрос без транзакции в два раза быстрее чем с ней. Мой тест прогревает базу, если посмотреть внимательно. Я именно этого и добивался чтобы данные закешировались и показать влияние сетевой задержки.
О какой производительности? у меня не было цели тестировать производительность БД. И статья не о производительности БД, а про то как мы получаем лишнюю сетевую операцию на пустом месте. Я как раз и ставил цель протестировать около идеальные условия когда данные закэшированны и нет конкуренции и БД отвечает мгновенно. В случае нагрузок БД может отвечать еще дольше и тогда разница будет еще нагляднее, но совершенно очевидно что быстрее сетевые запросы к бд выполняются точно не будут.
Плохо смотрите. Констрейнт уникальности автоматически создает уникальный индекс. Я все больше начинаю сомневаться в вашей компетенции. Все ваши реплики абсолютно невпопад.
Спасибо, но я воздержусь от вашего предложения тестировать перфоманс БД, вы судя по всему совсем не поняли посыл статьи. И совсем непонятно о каких результатах мне говорили?
Вы делаете очень странные выводы, которые основаны не понятно на чем. Я вам написал, что основной задачей которой я занимался - была оптимизация скл запросов и скорости их выполнения. Действительно спор без смысла. Какой смысл ставить транзакции везде, даже там где они не нужны, в надежде на то что далее кто то не допустит "мифическую ошибку". Как я писал в ответах на другие комментарии, практика говорит об обратном, когда в такие методы не глядя начинают добавлять вызов внешних сервисов.
Совершенно верно! Именно этот кейс я имел ввиду. Но соглашусь что моя формулировка был не верна, поэтому поправил статью.
Но справделивости ради надо заметить, что постгрес использует технологию снимков данных для работы, поэтому он по дефолту предоставляет уровень repeatable read https://habr.com/ru/companies/postgrespro/articles/442804/
Да, про это уже писали - https://habr.com/ru/articles/803395/comments/#comment_26672365
Был не корректен в своих фомрулировках, поправил статью.
Понятно что транзакции как таковые тут не причем. Но если они стоят везде, и как пишут выше что лучше их ставить чтобы перебдеть, то вероятность такого случая весьма не низкая и как показывает практика такое случается довольно часто
Согласен был не прав, в столь категоричной формулировке, не смотря на то что это был не наш случай.
В два раза это микрооптимизация?
Определение транзакции достаточно простое, чтобы понять случаи где она не нужна.
Как показывает практика именно это и приводит к реальным ошибкам. Когда на методе стоит транзакция, а в него потом пихают все что не попадя, не смотря на транзакцию.
Невыдуманные примеры из этого проекта:
1) над классом стоит транзакция с readOnly=true, пришел разраб добавил метод который меняет данные. Спринг тест это не отловил т.к. тоже был помечен транзакцией. В итоге сервис упал с ошибкой - изменение данных в читающей транзакции. По итогу было очень большое разбирательство.
2) есть метод с транзакцией без readOnly. Он потихоньку разрастается и в него добавляют потом пуш сообщения в кафку. В итогу имеем случаи когда консьюмер получит сообщение с ид сущности, которая не была еще закомичена в исходном методе. И флоу полностью сломано. И отладить такое и найти проблему было очень тяжело.
Может потомучто вы не используете там ручное управления транзакциями?)
Ну как сказать.. когда самые горячие методы апи это как раз одиночные запросы к БД, и если убрать транзакцию то скорость работы и пропускная способность этих методов возрастет вдвое.
Да тут я наверное погорячился. Но я вот что то сходу не могу придумать реального примера когда мне потребуется Isolation Levels#Serializable для читающих транзакций, чтобы по сути все работало в однопоток, и остальные потребители ждали завершения чтения. Как правило все необходимые данные можно получить в один запрос или в такой изоляции нет смысла исходя из задач предметной области. У нас такой потребности точно не было.
В статье нет ни слова что скорость не важна, а даже есть цитаты что они опрадвывали это перфомансом. Да я не прописал это явно, но основная проблема которой приходилось заниматься это как раз оптимизация запросов, чтобы меньше удерживать коннекты.
Название статьи намекает на некий "разбор решения", что подразумевает то о чем я уже писал. Что после окончания собеса, можно уже с холодной головой осмыслисть это еще раз и сделать вывод о том что правильнее было бы сделать вот так, хотя бы для Habr, чтобы показать людям какие есть грабли и как делать правильно. Но в итоге разбор очень поверхностный, который соответсвует начальному уровню.
Задача при всей ее кажущейся простоте имеет большой маневр для копания в глубь. О чем я также написал в своем комментарии - "А суть пробелмы в том что большую часть времемни приложение затрачивает на то чтобы ждать ответ по сети". А у вас нет ни малейшего намека на это. Я так думаю (на самом деле уверен) что работадель при оценке этой задаче смотрит именно на это - на этапе устного разбора решения - увидит ли кандитат основную проблему или нет. И предложит ли хотя бы на словах примерные пути решения. И именно от этого зависит размер оффера и позиция которую предложат соискателю.
Вы проверяли? Специально для вас сделал коммит, который показывает что это не так. Graceful shutdown также прекрасно работает.
Выбирать решение это уже дело вкуса. Главное чтобы оно решало основную проблему и было неблокирующим. По поводу избыточности замечу что мой метод combineHttpRequests, что ваш getJokesInBatch, которые отвечают за отправку запросов в параллель, занимают примерно одинаково место. И с тем же Spring Reactor будет также - кобинирование операторов Mono в fluent стиле, также как у меня с CompletableFuture.
Я написал код так, чтобы показать что решить задачу можно на чистом JDK(11 и выше) с минимумом сторонних фреймворков.
Как я уже писал - это дело вкуса. Можно использовать абсолютно любой клиент какой нравится, главное чтобы он был неблокирующим.
Очень поверхностный разбор ошибок и анализ проблемы приведен в вашей статье. Обычно после собеса есть время еще раз обдумать на холодную голову и хотя бы для анализа на Habr продемонстрировать более совершенное решение. На основании которого можно показать другим как делать правильно. Но в статье сплошные антипаттерны на тему как делать не надо, подаются под соусом "вот так правильно".
Начнем по порядку:
Кастомный ExecutorService
Если есть необходимость создать свой собтсвенный пул потоков в spring, то делать это небходимо через его контекст. Тогда не надо будет думать над тем чтобы его завершить и делать костыли вида - @PreDestroy. Т.к. этот бин находить в контексте spring, то он умеет управлять его жизненым циклом и вызовет у него при завершении метод shutdown. Вот тут сделал пример, где показано как это надо делать правильно и в консоль при завершении приложения будет выведено - customShutdown.
Graceful shutdown
Spring уже сам все умеет и не надо делать опять костыли вида - executor.awaitTermination.
достаточно добавить настройку (server.shutdown=graceful). Пример опять тут. Если запустить приложение и вызвать http://localhost:8080/test и пока сервис уснул на 5сек, остановить приложение, то в логах увидим:
откуда видно:
07.728: началось выполнение метода
10.687: поступил сигнал на заврешение приложение. запустился GracefulShutdown
12.728: дождались завершения уже запущенного метода
12.744: приложение завершилось и в консоль вывелось customShutdown (Spring сам "погасил" наш пул)
Executors.newCachedThreadPool()
Я понимаю, что за час трудно написать идеальное решение. но мне кажется этого и не требовалось. По моему мнению идея была в том, что за час вы напишете простое решение - примерно как вы и сделали, а далее уже непосредственно на устном собеседовании придете к около идеальному решению. Сам бывал на подобного рода собеседованиях, только там уже давали исходный код/проект который нужно было улучшить. Но вы и близко не приблизились к нормальному решению, даже сделав разбор задания уже после окончания собеседования. И оффер скорее всего вам не сделали. Решение вида newCachedThreadPool это извините... как стрелять из пушки по воробьям. А суть пробелмы в том что большую часть времемни приложение затрачивает на то чтобы ждать ответ по сети, а процессорные такты тратит только на маппинг входных данных и респонса, а это очень простые операции, время выполнения которых ничтожно мало и им можно пренебречь условно принять его за 0. И с этим прекрасно справится один поток - да, да.. тот самый event loop.
Правильное решение должно было быть в применении одной из технологий:
1) обычные java CompletableFuture и ассинхроный неблокирующего хттп клиент который уже есть начиная с jdk11
2) spring reactor/webClient
3) coroutines/loom
Ну и чтобы не сотрясать воздух, привожу простое решение с использованием CompletableFuture где на одном потоке свободно обрабатывается 1000 запросов в параллель за 1сек. Я сделал простые два веб метода: 1) http://localhost:8080/testHttpAsync?n=1000 - запускает в паралель n запросов и 2) http://localhost:8080/testHttpAsyncBatch?batchSize=10&n=10 - выполняет последовательно batchSize пачек, в каждой пачке по n запросов в параллель(как у вас в задании), только мне лень было заниматься маппингом и точным расчетом батчей, поэтому просто вынес их в параметры метода.
Итого на примере метода testHttpAsync видно что запрос с n=1 отрабатывает 1сек и с n=1000 тоже примерно 1сек.
И все это спокойно работает на одном потоке customThread-1 (но в реальном мире надо еще следить за памятью чтобы не было OOM)
P.S. Никого не хотел обидеть, я за конструктивную критику. Тесты тоже было делать лень, можете сами поиграться на моих примерах.
Помимо "локальности" данных, новые inline/primitive типы позволяют аллоцировать данные "на регистрах процессорах", не делая дополнительных обращений к опереативной памяти и экономить такты процессора. В целом и сейчас такие кейсы могут упрощаться на этапе escape анализа. За то "скидывать" новые типы данные в ОП или нет, отвечает JVM
Это понятно. Но проблема как была так и остается - с использованием блокирующего кода внутри корутин - неважно IO или что то другое. И хорошо если IDE умеет распознавать такие случаи и подсказывать. А так это потенциально опасное место чтобы выстрелить себе в ногу. Именно этот минус я и имел ввиду.
По второму пункту вы сами ответили на свой вопрос - какой смысл использовать корутины, если есть возможность использовать JDK21 и выше.
Я бы к минусам корутин еще отнес - чтобы их эффективно использовать все IO вызовы внутри также должны уметь поддерживать корутины(быть не блокирующими), иначе при попытке вызова обычного блокирующего java метода внутри suspend функции это ни к чему хорошему не приведет. И об этом надо помнить постоянно(в отличии от Loom), а попытки решить эту проблему "разукрашивают" код еще сильнее. Похожий тред есть на stackoverflow.
Такое мог ляпнуть только необразованный человек, который либо не учился в школе или учился плохо. Получается что вторая мировая война сказалась на демографии Япония хуже чем на СССР? Потери Японии 2.5 млн, против 26.5 у СССР если что. Все это легко проверяется в интернете. Более того у Японии нет почти никакого провала по демографии в этот период в отличии от СССР. На начало войны 1941г - 72.7млн на 1945 72.4млн по данным с вики.
Если говорить про "совок", то надо сравнивать данные первой переписи 1926 г и последней в СССР 1991, а не начало и конец века. Так если что, в первую мировую войну потери России были одними из самых больших (тоже наверное "совок" виноват). И с этим "наследием" уже пришлось жить дальше СССР. Если говорить только про Россию/РСФСР то на 1926г было 100млн человек, на 1991г - 148млн. И отрицательное сальдо 41г-45г -23млн, вместо 0.3млн у Японии. Итого на 1926г Русских было в 1.6 раз больше, на 91г в 1.2 раза больше. Где тут три раза? я специально брал данные только по РСФСР, а не по СССР в целом. А если убрать провал по демографии России вызванный второй мировой, то получается что разница +- осталась такой же. И по данным из вики что я приводил, видно что например в тех же 80ых, рост численности был выше чем в Японии.
О как! оказывается все дело в законе. Удобная позиция, найти какой-то факт и полагать что дело только в нем и озвучивать факт только на половину. Что его отменили только в лихие 90-ые. А то что его ввели в ноябре 1941 года, то да, эта дата ни о чем не говорит.
А население росло потому что оно было уверено в завтрашнем дне. А заводы и вся инфраструктура которая была построена в СССР этому способствовала. И росло население с середины 20-ых годов(можете посмотреть в википедии), не смотря на первые самые тяжелые пятилетки и репресии второй половины 30х.
Здравствуйте. На сколько мне известно, по моим источникам, внутренний разбор по моей ситуации уже был. Что там у вас было решено по итогу если честно мне не очень интересно ни тогда (т.к. на тот момент я уже определился с новым местом работы), ни тем более сейчас, полтора года спустя. Но статьи с похожим содержанием нет нет, да периодически появляются, это о чем то, да говорит. Вообщем если вам интересно, мои ФИО есть в профиле, по ним со 100% вероятностью вы сможете найти данные обо мне внутри компании. И как я уже говорил в Тинькофф трудоустраиваться больше не собираюсь, поэтому полтора года спустя мне не очень интересна вся эта история. Но вы можете попытаться это исправить, для будующих потенциальных сотрудников.
Совершенно верно. Как я понял это мало кого интересовало - мой прошлый опыт в этой компании. Сначало надо было пройти сито обязательных секций, может быть далее при общении на конкретный проект, мой опыт бы и учли, но я до этого не дошел, т.к. после двух первых секций получил оценку мидл, и никакого далее разговора о пизиции senior речи быть не могло)
Кстати если в других компаниях что я указал, в процессе собеса, чувсвуешь что имешь разговор с квалифицированным инженерорм, то тут явно было ощущение что тебя гонят по опроснику и сверяют с эталоном. и есть только одно правльное решение и никак иначе.
upd: как сказали мои знакомые что там работают - мне просто не повезло с интервьюерами
В ali была классическая задача на поиск в ширину - найти кратчайший путь. В huawei проектировали структуру данных, для нахождения строк, по начальной подстроке. на знание trie - деревьев.