(java || kotlin) && devOps
355 subscribers
7 photos
1 video
7 files
354 links
Полезное про Java и Kotlin - фреймворки, паттерны, тесты, тонкости JVM. Немного архитектуры. И DevOps, куда без него
Download Telegram
Небольшая заметка.

Мы обсуждаем, как хорошо AI пишет код. Но люди его используют совсем не для этого: https://t-j.ru/news/how-people-use-chatgpt

Это я, к тому, для каких задач его будут оптимизировать.

С другой стороны: да, 4% казалось бы немного. Но если сравнить с обычным поиском, то рост раза в 2. Точных цифр по доле запросов по разработке в поисковом трафике нет, но тот же ChatGPT дает неплохую оценку исходя из числа разработчиков и среднего числа их запросов в день.

#ai
Как быстрее погрузиться в код?

Речь про существующий микросервис и нового разработчика.
Я уже писал, что JavaDoc (KDoc) не является обязательным для каждого метода\поля или класса (как минимум для бизнес-приложения, общие библиотеки - особый кейс), т.к. документацию никто не читает.
А что же тогда будет документацией? Например, тесты. Их конечно тоже новичок не будет читать на 100%, но во-первых их и так нужно писать, а во-вторых - при рефакторинге падающий тест покажет, что забыли поправить, а в целом любой существующий тест изменяемого класса покажет, как он работает.

А недавно я нашел еще один полезный способ задокументировать микросервис так, чтобы этой "документацией" пользовались.
Начну немного издалека. Есть такая ИТ консалтинговая компания как Thoughtworks. Ну есть и есть, где мы и где консалтинг. Но там работает такой небезызвестный человек, как Мартин Фаулер. Главный научным руководителем https://www.thoughtworks.com/profiles/leaders/martin-fowler
А это внушает некий уровень доверия.
Так вот, компания ведет реестр технологий а-ля техрадар.
И в текущей его версии есть такая штука https://www.thoughtworks.com/en-de/radar/techniques/api-request-collection-as-api-product-artifact
как коллекция API запросов как артефакт продукта.
На самом деле мысль лежит на поверхности, я уже достаточно давно практикую прихранивание запросов в формате IDEA api collection вместе с исходниками в тех проектах, над которыми приходилось работать. Да, над форматом стоит подумать отдельно, возможно Insomnia будет по-универсальнее, зависит от команды и организации. Но сама идея мне очень нравится. Такой документацией точно будут пользоваться.

P.S. Кто ее должен делать - разработчики или тестировщики и нужно ли шарить коллекцию между ними - тоже вопрос для обсуждения. В идеале - думаю, что да.

P.P.S. Да, когда я говорю про артефакт продукта - это значит мало ее сделать, ее нужно поддерживать в актуальном состоянии.

#api #onbording #documentation
🔥1
Основные проблемы AI в разработке.

Я вижу две основные проблемы.

Первая - принципиально недетерминированный ответ как отражение вероятностной природы LLM. Если в креативных задачах это плюс, но в разработке скорее минус.

Вторая - естественный язык не самое лучшее API из-за своей неоднозначности.

И для второй, а частично и для первой проблемы есть решение - паттерн structured output. Суть проста - мы говорим модели, в каком виде хотели бы получить ответ. Это может быть JSON схема или класс Response. Базовый формат - JSON, но он на уровне библиотеки легко трансформируется в класс для большинства языков программирования. Ключевой момент - вызов модели должен вернуть правильный по структуре JSON с вероятностью 100%. И далее его можно или без лишних проверок парсить и передавать на вход следующему методу.

Реализован паттерн должен быть в самой модели, так как на уровне библиотеки или промта гарантии 100% соответствия получить нельзя.

Вот статья с примером использования:
https://habr.com/ru/articles/923096

P.S. Паттерны есть везде, коллекция AI паттернов постепенно растёт)

#ai #llm
RestTemplate is dead, baby)))

Spring наконец-то решили задепрекейтить RestTemplate.
Пруф: https://spring.io/blog/2025/09/30/the-state-of-http-clients-in-spring

Его замены в fluent стиле: RestClient для синхронного и WebCLient для асинхронного взаимодействия.
Видимо, команда Spring таки выпилила его из компонентов фреймворка и теперь предлагает это сделать всем остальным)

На самом деле я немного добавил сенсационности в пост.
А реальная хронология событий планируется такая:
- в ноябре этого года (Spring 7.0) будет объявлено о том, что компонент deprecated
- формально deprecated он станет в ноябре 2026 года (Spring 7.1)
- выпилят в Spring 8.0 где-то в 27 году.

Это мир Java == мир обратной совместимости)

#spring #web
👍2
Языки программирования общего назначения?

