Комментарии 49
Используюем 11 джаву, плюс ломбок и котлин.
Вы провели крутую работу, прямо интересно было читать. Я такими знаниями ещё не обладаю. :)
Но скажите, чего ради ставить на прод 15 версию, когда она ещё не LTS, и мне кажется очевидным, что большинство болячек на ней будет в том или ином виде присутствовать? Мне видится куда выигрышней стратегия "сидеть на 11, а потом перейти на 17". Правда, на ней текстовые блоки и switch новый нельзя, да. Но стоит ли ради плюшек рисковать продом? Я не знаю.
Я вот тоже. Не могу понять, каким образом текстовые блоки могут быть поводом для миграции. Те, кому они нужны прям очень-очень, давно уже и так пишут на скале или груви, и запускаются на JDK 8.
Могут быть важные фиксы? А могут и не быть. Не проще ли прочитать release notes, там же все написано? Если вы не сталкиваетесь с проблемами на условно, 1.8.0_181, как мы, то фиксы вам в общем-то и не нужны.
Тем более смигрировать приложение 24х7 вообще дорого.
Не о всех фиксах пишется в release notes. О фиксе, который описан в статье, в release notes ничего не сказано
Я просто про другой подход к этому делу говорю — если у вас работающее приложение, 24х7, и вы не сталкивались с проблемами на текущей версии JDK/JRE, то вам ставить новую версию вообще говоря не нужно. Чинить вам нечего, а вот сломать она что-то вполне может. А если у вас есть проблемы — то вы их по нормальному, заводите в баг трекер тому же Ораклу, и ждете того релиза, в котором будут исправлены именно они (а не сотня других багов, которые у вас не проявляются).
Переезжать и чинить придется все равно — сейчас или через n лет, когда будет следующий LTS. А вот начать экономить копейку из-за фич и оптимизаций, которые завозят в каждую промежуточную версию, лучше сейчас, чем через n лет. Вполне прагматично.
И если вы посмотрите опрос — то таких как я, на 8, еще 20%.
А если посчитать эти же проценты скажем в ядрах CPU — то я подозреваю, что один наш хадуп стоит десятка компаний поменьше.
Ну и мы не будем никогда его мигрировать на новые версии раз в полгода, как они теперь выходят. Это просто не имеет никакого смысла. Есть куча мест, где можно оптимизировать прикладной софт, и они дают прирост зачастую на порядки (потому что оптимизации в JDK это память и процессор, а мы оптимизируем на уровне база, сеть, диски и т.п. — и это обычно дает больший эффект и сразу). В нашем случае миграция — это скажем построение нового кластера, на новых железках, и новой версии хадупа.
Вот, посмотрите скажем сюда. И вы тут увидите, что поддерживается только 11. И все.
Да и у апача все примерно так же:
Supported Java Versions
Apache Hadoop 3.3 and upper supports Java 8 and Java 11 (runtime only)
Please compile Hadoop with Java 8. Compiling Hadoop with Java 11 is not supported: HADOOP-16795 — Java 11 compile support OPEN
Apache Hadoop from 3.0.x to 3.2.x now supports only Java 8
Apache Hadoop from 2.7.x to 2.10.x support both Java 7 and 8
Ну так и куда сейчас на 15? Это просто невозможно.
Ну джава, к счастью, не заканчивается на хадупе. Те, кто держит большие флоты в облаках для работы своих 24/7 сервисов, будут рады каждому сэкономленному метру памяти и каждой миллисекунде уменьшающегося latency. В зависимисти от масштаба, разница в стоимости виртуального железа на месяц или год может многократно покрыть стоимость перехода, который делается один раз и не очень долго.
Вот прям если не лень — можете мне назвать список фич, которые были внедрены с Java 8
Могу перечислить то, из-за отсутствия чего лично я вынужден придумывать многоэтажные костыли уже несколько месяцев: VarHandles (9), нормальный HttpClient на NIO (11), absolute bulk read/write для ByteBuffer (14).
Так я и не имел в виду, что заканчивается. Я скорее это как реальный пример, когда есть в наличии большие кластера, десятки тысяч ядер, и их просто так нельзя мигрировать, некуда. Конечно, есть и другие варианты, и с ними все может быть сильно проще.
>HttpClient на NIO (11) — а разве это нельзя реализовать самому? Или там под капотом какие-то низкоуровневые фичи? Ну в смысле — костыли это понятно, неохота, но если они возможны — то это вполне может быть дешевле миграции. Вот для меня примерно так это и обстоит. Ну то есть это — как те же текстовые блоки, хорошо конечно, что они появились — но я пишу на скале и груви, и у меня они по факту всегда были, сколько помню.
HttpClient на NIO (11) — а разве это нельзя реализовать самому?
Можно, но довольно трудозатратно. Да и зачем, если вот он уже написан. Через несколько лет, он уже будет в каждом утюге, но конкретно сегодня 30-40% людей все еще сидят на 8, к сожалению.
Ну в смысле — костыли это понятно, неохота, но если они возможны — то это вполне может быть дешевле миграции.
Это же не просто костыли, это тормозные костыли. Локи вместо CAS, массивы байтов в хипе вместо direct buffers.
> следующий хадуп уже не запуститься на 8.
У нас текущий cloudera 5.14 (2.6). Следующий cloudera 7.1 не просто запускается, но и рекомендует 1.8 все равно. И самосборный 3.3 тоже пока на 1.8 живет, только OpenSDK вроде.
можете мне назвать список фич, которые были внедрены с Java 8
Вот вообще киллер-фича: https://openjdk.java.net/jeps/358 — доступна с Java 14.
JDK 14, не 11 и в ней эта фича по умолчанию отключена.
Если этот баг вам мешает и, судя по комментариям к багрепорту, известны способы его воспроизведения, почему бы не поправить его самому? Нужны только отладчик и желание.
Это фича JVM и про это английским по белому написано в этом JEP. Для лучшего эффекта class-файлы с проблемным кодом стоит собрать с полной отладочной информацией. Для javac
это параметр -g
.
Ну, не совсем. Спарк не настолько простая фигня, чтобы это можно было сделать скажем за 30 минут. Если это происходит в распределенной среде, уже подцепиться отладчиком не совсем тривиальная задача, потому что вы не знаете в общем случае, на каком узле все будет происходить.
>Для javac это параметр -g.
Ну т.е. компилятор любой версии, но с отладкой? Впрочем, мне это вряд ли поможет, пока что никакой хадуп не поддерживает Java 14. И это не javac, а скала (но это уже мелочи).
На самом деле есть другой способ, не сильно простой, но действенный — залезть в исходники, и вникнуть, что там происходит.
Если в исходниках написано x().y().i = 99;
, то для вникания придётся ещё вникнуть во все возможные детали реализации x
и y
(а в скале — ещё и в i
), что и когда они могут возвращать, а если это интерфейсы, то проще сразу с отладчиком влезть, чем вручную выполнять весь этот код в голове.
И вот тут-то JEP 358 и должен помочь, потому что призван по крайней мере ограничить круг поиска одним конкретным местом, так как сразу будет ясно, что именно вернуло неожиданное значение. А вот так чтобы всё сразу показали — ну, серебряных пуль не бывает, но из этого никак не следует, что нет смысла даже и пытаться.
def toCatalystDecimal(hdoi: HiveDecimalObjectInspector, data: Any): Decimal = {
if (hdoi.preferWritable()) {
Decimal(hdoi.getPrimitiveWritableObject(data).getHiveDecimal().bigDecimalValue,
hdoi.precision(), hdoi.scale())
} else {
Decimal(hdoi.getPrimitiveJavaObject(data).bigDecimalValue(), hdoi.precision(), hdoi.scale())
}
}
Вот что падает. Тут вполне очевидно, что именно null. Ну или во всяком случае, подозреваемый вполне явно виден. Нужно лишь пройтись по стеку, и понять, где он там по дороге таким стал.
Вы упорно не хотите понимать, что фича не является серебряной пулей, несмотря на огромную полезность.
Да, в таком коде, где вся работа ведётся с единственным суперобъектом, где лежит всё — почти все случаи ошибок будут связаны именно с суперобъектом (но всё равно не все, потому что в Java precision()
может возвращать null Double
, а конструктор Decimal
при этом — ожидать double
).
Но сводить весь язык к сугубо своим ситуациям — это путь в никуда. Вам не нужно обновляться (точнее — вы не можете, зелен виноград), потому вас никакие фичи не переубедят. Остальные обновятся.
пока ты выявишь и углубишься в проблемы на 15, как раз и выйдет 17.
Вы правы, стратегия обновления с LTS на LTS релевантна, но это медаль с двумя сторонами. Обновляя версии часто неминуемо будут всплывать разного рода проблемы, о которых не говорится в release notes.
И вот здесь кажется, что использовать LTS и не обновляться выгодно. Но, когда выйдет новая LTS, пусть 17-я, вам все равно придется решать проблемы промежуточных версий, и обновление займет значительно больше времени. А так получается «есть слона по частям». Вот такой путь вас может ждать при обновлении с 11 на 17-ю. Это те проблемы, которые мы решали обновляя Java:
При переходе с 11 на 14 изменились логи gc.
При переходе с 14 на 15 проблема описана в статье.
Вам придется решать эти проблемы сразу все. Основной поинт такой: чем длиннее цепочка обновлений, тем тяжелее это обновление сделать.
Ну и вообще говоря, решить проблемы сразу все может оказаться проще. Ну вот смотрите, у нас в телеграмм чатике стенда разработки пасется скажем человек 500 разработчиков (точные цифры тут не важны, главное — что у нас много команд). Представим себе, что мы начали мигрировать с Java 8 (as is) скажем на Java 15. Мы наступили на разные грабли — но весьма вероятно, что разные команды на разные их виды. В итоге, одна команда, решив одну проблему, избавляет другие от повторения процесса. Зато сами стенды мы обновим один раз, а не четыре — т.е. тем кто это будет делать, задача упростится.
Ну т.е. это зависит от того, какова у вас структура компании. Не исключаю, что где-то вообще все эти проблемы решат централизованно, потому что это «одна из ключевых задач команды Архитектура» (кстати, поправьте на Архитектора, а то как-то смешно получилось :)
В итоге, одна команда, решив одну проблему, избавляет другие от повторения процесса
Переходите на Java 15, оно того стоит.
Переходите в клуб любителей БДСМ)
Большое спасибо за статью!
У меня на проекте случилось тоже самое — при обновлении с 14 на 15 увеличилось потребление памяти, ограничения на размер Code Cache не было. На тот момент просто подняли лимиты в K8s, так как ресурсы были. Сейчас тоже думаю заняться вопросом исследования потребления памяти, т.к. функционал растёт, и память хочется ограничить везде, где можно.
Также хочу высказаться в поддержку того, что гораздо проще собирать костыли в небольшом количестве раз в полгода, чем сразу много оптом. Кроме того, все эти промежуточные релизы Java — это именно релизы, полноценные, прошедшие тесты. Это ведь не Beta, где реально могут что-то сломать. А багов каждый раз фиксят много.
Кстати, а почему у вас такие классные базовые образы JDK с датами вместо версии?
Интересно. На самом деле формат версии как JDK, так и докер-образа может включать перерелизы, например:
$docker run -it bellsoft/liberica-openjdk-alpine-musl:15.0.2-10 java -version
openjdk version "15.0.2" 2021-01-19
OpenJDK Runtime Environment (build 15.0.2+10)
OpenJDK 64-Bit Server VM (build 15.0.2+10, mixed mode)
Как мы переходили на Java 15 или история одного бага в jvm длиной в 6 лет