Java, Python, C, Go. Формально, технически - да, все это универсальные языки, которые можно применять в любой, задача. А фактически?

Говорим Python - подразумеваем Data Science, ML и AI.
Go захватил разработку крипты. Плюс операторы в k8s.
Kotlin - от 70 до 90% приложений в Google Play (топовых приложений если быть точным, т.к. есть "хвост" легаси)
Похожая картина у Swift - примерно 60% в iOS (доля бинпрников в iOS 17).
C# - нативные Windows приложения (Delphi и VB.NET эту битву проиграли).
Groovy нашел свою нишу в Jenkins как DSL (Gradle еще, но там его теснит Kotlin, да и не должны Gradle скрипты быть большими).
Lua - язык плагинов (Nginx, Tarantool) и скриптовый язык для игр. То бишь - король DSL.
Драйвера пишут на C (до сих пор ли? Раньше так было).

Да, экосистема (библиотеки, фреймворки, документация, сообщество) рулит)

И вопрос - что я забыл?

#lang
Небольшой забавный (и при этом грустный) факт о JVM.

Для запуска Hello world приложения на Java JVM загружает 450 классов.

Пруф: https://inside.java/2025/01/28/jvm-start-up/


#jvm #fun_facts
😱4
Если IDEA легла при старте. Или mini IDEA troobleshooting guide.

Что можно сделать?

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

Предусловие - надо вспомнить где у вас хранятся настройки IDEA. По умолчанию на примере Windows это %USERDATA%\AppData\Local\xxx\yyy, xxx - это JetBrains\GIGAIDE\..., а yyy - имя IDE.
Но через idea.properties это место можно переопределить.

И так.
1) Логи. По умолчанию лежат в %USERDATA%\AppData\Local\xxx\yyy\log\idea.log
В логах стоит обратить внимание на исключения. Как ни странно, искать их надо по SEVERE, а не ERROR
Возможно из исключения будет сразу понятна причина.

2) Плагины. Часто в исключении есть какие-то классы, но за что они отвечают - не ясно. Но если перед исключением есть строчка
Plugin to blame: xxx,
то предполагаемый виновник найден.
Очень часто это новый плагин или его новая версия.
Его надо отключить. Но IDE не стартует, и настройки в UI недоступны.
Не беда - внешние плагины можно отключить удалив соответствующую папку из %USERDATA%\AppData\Roaming\xxx\yyy\plugins.
Бывают сбои и во встроенных (bundled) плагинах, поэтому есть второй способ: добавить id плагина в файл %USERDATA%\AppData\Roaming\xxx\yyy\disabled_plugins.txt
Важно - id, а не имя. id это по сути grouId артифакта, найти его можно в логе.

3) Текущий проект. У меня были кейсы, когда даже с плагином с утечкой памяти, сжирающим 6, 8, 16 Гб - т.е. все что дадут - удавалось запустить IDE открыв пустой проект. Но IDEA по умолчанию открывает последние проекты. Вариант решения - переименовать папку, чтобы она их не нашла.

4) Опции запуска. https://www.jetbrains.com/help/idea/working-with-the-ide-features-from-command-line.html#arguments
Могут помочь решить проблему следующие:
а) disableNonBundledPlugins - запуск IDE без внешних плагинов
б) dontReopenProjects - более элегантный вариант открыть IDEA без последних проектов

5) Память. Если в логах есть OutOfMemoryError - можно попробовать увеличить Heap для IDEA. Почему можно попробовать - потому что это не панацея, при утечке памяти не поможет.
Второй вопрос - как увеличить? Через idea64.exe.vmoptions.
А если не хватает прав его поправить?
Есть его "профильный" (лежащий в профиле пользователя) двойник %USERDATA%\AppData\Roaming\xxx\yyy\idea64.exe.vmoptions.
Рекомендую использовать его, особенно если у вас настройки IDEA в кастомной папке, не меняющейся от версии к версии.

6) thread dump и heap dump. Больше помогут разрабам IDE, но глянуть можно.
Создаются в двух случаях.
а) При каждом зависании (freeze) создаются в %USERDATA%\AppData\Local\xxx\yyy\log\threadDumps-freeze-zzz
б) при OutOfMemory с включенной опцией -XX:+HeapDumpOnOutOfMemoryError (а в idea64.exe.vmoptions она по умолчанию есть) в %USERDATA% создается heapdump и лог, включающий threaddumps

На сегодня все, если найду еще интересные лайфхаки - сделаю новый пост.

#idea #ide #troubleshooting
👍2
Минутка философии на канале)
На тему AI конечно же)

Вопрос - не является ли массовое внедрение AI сейчас (причем со словами везде и надо еще вчера) временным хайпом?

Ответ - даже если и является, очень может быть, что является, AI из разработки уже не уйдет. Как не ушли Agile, DevOps, микросервисы.

Почему - потому что уже на текущем уровне инструмент позволяет автоматизировать рутину малой кровью. Не все модели, не любые задачи - но рабочие кейсы есть. Описание чужого кода, типовой код (маппинг), каркас приложения, базовый набор тестов, замена поиска... И их будет больше.

Вывод - изучать надо)

#ai
👍3💯2
Новая LTS Java.

Я о Java 25.
Вышла не вчера, поэтому также вышла и хорошая статья с обзором нововведений https://habr.com/ru/companies/T1Holding/articles/946778/.
Там даже табличка включения новых фич от 21 до 25 версии есть. И примеры кода - было\стало.
И от меня как всегда пару комментариев)

Для начала бросаются в глаза две фичи со "странным" названием Quantum-Resistant ...
Почему бросаются - квантовые компьютеры ожидаются в промышленном использовании в течение 10+ лет, а устойчивые к взлому квантовым компьютером алгоритмы в Java появились уже сейчас.
С - стратегия.
Б - банковское ПО)

Три фичи со словами Ahead-of-Time, главная из них - Ahead-of-Time Class Loading & Linking.
Это дальнейшее развитие темы CDS https://t.me/javaKotlinDevOps/316
Т.е. ускорение старта приложения.
Если CDS убирает этап верификации классов сохраняя в архиве уже проверенные классы,
то Ahead-of-Time Class Loading & Linking как следует из названия сохраняет всю необходимую для работы с классами информацию. Так сказать в распакованном виде, поэтому ее сразу можно грузить в Metaspace.
Одно но: набор классов у всех разный, поэтому нужно запустить приложение в тестовом режиме и собрать данные по актуальным классам, которые и будут сохранены в архиве.
Заодно сохраняется и статистика их использования (Ahead-of-Time Method Profiling), что позволяет при старте JVM сразу запустить компиляцию часто используемых методов.
Последняя фича - это offline Profile-Guided Optimization https://t.me/javaKotlinDevOps/315, которая ранее была killer feature коммерческих JVM: Azul ReadyNow и GraalVM Enterprise Native Image.
Итоговое ускорение загрузки: 42% vs 33% у CDS https://www.happycoders.eu/java/ahead-of-time-class-loading-and-linking/

Есть еще две оптимизационные фичи: Compact Object Headers и Linking Run-Time Images without JMODs.
Первая уменьшает размер любого объекта в памяти, оптимизируя заголовки. Вторая - уменьшает объем JDK, убирая оттуда JMOD файлы. JMOD появились вместе с модулями Java как развитие jar.
И классы в jmod файлах уже есть в JDK, т.е. имеем дублирование. Сейчас его убрали, размер JDK стал меньше на 25%. Важно в облаках с тысячами микросервисов.

На самом деле технология модулей в Java до сих пор не прижилась в коммерческой разработке, но команда Java не сдается)
Module Import Declarations - можно разом импортировать все классы в модуле. С одной стороны загрязняется область видимости, с другой - удобно.

Markdown Documentation Comments: Markdown - стандарт документации в ИТ в целом, получаем больше возможностей в JavaDoc. Да, JavaDoc нужны не всегда, но пригодится.

Фичи с JFR в названии - допилен профайлер: меньше влияние на исполнение кода (JFR Cooperative Sampling), больше данных (JFR Method Timing & Tracing).

Unnamed Variables & Patterns: _ (подчеркивание) обозначает для компилятора и валидаторов неиспользуемую переменную. Java пополнила длинный список языков, где это уже есть)

Scoped Values - более безопасный вариант Thread Local.

Также дошли до prod ready версии фичи из моего поста про Java 22 https://t.me/javaKotlinDevOps/278
* Launch Multi-File Source-Code Programs
* Implicitly Declared Classes and Instance Main Methods
* Stream Gatherers
* Class-File API
* Statements before super

Итого - решаются проблемы с производительностью и объемом, допиливается функционал (стримы, модули, JFR, Thread Local, GC), стандартизируются API (Class-File API), немного синтаксического сахара (_).

А String templates https://t.me/javaKotlinDevOps/246 выкинули. Слишком отличается от мэйнстрима, я про идею процессора STR."xxx", обрабатывающего строки

#java #jdk #java_new_version
Серия: "Хозяйке на заметку" про PostgreSQL.
А точнее про создание индексов.

CREATE INDEX table_idx ON books (title) 


Вроде все просто.

Но есть ряд интересных опций.
1) CONCURRENTLY
Получается так:
CREATE INDEX CONCURRENTLY table_idx ON books (title)  

Когда нужно - всегда если накат идет на активное плечо ПРОМа. Опция не блокирует изменение таблицы пользователя. Минусы: команда выполняется дольше.

2) INCLUDE
Известно, что чтение значения индекса работает быстрее, т.к. это значение хранится в индексе, не нужно ходить в таблицу. Но кроме того в индекс можно положить любые другие значения из записи таблицы. Можно, но осторожно, т.к. это в любом случае дублирование данных. Эффект надо подтверждать на НТ.
Получаем:
CREATE INDEX table_idx ON books (title) INCLUDE (isbn)


3) WHERE
Позволяет поместить в индекс не все значения столбца, имеющиеся в таблице.
Решает следующие кейсы:
а) низкая селективность индекса: выбирает только значения с высокой селективностью
б) экономия места в памяти (и возможно в каких-то случаях на диске): выбираем в индекс только необходимое
в) частичная уникальность: если нужен уникальный индекс, но он соблюдается не для всех значений.
Выглядит так:
CREATE INDEX table_idx ON books (title) WHERE country = 'Russia'


P.S. Еще есть интересная опция USING, позволяющая использовать разные типы индексов (по умолчанию в PostgreSQL используется btree), но это отдельная тема)

Детали в официальной документации https://www.postgresql.org/docs/current/sql-createindex.html

#postgresql #db #performance
4
Рубрика "Виртуальные споры с Бугаенко")))

Егор любит провокативные посты, причём всегда (почти всегда?) в таких постах есть здравая мысль, заставляющая задуматься о том, как я пишу код. И этим они хороши)

Сейчас набрел на 2 поста о переменных:
https://www.yegor256.com/2015/01/12/compound-name-is-code-smell.html
https://www.yegor256.com/2015/09/01/redundant-variables-are-evil.html

Краткое содержание первого: составные имена переменных - типа flieName - это code smell. Аргумент - если имя пришлось сделать из 2+ слов, то это значит у нам слишком большая область видимости и нарушение Single Responsibility.
Тут в целом да. Я за небольшие методы, компилятор их оптимизирует. И такой критерий простоты метода как простые имена переменных - не очевидно, не единственный, но красиво.
А вот если говорить про имена полей класса, то тут скорее нет. Да, к классам тоже применим Single Responsibility, да, в имени поля не должно повторяться имя класса. Но видится, что строгое следование данному правилу усугубит "ООП-болезнь" - бесконечное дробление классов. А так мы отходим от структуры предметной области и усложняем чтение кода из-за прыжков между файлами. Вспомним, что разработка - искусство компромиссов)

Второй пост о том, что вынесение результата выполнения строчки кода в одноразовую локальную переменную - зло. Т.к. появляется 2+ строчки вместо одной, и ухудшается читаемость.
С одной стороны да. Но если у нас есть вызов метода, он уже достаточно сложный, а расчёт значений его параметров увеличивает длину строки кода до 100, 200... символов?
Да, можно выделить часть кода в отдельные методы. Методы тоже одноразовые...
А тогда какая разница?
Поэтому я бы сказал так: автоматом выносить все расчитываемые знпченич в переменные точно не надо.
Но и обратное не верно. Иногда такой приём улучшает читаемость, и в этом случае оправдан. Снова компромиссы)))

#clean_code #dev_compromises
Снова про индексы.

Пост навеян вот этой статьей: https://vladmihalcea.com/index-selectivity/
Рекомендую прочитать, она короткая.

Многие знают про селективность индексов. На собесах в ответ на вопрос: на какие поля нужно делать индексы - я часто слышу про низкую селективность.
Суть в том, что если в колонке значения сильно не уникальные - хорошие примеры это boolean и enum - то индекс делать не надо.

Вообще говоря да, это так. Ответ верный.

Но есть нюанс. Полный ответ такой. Проблема такого индекса не в том, что БД будет неэффективно работать. БД после первого же запроса с использованием индекса поймет, что его стоимость выше, чем у full scan. И переключит все оставшиеся запросы на full scan.
А вот в чем мы точно проиграем - индекс съест место на диске и замедлит вставку. И что более важно: введет в заблуждение разработчика, т.к. создавая индекс он очевидно хочет решить с помощью него какую-то проблему (потенциальную?) с производительностью. А индекс по факту просто не работает.

P.S. И да, если по каким-то значениям в столбце селективность хорошая - поможет частичный индекс (WHERE в индексе).

P.P.S Сломать БД сложнее, чем кажется) Но все в ваших руках) И быстрее это можно сделать отсутствием нужно индекса, чем лишним индексом.

#db #performance
Регулярная рубрика - новости AI.

Нашел очередную статью про RAG https://habr.com/ru/companies/raft/articles/954158/
Казалось бы, нашел и нашел.
Но что интересно.
Вот есть AI. AI агент для примера.
Он общается с LLM.
LLM бывают разные.
Можно прикрутить AI proxy для совместимости по API.
А еще есть разные фреймворки для построения агентов.
Агент вызывает какие-то тулы.
Или MCP сервера по некому стандартизированному протоколу.
Или другого агента, для этого тоже есть стандартизированные протоколы.
Как источник домен-специфичных данных агент использует RAG.
RAG - это векторное хранилище, они бывают разные. Как специализированные, так и в виде расширения того же PotgreSQL.
Плюс есть технология преобразования данных (текстовых как правило, но не только) в векторный формат (embedding). Это тоже разные специализированные модели (но не те модели, что обрабатывают запрос пользователя).
Плюс есть технология разбиения данных на части (chunks), т.к. поиск по RAG - это сравнение векторов. Поэтому размер вектора для сравнения должен быть конечен, а документы в теории могут весить многие мегабайты.
Так вот. Даже для разбиения докумнетов на chunks уже появилось несколько конкурирующих библиотек, про это статья в начале поста.

О чем это говорит? Растет экосистема. Т.е. технология LLM потихоньку приходит к своей зрелости.

P.S. И поспорю сам с собой: за этот год по словам компетентных людей (я только учусь) сменилось три (!) принципа построения AI агентов.

#ai #rag
LLM как серебряная пуля?

Конечно же нет.
А если серьезно - что не умеет LLM?

1) выдавать актуальную информацию. Фиксится подключением веб-поиска

2) выдавать 100% точные ответы. LLM вероятностна по своей природе, поэтому даже самая мощная модель с огромным контекстным окном с выверенным промтом может галлюцинировать

3) отвечать на доменноспецифичные вопросы. Фиксится RAG, куда закачивается доменная специфика

4) выполнять сложные вычисления. Опять же LLM - статистический вычислитель, а не математический. Решается вынесением вычислений в тулы

5) работать с большими объёмами информации. Над этим - контекстное окно - работают все разработчики LLM, окно уже превышает 1 млн токенов, но в любом случае оно конечно. А ещё токены стоят денег. Альтернативныое решение - помещение больших документов в RAG, чтобы не гонять их туда сюда, или разбиение на части (chunks) перед взаимодействием с LLM.

6) формировать JSON. Ладно, если быть точным - плохо умеет. Как ни странно, уменьшение размера и упрощение шаблона ответа может ускорить его генерацию. structured output не спасает

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

8) понимать людей. Да, модель отвечает на любой вопрос. Ну почти любой, цензура как никак) Да, кому-то AI уже заменяет "кожаного" собеседника. Но в описанных случаях будет получен некий среднестатистический ответ, который должен порадовать спрашивающего. А если ваш вопрос уникальный и сложный, то промт должен быть непротиворечивым, компактным и структурированным. Так ли общаются люди? Да нет)))

9) изобретать, ставить задачи, мыслить. Опять же - LLM - это просто статистическая машина, обученная на большом объёме данных.

Так что кожаным мешками работа ещё найдётся.

P.S. и не только строителя дата-центров)

#ai
👍3
Бесконечный PostgreSQL

В SQL есть такая штука, как подзапросы.

Пример:
SELECT column1, column2
FROM table_name
WHERE column1 IN (
SELECT column1
FROM another_table
WHERE condition
);


Но конкретно в PostgreSQL у подзапросов есть целых 4 альтернативы:
1) CTE (Общее Табличное Выражение)
2) VIEW
3) MATERIALIZED VIEW
4) TEMPORARY TABLE

Все они отличаются от подзапросов тем, что позволяют переиспользование.
И практически все - кроме обычного VIEW - материализуются, т.е. сохраняют результат на диск.
Есть отличия во времени жизни. CTE живут в рамках запроса, временные таблицы - сессии, а VIEW - до удаления. Поэтому VIEW позволяют навешивать права доступа.

Детали как обычно в статье: https://habr.com/ru/articles/855694/

Также рекомендую почитать комменты к статье, там важные дополнения:
- CTE тоже могут материализоваться как и VIEW, но этим сложнее управлять. А наличие или отсутствие материализации играет роль если в выборке есть динамически вычисляемые столбцы - например, генерация uuid, текущая дата или просто random.
- про временные таблицы важно помнить, что они живут только в рамках текущей сессии (соединения). Поэтому при работе с пулом коннектов, т.е. практически всегда, ими пользоваться не стоит.

Ну и еще один важный момент - материализация = сохранение текущего состояния на диск. Данные обновляться не будут!

Меня удивило существование CTE. Хотя если поискать - они в том или ином виде существуют во всех основных СУБД. Даже в SQLite)

#rdbms #postgresql
Серия "хозяйке на заметку", а точнее разработчику библиотек на заметку.

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

Но жизнь как всегда сложнее. И что же у нас есть?

Java

1) объявить метод устаревшим с какой-то версии - @Deprecated.
Причем эту аннотацию можно не просто повесить на метод, у нее есть два поля: forRemoval и since, см. https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Deprecated.html

2) указать на что заменяем метод. @Deprecated не предоставляет такого механизма.
Но есть на свете добрые люди: https://errorprone.info/docs/inlineme
Как это может быть использовано:
а) статический анализ, собственно errorprone плагин: https://errorprone.info/docs/installation
б) миграция: https://docs.openrewrite.org/recipes/java/logging/log4j/inlinemethods

3) скрыть метод для потребителей не удаляя его из кодовой базы библиотеки. Т.е. оставляя его для тулов или для внутреннего использования. Java - снова нет( И даже добрые не помогли(

4) указать степень зрелости API. Стандартно - опять нет, но есть такая библиотечка @API Guardian https://github.com/apiguardian-team/apiguardian, позволяющая пометить API примерно так:

@API(status = STABLE, since = "1.0")
public class StableService {
@API(status = EXPERIMENTAL)
public void experimentalMethod() {
}

@API(status = DEPRECATED, since = "2.0")
public void deprecatedMethod() {
}

@API(status = INTERNAL)
public void internalMethod() {
}
}


Используется в JUnit, к слову.

Более простая альтернатива: @Beta из Guava, https://guava.dev/releases/23.4-jre/api/docs/com/google/common/annotations/Beta.html

5) потребовать у клиента явного подтверждения в коде, что он готов использовать бета-версию, а не просто warning компилятора - увы, нет.

Kotlin

1) @Deprecated
2) @Deprecated(replaceWith) https://kotlinlang.org/api/core/kotlin-stdlib/kotlin/-replace-with/
3) @Deprecated(level) https://www.baeldung.com/kotlin/deprecation
4-5) Механизм Opt-In: https://kotlinlang.org/docs/opt-in-requirements.html#opt-in-to-inherit-from-a-class-or-interface

// Library code
@RequiresOptIn(message = "This API is experimental. It could change in the future without notice.")
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
annotation class MyDateTime

@MyDateTime
// A class requiring opt-in
class DateProvider


// Client code
@OptIn(MyDateTime::class)

// Uses DateProvider
fun getDate(): Date {
val dateProvider: DateProvider
// ...
}

Все это, естественно, поддерживается в IDEA из коробки.
Плюс в Kotlin можно использовать @API Guardian

Получилась реклама Kotlin, что не удивительно, учитывая время его появления и назначение языка.

#api #java #kotlin
EXPLAIN не так прост, как кажется.

Многие - сужу по проводимым собесам - знают про EXPLAIN и план выполнения запроса.
Вопрос - насколько этот план, а точнее цифры в нем, отражают реальность? Например, время выполнения запроса.

Я бы ввел 4 уровня приближения к реальности:

1) EXPLAIN - ничего не выполняет на самом деле, вместо этого использует накопленную статистику. Следовательно, может ошибаться, если:
а) эвристики СУБД (пусть будет PostgreSQL) не подходят к запросу
б) статистика устарела.

Самый быстрый, практически мгновенный, с него лучше начинать.

Вывод:
[
{
"Plan": {
"Node Type": "Bitmap Heap Scan",
"Parallel Aware": false,
"Async Capable": false,
"Relation Name": "customer",
"Alias": "customer",
"Startup Cost": 12072.01,
"Total Cost": 59658.76,
"Plan Rows": 358140,
"Plan Width": 4,
"Recheck Cond": "(c_mktsegment = 'FURNITURE'::bpchar)",
"Plans": [
{
"Node Type": "Bitmap Index Scan",
"Parent Relationship": "Outer",
"Parallel Aware": false,
"Async Capable": false,
"Index Name": "idx_customer_mktsegment_acctbal",
"Startup Cost": 0.00,
"Total Cost": 11982.48,
"Plan Rows": 358140,
"Plan Width": 0,
"Index Cond": "(c_mktsegment = 'FURNITURE'::bpchar)"
}
]
}
}
]

Обратите внимание на строчки со Plan и Cost в названии: идет расчет по статистике.

2) EXPLAIN (ANALYZE) - выполняет запрос по настоящему, считает реальное время выполнения, число записей и использование памяти.

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

Вывод:
[
{
"Plan": {
"Node Type": "Bitmap Heap Scan",
"Parallel Aware": false,
"Async Capable": false,
"Relation Name": "customer",
"Alias": "customer",
"Actual Startup Time": 48.349,
"Actual Total Time": 184.486,
"Actual Rows": 359251,
"Actual Loops": 1,
"Recheck Cond": "(c_mktsegment = 'FURNITURE'::bpchar)",
"Rows Removed by Index Recheck": 0,
"Exact Heap Blocks": 43106,
"Lossy Heap Blocks": 0,
"Plans": [
{
"Node Type": "Bitmap Index Scan",
"Parent Relationship": "Outer",
"Parallel Aware": false,
"Async Capable": false,
"Index Name": "idx_customer_mktsegment_acctbal",
"Actual Startup Time": 39.360,
"Actual Total Time": 39.362,
"Actual Rows": 359251,
"Actual Loops": 1,
"Index Cond": "(c_mktsegment = 'FURNITURE'::bpchar)"
}
]
}
]


Появляются параметры со Actual в названии - это реальные показатели, поэтому то же время может меняться от вызова к вызову т.к. работает кэширование и меняется нагрузка на сервере.
Набор возвращаемых значений настраивается доп. параметрами.
В примере выше на самом деле использовался EXPLAIN (ANALYZE, TIMING), можно еще смотреть расход памяти, стоимость запроса в "попугаях", использование кэша, ...
3) EXPLAIN (ANALYZE, SERIALIZE, TIMING) - дополнительно появляется шаг преобразования данных на сервере, как это происходит при реальном запросе.

Дает еще более точный прогноз времени выполнения, но это все еще не реальное время, и дальше станет ясно почему.
Вывод:
[
{
"Plan": {
"Node Type": "Bitmap Heap Scan",
"Parallel Aware": false,
"Async Capable": false,
"Relation Name": "customer",
"Alias": "customer",
"Actual Rows": 359251,
"Actual Loops": 1,
"Recheck Cond": "(c_mktsegment = 'FURNITURE'::bpchar)",
"Rows Removed by Index Recheck": 0,
"Exact Heap Blocks": 43106,
"Lossy Heap Blocks": 0,
"Plans": [
{
"Node Type": "Bitmap Index Scan",
"Parent Relationship": "Outer",
"Parallel Aware": false,
"Async Capable": false,
"Index Name": "idx_customer_mktsegment_acctbal",
"Actual Rows": 359251,
"Actual Loops": 1,
"Index Cond": "(c_mktsegment = 'FURNITURE'::bpchar)"
}
]
},
"Serialization": {
"Time": 35.041,
"Output Volume": 4345,
"Format": "text"
}
}
]

Появился отдельный блок Serialization.

4) собственно выполнение оригинального запроса с клиента. Дополнительно включает в себя сетевую задержку между клиентом и сервером и преобразования на клиенте.
Перед выполнением лучше запустить count(*) для оценки масштаба бедствия)

#rdbmc #postgresql #troubleshooting
ООП и БД

Какая связь между двумя данными понятиями? Ответ вроде бы очевиден - ORM, JPA, Hibernate и все такое.
Либо если пойти другим путем - MyBatis или JOOQ.

Но на самом деле элементы ООП есть в БД. Я про наследование таблиц https://postgrespro.ru/docs/postgresql/current/ddl-inherit

Надо ли оно?
Прям моделировать объекты в БД в качестве базовой практики - нет, не понимаю какие у этого плюсы. А сложность добавляется.
Но можно посмотреть на наследование с другой стороны.

CREATE TABLE localities (
name text,
population float
);

CREATE TABLE cities (
) INHERITS (cities);

CREATE TABLE villages (
) INHERITS (cities);


Что получилось в такой конфигурации? Это аналог VIEW, собирающей данные из двух таблиц - cities и villages.
Если посмотреть с другой стороны - аналог таблицы localities с двумя партициями: cities и villages.

Из плюсов такой конфигурации: возможность менять структуру таблиц в наследниках. Добавлять поля или индексы.

Из минусов по сравнению с партициями:
а) работу с партициями PostgreSQL оптимизирует лучше
б) партиции лучше подходят для разделения по "бесконечным" данным, самый понятный пример - время.

Из минусов по сравнению с VIEW - VIEW легче за счет своей виртуальности (не MATERIALIZED).

Важная особенность использования наследования в таком режиме - в большинстве случаев вставку в родителя нужно запрещать, т.к. логически это будет выглядеть странно.
Населенный пункт является городом и деревней в нашем случае.
Как это сделать?
ALTER TABLE localities ADD CONSTRAINT localities_insert_lock CHECK (false) NO INHERIT;



В общем - использовать соблюдая меры предосторожности!) Т.е. четко понимая зачем.

#db #postgresql
1
PostgreSQL и кэш

Речь не про кэширование JPA, а про собственное кэширование БД.

1) на сервере есть 2 уровня кэширования: PostgreSQL buffer cache и Linux page cache. В отличие от Kafka, где он один.
А если использовать кэширование на уровне JPA, в итоге получается 4 уровня)

2) по умолчанию кэша выделяется мало, для PROD like его размер нужно увеличить.
Посмотреть сколько:
SHOW shared_buffers


Изменить:
ALTER SYSTEM SET shared_buffers TO <your_value>; -- при наличии прав


или через /etc/postgresql/../postgresql.conf
c рестартом сервера.

Универсальных рекомендаций по размеру быть не может, но можно начать с 1/4 ОЗУ.

3) размером Linux page cache напрямую управлять нельзя. Но его максимальный размер можно ограничить косвенно, через
sysctl vm.swappiness

Да, это процент использования swap файла, но как побочный эффект он ограничивает использование ОЗУ под page cache, и оставшееся пространство можно выделить PostgreSQL.

Зная эти два параметра можно рассчитать максимальное значение для кэша PostgreSQL:

shared_buffers ~= ОЗУ * vm.swappiness - резерв ОС - резерв PostgreSQL


4) со значением shared_buffers нужно быть осторожным, также как и с настройкой heap space в JVM. PostgreSQL ожидает, что вся эта память будет ему доступна.
Если ОЗУ закончится, а процесс будет все равно пытаться запрашивать память, придет злой OOM Killer и убьет его).

5) есть расширение pg_buffercache, предоставляющее одноименное view с информацией о страницах кэша PostgreSQL: к какой таблице относится, как используется.
Перед его использованием нужно само собой установить:
CREATE EXTENSION pg_buffercache;

перегрузить сервер и дать права:
GRANT pg_monitor TO xxx;


Использование:
SELECT count(*)
FROM pg_buffercache
WHERE relfilenode = pg_relation_filenode('table'::regclass);


6) при старте PostgreSQL все кэши пустые.
Интересно, что "SELECT *" не заполняет кэш на 100%, т.к. используется более хитрый механизм, когда для каждой выполняемой операции выделяется часть кэша.
А вот изменение данных, которое тоже идет через буфер, точно кэширует все изменяемые данные.

7) изменение данных - плохой способ заполнить кэш.
А часто прогреть кэш нужно, как минимум для справочных данных.
Да вообще говоря и для любых данных, т.к. если удастся поместить всю БД в кэш, то это ускорит работу с БД на пару порядков (ОЗУ vs диски).
Ну и если быть точным - ускорит серверную часть процесса, см. https://t.me/javaKotlinDevOps/487

И ... для прогрева кэша тоже есть расширение:

CREATE EXTENSION pg_prewarm;
ALTER SYSTEM SET shared_preload_libraries = 'pg_prewarm';

и рестарт.

Использование:
SELECT pg_prewarm('table');


8) PostgreSQL сам чистит кэш в фоновом режиме, а явно сбросить кэш можно только перезагрузкой сервера. Что-то мне это напоминает из мира Java)

#cache #postgresql #performance
Уровни изоляции БД.

Вопрос с собесов - расскажите про уровни изоляции БД. Их как известно четыре:
1) READ UNCOMMITED
2) READ COMMITTED
3) REPEATABLE READ
4) SERIALIZABLE
Вроде известные факты, ничего интересного.

Но интересно то, что это все теория, а на практике READ UNCOMMITED не реализован в большом числе СУБД.  Если брать большую четвёрку - Oracle и PostgreSQL не реализуют, MSSQL и MySQL - реализуют.

Вопрос - почему так?

А потому что сейчас он не особо нужен.
READ UNCOMMITED - это грязные чтения. Зачем видеть чужие незафиксированные данные? Да не зачем.

Окей, тогда может быть скорость - отсутствие блокировок?

Если говорить про чтение - снова мимо. Сейчас почти все СУБД реализуют механизм снимков - MVCC (Multiversion Concurrency Control), детальнее https://aristov.tech/blog/mvcc-v-postgresql/?ysclid=mi26mzeuj6767354782 - для обеспечения изоляции и транзакционности. А в этом случае при чтении выбирается нужная версия данных по определённому алгоритму без всяких блокировок.

Запись. Конкурентно записывать не дожидаясь фиксации более ранних изменений - мусор на выходе.
Единственный кейс - если клиент может гарантировать отсутствие конкуренции. А это можно сделать, если клиент один. В общем кейс редкий.

Подозреваю, READ UNCOMMITED - наследие времени, когда MVCC не было.

#DB #PostgreSQL
👍1