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

Я уже писал, как логирование может влиять на производительность: https://t.me/javaKotlinDevOps/15
Соберу в одном посте несколько важных опций, касающихся быстродействия log4j. Казалось бы логгер, что там может быть сложного, но...

1) Пару слов про то, почему лучше объявлять логгер статической переменной https://logging.apache.org/log4j/2.x/manual/usage.html

2) Две оптимизации касающиеся шаблонов сообщений: строка с параметрами в виде {} вычисляется лениво если включен нужный уровень логирования. Плюс поддержка передачи параметров в виде лямбд позволяет лениво вычислить значение параметра. https://logging.apache.org/log4j/2.x/manual/api.html
Итого конструкции вида if (logger.isTraceEnabled()) становятся не нужны.

3) логирование может быть синхронным и асинхронным. Последнее очевидно быстрее, но у него есть минусы: в случае ошибок сообщения могут терятся без уведомления прикладного кода, под асинхронное логирование нужен отдельный поток. Детали https://logging.apache.org/log4j/2.x/manual/async.html Там же есть сравнение по производительности, и оно впечатляет.

4) Начиная с версии 2.6 log4j переиспользует временные объекты и т.об. уменьшает нагрузку на сборщик мусора: https://logging.apache.org/log4j/2.x/manual/garbagefree.html
По ссылке говорится об ограничениях garbage free режима и есть графики производительности.

5) Логирование в memory-mapped файлы - по сути это область в памяти, за синхронизацию которой на диск отвечает ОС. https://logging.apache.org/log4j/2.x/manual/appenders.html#MemoryMappedFileAppender

Ну и сравнение производительности с logback и разных Appenders https://logging.apache.org/log4j/2.x/performance.html

P.S. Бросается в глаза, что авторы библиотеки начиная с версии 2 заморочились с производительностью. Куча графиков, статей

#logging #log4j
Всем привет!

В разработке сейчас много хайповых понятий, те же микросервисы, Service Mesh, GitOps, Serverless. Обо всем этом как-нибудь напишу, а сегодня пару мыслей про Cloud Native.

Если подумать, то для того, чтобы сделать native приложение для облака, нужно не так уж много.

1) k8s должен как-то понимать, что приложение поднялось и может принимать траффик. Для этого облачный сервис должен реализовать probes, они же healthchecks.
https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup-probes/
Вообще их три, но в случае простейшего приложения хватит одной - liveness. Остальные две нужны если приложение долго стартует - startup или может быть недоступно в процессе работы - readyness.

2) облачное приложение не должно долго стартовать. Причина: k8s при добавлении новых нод или подов, или изменения их настроек Anti-affinity (возможности совместной работы на одной ноде) в любой момент может погасить под и поднять его на другом сервере. Да, можно указать startup probe чтобы траффик не шел на долго стартующее приложение. Можно указать maxUnavailable https://kubernetes.io/docs/tasks/run-application/configure-pdb/ для того, чтобы k8s оставлял запас подов при изменении их численности. Но все это обходные пути. Если вспомнить про Spring Boot, то я бы сказал что ему есть куда расти в плане оптимизации, не зря сейчас растет популярность его альтернатив, стартующих существенно быстрее: Quarkus, Micronaut, Helidon и использования native images.

3) cloud native приложение не должно зависеть от локального состояния - кэширования данных локально. Все критичные для работы данные должны хранится или в storage, или на клиенте. Причина все та же - k8s в любой момент может поднять под на другом сервере, локальный кэш при этом теряется.

4) для cloud native приложения крайне рекомендуется отбрасывать метрики и поддерживать distributed tracing - https://opentelemetry.io/docs/concepts/what-is-opentelemetry/. Причина: перенос в облако упрощает разработку, как правило идет рука об руку с микросервисами, следовательно, сервисов становится существенно больше, следовательно, должны быть инструменты для отслеживания их состояния и более точного понимания, где проблема.

Что на мой взгляд не относится к критичным требованиям для Cloud native приложений.

1) поддержка Docker. В Docker можно засунуть практически любое приложение, есть куча официальных образов. Даже IBM Websphere и WildFly, хотя использование их в облаке выглядит странным) Вижу проблему только с Windows native приложениями, но их остается не так уже много

2) поддержка внутри приложения cloud \ fault tolerance функций: circuit breakers, retries, timeouts, service discovery. Например, этот функционал реализуется в Spring Cloud библиотеке. Почему так? Потому что в связке k8s + service mesh все эти функции можно реализовать на уровне облака. Не хочу сказать, что Spring Cloud и его аналоги не нужны, но точно не обязательны для облака.

3) использование REST API. С одной стороны для HTTP траффика k8s + service mesh дает больше возможностей по маршрутизации, но с другой стороны tcp трафик тоже маршрутизируется.

#cloud_native #microservices #spring_boot #tracing
Всем привет!

Сегодня короткое ревью на книжку Непрерывное развитие API. https://habr.com/ru/company/piter/blog/472522/
Несмотря на многообещающее название - не понравилась.
Слишком менеджерская и "капитанская".
По технике практически ноль.
Приведу несколько идей, достаточно полезных, чтобы примерно было понятно, о чем речь:

1) рассматривайте API как продукт, есть компании, где API - единственный продукт: Twillo, Stripe
2) в первую версию API надо вкладывать больше усилий, потом можно ее непрерывно улучшать небольшими шагами, Lean, Agile, все как мы любим)
3) документация для API важна, особенно примеры использования, также для больших организаций полезен реестр API и инструменты для тестирования типа тех, что предоставляет Swagger https://editor.swagger.io/
4) тестируйте создаваемое API со стороны клиента, это поможет лучше его спроектировать
5) мониторьте боевые сервера API как на предмет ошибок, так и для сбора общей статистики по числу запросов, времени выполенния, типичным последовательностям запросов. Возможно постоянно повторяющиеся последовательности вызовов можно заменить одним.
6) с ростом организации централизовано реализовывать API невозможно, поэтому большУю часть решений нужно отдавать в команды. Централизовано имеет смысл отбирать список допустимых протоколов\технологий, и, возможно, согласование и тестирование API
7) ключевые решения по API должны примимать опытные разработчики
8) во внешнее API стоит вкладывать больше усилий, чем во внутреннее
9) автоматизация тестирования API требует больших затрат на разработку и поддержку как и любая автоматизация, но в целом полезна
10) API должно помогать достижению целей организации, можно использовать KPI и OKR для определения, достигает ли API поставленных целей. Вот на этом шаге и бросил читать)

Итого: верхнеуровнево есть правильные мысли, но с практическая ценность маленькая. Не рекомендую.

#books #api
2022-11-23_13-13-49.png
42.8 KB
Всем привет! Недавно IDEA подсказала мне интересную фичу - построение UML диаграмм для класса. Показывает родителей, интерфейсы и аннотации. IMHO полезная фича. На скрине пример из нашего CI pipeline. Комбинация клавиш Ctrl+Alt+Shift+U или ⌥ ⇧ ⌘ U Детали здесь https://www.jetbrains.com/help/idea/class-diagram.html?keymap=secondary_macos И снова советую не выключать или включить если выключена "подсказку дня": Help -> Tip of the day #IDEA
Всем привет!

Пару слов про на мой взгляд достаточно важную, но часто недооцененную штуку, как комментарии к commit-ам в git. На самом деле к любой Version Control System, но у нас же на 95+% git)))

Что должно быть:
1) комментарии обязательны
2) в теле комментария должен быть тикет, по которому ведутся работы. В начале текста комментария. В идеале - один тикет. Если правка одной строки исправляет несколько багов\закрывает несколько задач - то лучше закрыть лишние тикеты как дубликаты. Если не хочется заводить тикет на каждый чих - можно завести и использовать тикет типа "мелкие правки"
3) любой комментарий должен быть содержательным, т.е. описывать суть изменений. Лучше не привязваться к именам файлов\классов, т.к. они меняются при рефакторинге, а оперировать понятиями предметной области
4) по длине рекомендую сильно не увлекаться, ограничиваться одной или несколькими фразами. Заодно и сommit-ы будут более атомарными, проще будет revert-ть.
5) формат сommit должен быть единообразным, можно даже контролировать это на сервере. Т.к. по моему опыту без контроля сложно придерживаться единообразия, периодически проскакивают неформатные комментарии)
6) конечная цель комментариев - чтобы список commit-ов нес полезную информацию и его можно было использовать как changelist сервиса
7) следствие из предыдущего пункта - можно и нужно использовать amend для слияния нескольких commit-ов в один. Единственное исключение - если ваш git server синхронизируется, в другую подсеть например, и синхронизация ломается при amend. Опять же при односторонней синхронизации проблему решить можно.
8) следующий уровень - перед созданием Pull request\Merge request можно отрефакторить список commit-ов - переименовать, слить несколько из середины. IDEA позволяет все это сделать. Также можно хардкорно, через коммандную строку с помощью rebase - https://habr.com/ru/post/201922/ С одной стороны так мы становится на путь перфекционизма, но помнить о такой возможности надо)
9) Описание формата commit должно быть в readme.md проекта

Отдельно хочу сказать про язык комментариев. Есть мнение о необходимости английского - я с ним не согласен. По желанию команды. Все-таки не все в совершенстве владеют английским, как среди текущих разработчиков сервиса, так и среди будущих. А читаемость прежде всего!) Да, в английском нет родов у глаголов, поэтому с ним проще. В русском лучшим вариантом кажется использовать стадательный залог - "сделано то-то". Добавление рода или числа - сделал\сделали\сделала - кажется лишней информацией.

Также отдельная тема - нужны ли какие-то специальные обозначения в commit message. Например, алиас для тип изменений: фича, багфикс, рефакторинг, конфиги, тесты. Или указание модуля, где менялся код. Считаю, что не обязательно, но может быть полезно.

#git #code_review
Всем привет!

Интересная статья про динамическую перезагрузку properties https://www.baeldung.com/spring-reloading-properties в Java приложении.
Два варианта:
1) Apache Сommons Сonfiguration
2) Spring Cloud
Первое работает через отслеживание изменений в файле, второе - управляемое извне перечитывание после вызова /refresh endpoint.

#java #cloud #spring_boot
Всем привет! Возвращаясь к сравнению поиска Яндекс и Google. См. скриншоты ниже. На первом месте у обоих stackoverflow, ответы сгрупированы, причем первый в списке совпадает. На втором скажем так "авторитетный" сайт, сайты разные. На третьем у обоих официальная документация, причем одна и та же статья по содержанию, хотя и из разных разделов. Из явных различий: у Яндекса вместился четвертый ответ, а у Google виден блок "похожие запросы", которого нет у Яндекса. Ну и результатов у Google побольше, но это логично) #search
Всем привет!

Я подозреваю почти все знают и используют такой фреймворк, как Mockito. По крайней мере судя по собеседованиям, которые я провожу и коду, который вижу)
Наверняка все знают mock, spy, when(...).thenAnswer.
Но я хотел бы рассказать про ряд его малоизвестных особенностей:
1) можно создавать тестовые двойники через Mockito.mock или Mockito.spy, а можно воспользоваться аннотациями над полями класса: @Mock и @Spy, главное не забыть вызвать MockitoAnnotations.openMocks(testClass); перед каждым вызовом тестового метода
2) Mockito с версии 2 научился mock-ать final методы и классы
3) Mockito также научился mock-ать static методы. Что ж, PowerMock теперь не нужен) Да, остаются private методы, но во-первых я бы их не тестировал, а во-вторых если очень надо - есть рефлексия
4) через verify можно делать сложные проверки поведения: что методы какого-то класса не вызывались, что вызывались только определенные методы класса и ничего более, сколько раз вызывался метод, с какими аргументами. Здесь главное не переборщить и не сделать тест слишком хрупким. Разделять тестирование бизнес-процесса и детали реализации
5) с помощью ArgumentCaptor можно захватить содержимое передаваемого в mock параметра и проанализировать его отдельными assert-ами. Полезно для сложных объектов или для анализа содержимого строки
6) как известно, двойники, созданные с помощью mock, по умолчанию не возвращают ничего при вызове метода, т.к. вызова не происходит. Можно захардкодить ответ в помощью Mockito.when(...).thenReturn. Но если нужно вернуть значение в зависимости от входных параметров, а настраивать реальный объект не хочется - есть два варианта: создать наследника интерфейса Answer, где в методе answer сформировать ответ, либо сделать то же самое c использованием Streams API и Mockito.when(...).thenAnswer
Когда искал ссылки для поста, нашел отличную статью, где описываются практически все описанные выше лайфхаки: https://habr.com/ru/post/444982
Ну разве что кроме mock static https://www.baeldung.com/mockito-mock-static-methods и возврата сложного ответа из mock при помощи Answer https://www.baeldung.com/mockito-mock-methods

Пост будет дополняться полезными фичами по мере их нахождения)
Как говорить читать не перечитать https://www.javadoc.io/doc/org.mockito/mockito-core/latest/org/mockito/Mockito.html
Stay tuned)

#unittest #mockito
Всем привет!

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

Этапы развития распределенной трассировки - очень полезной технологии для отслеживания пути запроса между микросервисами, о которой я уже несколько раз писал:

1) появляются разные реализации трассировки, самые известные - Jaeger и Zipkin
2) в 2016 году создается независимый от вендора стандарт для клиентских библиотек создания трейсов и отправки данных трейсинга во внешнее хранилище - OpenTracing. В качестве хранилищ поддерживаются те же Jaeger и Zipkin. Под эгидой Google в 2017 году параллельно создается еще один, менее известный - OpenCensus
3) OpenTracing и OpenCensus объединяются в 2019 году в OpenTelemetry. https://opentelemetry.io/docs/concepts/what-is-opentelemetry/#hello-opentelemetry
Появляется также промежуточный слой collectors для маршрутизации и балансировки данных трассировки https://opentelemetry.io/docs/concepts/data-collection/
Причем под телеметрией понимаются не только трассировка, но и сбор метрик.
Естественно есть библиотека для Java.
Вроде бы конец истории...
4) Ребята из Micrometer, создавшие ранее фасад для отправки метрик в JVM приложениях, также решили "прибрать к рукам" :) и tracing, в 2022 году появляется Micrometer Tracing. Spring Boot 3, недавно вышедший, оперативно объявляет о его поддержке https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.0-Release-Notes. Судя по декларированной цели https://micrometer.io/docs/tracing#_purpose - не все еще используют OpenTelemetry, поэтому нужен новый фасад.

Что смущает. С одной стороны Micrometer Tracing вводит новый уровень абстракции: разделяет процессы создания трейса (tracer) и отправки его в хранилища (reporter) и, соответственно, поддерживает разные tracers и reporters. Это стандартный путь развития любой технологии.
С другой стороны он дублирует функционал OpenTelemetry, и новый уровень абстракции выглядит излишним.

Как говорится, будем посмотреть, что в итоге приживется. Spring Boot - конечно весомый козырь

P.S. Рекомендую ознакомится с терминологией распределенной трассировки - https://micrometer.io/docs/tracing#_glossary
И пара примеров внедрения:
https://habr.com/ru/company/ru_mts/blog/537892/
https://habr.com/ru/company/hh/blog/686414/

#tracing
Всем привет!

Надеюсь все уже перешли на JUnit 5?

У JUnit 5 много крутых фич по сравнению с 4-й версией, например:
1) более удобная работа с параметризованными тестами через @ParametrizedTest, который вешается на метод - можно миксовать параметризированные и непараметризированные тесты в одном тестовом классе
2) возможность параметризировать не только входные параметры, но и все остальное в тесте с помощью динамически генерируемых тестов DynamicTests https://www.baeldung.com/junit5-dynamic-tests
3) есть возможность проверки исключений и таймаута через методы - assertThrows и assertTimeout. Это более правильно - assert идет после Act (я про Arrange-Act-Assert) и позволяет делать несколько проверок в одном тестовом методе
4) условное выполнение тестов в зависимости от среды: @EnabledOnOs, @EnabledOnJre, @EnabledIfSystemProperty, @EnabledIfEnvironmentVariable, также можно создавать свои условия
5) @RepeatedTest для повтора теста N раз
6) @DisplayName - полезно для задания говорящего имени теста для параметризированных тестов, к примеру
7) @Nested для складывания тестовых классов в "матрешку". Полезно, если у часть тестов нужно выполнить отнаследовавшись от базового класса
8) assertAll - объединение нескольких assert-ов в тесте. Полезно, как способ отключить fail fast режим проверок в JUnit - прогнать все assert-ы в тесте несмотря на падение одного из них.
9) ну и наконец механизм extensions. В 4-й версии был механизм Runner-ов, но его ограничение состояло в том, что на класс можно было повесить один Runner. А extensions может быть сколько угодно. Вот пример реализации BDD с помощью extension https://www.infoq.com/articles/deep-dive-junit5-extensions/ Здесь же есть пример 3-го способа параметризации теста - через ParameterResolver.
10) кроме всего прочего механизм extension добавляет store - контекст для обмена данными между тестами. Важно: для модульных тестов это антипаттерн, но нормально для интеграционных https://junit.org/junit5/docs/5.0.0/api/org/junit/jupiter/api/extension/ExtensionContext.Store.html
11) указание очередности тестов через @Order - полезно для тестирования отдельных шагов в интеграционном тесте. По умолчанию, кстати, тесты запускаются в алфавитном порядке https://www.baeldung.com/junit-5-test-order

В общем JUnit 5 догнал и перегнал TestNG, рекомендую к использованию если еще не пробовали.

#junit #unittests
Всем привет!

Пару мыслей о BDD фреймворках, самым известным представителем которых является Cucumber.
Пример теста на Cucumber, чтобы было понятно о чем я:

Scenario: Eric wants to withdraw money from his bank account at an ATM
Given Eric has a valid Credit or Debit card
And his account balance is $100
When he inserts his card
And withdraws $45
Then the ATM returns $45
And his account balance is $55

Т.е по сути мы привязываем к методам в коде фразы естественного языка и т.об. можем написать тест как осмысленное предложение.

Мне не понятно, где это может быть полезно.

Основная трудность в BDD - требуется большая подготовительная работа по созданию "словаря", из которого будут конструироваться тесты. Причем если приложение сложное и развивается - работа становится постоянной. Вопрос - а нужно ли делать эту работу?

Рассмотрим возможные случаи использования.

1) разработчики для написания "системных" тестов, проверяющих сервис целиком? Не нужно, разработчику проще написать на обычном JUnit 5, максимум добавить туда Rest Assured как удобным способ в fluent стиле написать тест c четко выделенными стадиями Arrage-Act-Assert "в одну строчку". Оффтоп: почему я слово "в одну строчку" взял в кавычки думаю понятно, а вот насчет "системных" - с терминологией в классификации тестов все плохо, причем когда я начал эту тему изучать лет 6 назад - все было также))) Решение я вижу - определить терминологию в своей команде и ее придерживаться.

2) автотестеры, интеграционное тестирование, тестирование на заглушках, регресс? Как по мне если тестировщик решил перейти в касту автотестеров, то разбираться в Java или Kotlin он должен. Ну или в любом другом языке, где есть хороший тестовый фреймворк. И в DevOps немножко. Очень сложно сделать такую платформу для автотестов, где этих знаний не понадобится.

3) ручные тестировщики? Если тестировщик не хочет в автоматизацию, то я не верю, что BDD может чем-то помочь. Если не прав - хотелось бы узнать про такие кейсы. Снова оффтоп: тестировщик, который не хочет в автоматизацию, не равно плохой тестировщик. Есть роль тест-менеджера, определяющего полноту набора тест-кейсов, состав регресса, пишущего сценарии, и т.об. гарантирующий соответствие кода требованиям. Есть исследовательское тестирование, где автоматизация играет вспомогательную роль.

4) нагрузочное тестирование? Точно не про то: использовать или нет BDD - далеко не та проблема, которая важна для НТ. Профит не понятен

5) системные аналитики? Те, которых я видел "в живой природе", тесты не пишут. У них и так работы хватает: API, схема БД, сиквенс диаграммы, карта экранов, согласования, впитывание и трансформация мыслей продактов...

6) приемочные испытания. Тут развилка. Если мы говорим о приемке внутри одной организации - по моему опыту заинтересованные лица валидируют аналитику заранее, и приемка сводится к демострации и ответах на каверзные вопросы. Возможен прогон тех же автотестов, что и на стадии интеграционного тестирования. И наконец единственный кейс, где BDD может рулить - приемка на стороне организации-заказчика. Для них поставляемое ПО - черный ящик. И то, если процесс не по Agile, и заказчик в процессах команды разработки не участвует.

И еще небольшой гвоздик напоследок - сделать словарь для BDD легко, когда в нем 5-10-20 фраз. Т.е для небольшого приложения. А когда их станет не хватать? Тогда или нужная тяжелая работа по вычистке, универсализации, или словарь станет примитивным - оправь http запрос, прочитай заголовок ответа, а в этом случае BDD становится бесполезным.

#unittests #bdd #testing
Всем привет!

На каких принципах постороены современные высокопроизводительные системы?
Не претендую на полный список, но попробую собрать основные архитектурные принципы с примерами реализующих их систем.

1) shared nothing - каждый запрос на обновление пользовательских данных обрабатывается одним (!) экземпляром сервиса. Пропадает необходимость в распределенных транзакциях или использовании паттерна "Сага", и т.об. повышается скорость и надежность. Технически это горизонтальное масштабирование сервиса\балансировщиков\проксей плюс шардирование хранилища и кэша Примеры: Kafka, Kafka Streams, Spark, Terradata, Hadoop, Solr, ElasticSearch... На примере Kafka: каждый брокер получает свою долю партиций - частей на которые делятся топики - и отвечает за чтение, запись из них, а также репликацию данных. Да, всему кластеру Kafka приходится шарить метаданные о расположении партиций на брокерах - в Zookepper в текущих версиях и в специальных топиках с метаданными в последней версии. И да, ответственный за патрицию может меняться. Но за запросы к пользовательским данным в партиции в каждый момент времени отвечает один брокер, на остальные брокеры эта информация только реплицируется. Репликация проходит асинхронно, без привязки к запросу клиента. Еще примеры: https://dimosr.github.io/shared-nothing-architectures/

2) data locality - данные хранятся на той же ноде, где проходят вычисления. Нет лишних сетевых запросов - быстрее обработка данных. Примеры: Kafka Streams, Spark, Hadoop. На примере Kafka Streams - любые методы, агрегирующие и трансформирующие данные стрима, работают только с данными из тех партиций Kafka, которые лежат на локальной машине. Только так получится добиться приемлемой производительности поточной обработки данных (streaming) в распределенной системе.

3) append-only или log-based storage - данные сохраняются добавлением записи в файл, никаких обновлений и удалений на уровне записей не происходит, файлы ротируются, устаревшие файлы удаляются целиком. Где-то рядом хранится указатель на текущую запись в файле. Т.к последовательная запись на порядок быстрее случайной, то append-only сильно ускоряет запись. Примеры: снова Kafka, Hadoop, Lucene, этот же принцип лежит в основе техник write-ahead logging (WAL) в журналах упреждающей записи СУБД и CQRS + Event Sourcing. Немного о последней: https://www.baeldung.com/cqrs-event-sourcing-java . И о том, как работает WAL https://habr.com/ru/company/postgrespro/blog/459250/ И о том, как Kafka сохраняет данные: https://mbukowicz.github.io/kafka/2020/05/31/how-kafka-stores-messages.html

4) zero-copy - в общем случае данные при чтении из диска и к примеру отправке по сети копируются в памяти несколько раз из буфера в буфер. Почему? Потому что буферы у файлового драйвера, у сетевого драйвера и у Java разные. Но этого можно избежать и работать с данными из буфера ОС, если они не меняются вашим сервисом или меняются, но используются одним процессом. Естественно это ускоряет работу с данными. zero copy должен поддерживаться на уровне ОС, Linux поддерживает. Примеры использования: опять Kafka. Как это работает в Kafka https://andriymz.github.io/kafka/kafka-disk-write-performance/ Про zero copy в Java я упоминал в https://t.me/javaKotlinDevOps/17, вот тут детальнее https://shawn-xu.medium.com/its-all-about-buffers-zero-copy-mmap-and-java-nio-50f2a1bfc05c

to be continued

P.S. Во всех 4 пунктах упоминается Kafka, и это не случайность)

#arch #Kafka #performance
Всем привет!

Еще один широиспользуемый паттерн, более низкого уровня, чем описанные ранее: LMAX Disruptor.
https://lmax-exchange.github.io/disruptor/disruptor.html
Это готовая библиотека, решающая следующую задачу: есть упорядоченная очередь из каких-то данных, пишет в нее один поток, обрабатывать данные нужно в несколько потоков без блокировок. Реализована в виде кольцевого буфера и набора указателей на текущую ячейку буфера, по одному для каждого потока-читателя\писателя. В каждый момент времени в буфер пишет один поток, блокировки не ставятся, каждый поток может прочитать указатели других потоков и т.об. понять, с какими ячейками можно работать. Библиотеку достаточно хорошо пиарят, даже сам Мартин Фаулер: https://martinfowler.com/articles/lmax.html Использует log4j https://logging.apache.org/log4j/2.x/manual/async.html#UnderTheHood

Но вернемся к более общим архитектурным принципам: при реализации этой библиотеки используется принцип Mechanical Sympathy https://www.baeldung.com/lmax-disruptor-concurrency#1-mechanical-sympathy.
Суть его в следующем: хотя язык программирования и JVM в случае Java скрывают от нас кишочки компьютера - регистры процессора, кэши процессора 1,2,3 уровня, особенности работы процессора - для максимальной производительности их нужно учитывать. На примере LMAX Disruptor:
1) кольцевой буфер позволяет переиспользовать объекты в куче, уменьшая нагрузку на Garbage Collector
2) кольцевой буфер выделяется одним "куском", поэтому использует последовательные адреса в памяти, что ускоряет пакетное чтение из буфера - как за счет собственно последовательного чтения, так и зачет упреждающего кэширования процессором
3) одновременная запись в память приводит к взаимным сбросам кэша у различных ядер процессора, что плохо сказывается на производительности. В LMAX Disruptor, как я уже говорил, в каждый момент времени пишет в буфер один поток.
Все это вместе с отсутствием блокировок приводит к хорошей производительности.
Но к слову есть люди, считающие библиотеку слишком распиаренной - см. комментарии к статье https://dev.cheremin.info/2011/09/disruptor-1.html

#patterns #library
Всем привет!

Возвращаясь к Kotlin и переходу на него с Java.
Может возникнуть вопрос - как начать писать в стиле Kotlin?
Ответ - начни с официальной документации.

Ключевые отличия от Java по мнению авторов: https://kotlinlang.org/docs/comparison-to-java.html
Идиомы - часто используемые куски кода, можно сказать низкоуровневые паттерны языка: https://kotlinlang.org/docs/idioms.html
Coding conventions https://kotlinlang.org/docs/coding-conventions.html
Примеры кода от авторов языка: https://play.kotlinlang.org/byExample/01_introduction/01_Hello%20world
Migration guide с Java https://kotlinlang.org/docs/java-to-kotlin-idioms-strings.html

P.S. Надо было раньше этот пост написать)
P.P.S Документацию иногда стоит почитать)
P....S А для самых хардкорных - как известно Kotlin написан на Kotlin. https://github.com/JetBrains/kotlin

#java #kotlin
Всем привет!

Снова про Kotlin.
Возможно не все знают, что в Kotlin изобрели свои стримы - sequences https://kotlinlang.org/docs/sequences.html#sequence.
Зачем? Во-первых есть Kotlin/JS и Kotlin/Native, где нет JDK и стримов. А еще Kotlin может работать на Java 6.
А во-вторых - реализация стримов сделана под Java, что приводит к более сложному API, чем "принято" в Kotlin и проблемами с null safety из-за использования типов Java под капотом. Но у стримов есть и плюсы) Сравнение см. https://proandroiddev.com/java-streams-vs-kotlin-sequences-c9ae080abfdc

P.S. Есть некая ирония в том, что стримы, которые сильно упрощают код Java, выглядят все же более тяжеловесно по сравнению с реализацией в Kotlin)

#kotlin #java
Всем привет!

Давно хотел написать про основные модели ветвления при работе c исходным кодом.
Итак поехали!

1) великий и ужасный gitflow.
Картинка: https://habrastorage.org/r/w1560/webt/ah/aw/yf/ahawyfcuk_rids2mljkuocattzg.jpeg
Описание: https://github.com/SergeFocus/git-flow
Думаю, многие его знают, но на всякий случай напомню суть.
Основная работа ведется в фичных ветках, для багов есть специальные bugfix ветки. Все они вливаются в develop через Pull Request\Merge request. Для выпуска на ПРОД создается релизная ветка, код из которой после выхода на ПРОД попадает в master. Для выпуска hotfix также предусмотрены отдельные ветки.
Почему gitflow "великий":
а) дает контроль над тем, что и когда попадет в релиз. Поэтому любим в enterprise.
б) хорошо подходит для opensource проектов, т.к. там как правило много релизов и есть внешние коммитеры, качество кода которых нужно контролировать
в) дисциплинирует разработчиков
Почему "ужасный":
а) слишком громоздкий, т.к. master и hotfix ветки часто выглядят излишними
б) работа в фичных ветках может приводить к накоплению там большого количества кода и, следовательно, увеличивает вероятность конфликта при слиянии. Причем чем больше становится ветка, тем сложнее ее влить - и для автора из-за конфликтов, и для ревьюверов из-за объема кода. И сам принцип фичных веток на это провоцирует. Этакая положительная обратная связь, положительная в том смысле, что усиливает сложность вливания с каждым днем и каждой строчкой кода.
в) при плавной раскате на ПРОМ не понятно, в какой момент код должен попадать в master. Ведь в течение периода раскатки, а это может быть неделя +, на ПРОМ будет 2 версии. Кроме того, в этом случае о вливании в master часто забывают, т.к. все равно все работают с develop.

2) модификация gitflow с несколькими develop ветками. Может применяться в больших компаниях, когда критически важно, чтобы код той или иной фичи не попал в релиз Х без явного согласования, а feature toggle не аргумент)
Либо при переходе на новую версию платформы, когда код разных develop просто не совместим. Важная практическая особенность - git не позволит создать ветку develop/2.0 при наличии просто develop, т.к. у них одинаковый префикс. Поэтому лучше заранее определиться с моделью.
Хотя конечно всегда можно достичь требуемого с помощью создания промежуточных веток.

3) github flow. Как следует из названия придумана на GitHub-е, доступна на нем из коробки.
Картинка: https://user-images.githubusercontent.com/6351798/48032310-63842400-e114-11e8-8db0-06dc0504dcb5.png
Детали: https://docs.github.com/ru/get-started/quickstart/github-flow
По сравнению с gitflow сильно упрощена: выброшены develop, hotfix, release, bugfix, оставлены 2 типа веток: master и feature и Pull Request.
Для обозначения релизов можно использовать тэги, напомню, в git tag и branch технически одинаковы, разница лишь в UI того же Bitbucket, Github, Gitlab.
Плюсы:
а) очень простая модель
Минусы:
а) master нужно поддерживать в production ready состоянии -> feature toggles, модульные тесты и дисциплина в команде

#git #vcs #branching
4) gitlab flow. Опять же из названия понятно, кто ее придумал и где она применяется по умолчанию.
Картинка: см. детали
Детали c объяcнением чем авторов не устроили gitflow и github flow : https://docs.gitlab.com/ee/topics/gitlab_flow.html
Основные отличия от gitflow:
1) ветка для ПРОДа необязательна, но при необходимости - возможна
2) релизные ветки необязательны, только если есть внешний заказчик и\или нужны явные релизы
3) добавляется возможность environment веток - stage, pre-production, что позволяет запускать CD pipeline по появлению изменений в ветке
4) разделение bugfix\feature не требуется, хотя и не запрещается.
5) hotfix нет
Плюсы:
1) самый гибкий из вышеперечисленных flow, т.к. по сути в простейшем случае сводится к github flow, а в самом сложном похож на gitflow + environment ветки.
Минусы:
1) если использовать все возможные фичи - environment ветки, релизные ветки, production - история загрязняется кучей merge commits
2) при использовании environment веток багфикс долго идет до ПРОМа
2) не строгая модель, кого-то это может напрягать

5) trunk based. Самая на мой взгляд малоизвестная модель. Но при этом с нее все начинают. Как такое может быть?
По сути это github flow на максималках. Фичные ветки или живут 1-2 дня, или вообще их нет. Ага, push-им сразу в master. Т.е. можно жить в git, который по сути пропагандирует быстрое создание веток, с одной веткой.
Как же при этом все не сломать:
1) feature toggle
2) принцип замкового камня - разработка фичи планируется так, что конечному пользователю в UI и API она будет видна с последним сommit-ом https://martinfowler.com/bliki/KeystoneInterface.html
3) много автоматических тестов
4) опытные разработчики в команде
5) маленькая команда
6) частые push-и, чтобы снизить вероятность конфликтов
7) парное программирование, когда ревью проходится до push
8) отложенное ревью, что требует высокой инженерной культуры в команде
Картинка: https://images.prismic.io/launchdarkly/36802d7a-391b-4d0b-b712-e841437418c4_TrunkBasedDev-02-1024x576.png?ixlib=gatsbyFP&auto=compress%2Cformat&fit=max&rect=0%2C0%2C1024%2C576&w=2000&h=1125
Детали: https://habr.com/ru/post/519314/

В качестве "внеклассного чтения" могу порекомендовать достаточно подробную статью Мартина Фаулера про ветки: https://martinfowler.com/articles/branching-patterns.html
Там есть варианты ветвления, не подпадающие под модели выше и сравнение разных моделей.

#git #vcs #branching
Всем привет!

Что такое REST? Чтобы не увеличивать энтропию в сети я дам ссылку на хорошую статью про REST - https://habr.com/ru/post/590679/
Но как всегда попробую сделать из нее краткие выводы)))

Основные заблуждения касаемо REST:
1) REST - это не протокол, а парадигма при создании API. Большинство требований REST не относятся к протоколу (HTTP)
2) JSON не является обязательным для REST, просто большинство REST API используют JSON
3) нельзя сказать, что если мы не используем HTTP глаголы или продумали концепцию REST ресурсов - это не REST. Есть 4 уровня зрелости REST, https://habrastorage.org/getpro/habr/upload_files/bd8/e4b/848/bd8e4b8488e0085b7404a37dc93faf6c.png Выставляя API в виде набора HTTP POST запросов, мы находимся на 1-м уровне зрелости. Добавляем концепцию ресурсов, выраженных в HTTP path - 2-й уровень. HTTP глаголы - 3-й уровень. А 4-й - это такая интересная штука, как HATEOS. Детали: https://habr.com/ru/post/483328/ Кто-то его видел в живой природе, кстати?
4) REST не означает, что про валидацию по схеме можно забыть. Тот же OpenAPI - очень хорош, и позволяет достаточно гибко настроить схему и описать протокол взаимодействия.

Важные замечания:
1) REST по определению должен быть stateless. Как это понимать? Я скажу так: если мы привязываем клиента к конкретному серверу - это уже не stateless. Что значит привязываем? Это значит мы закэшировали что-то важное на этом конкретном сервере. И при выходе из строя этого сервера текущий процесс клиента ломается, например, ему приходится перелогинится или он потеряет введенные данные. Т.е. использование sticky session для оптимизации - не отменяет REST.
2) stateless не значит хранить все на клиенте - в куках или в кэше МП. Куки, к слову, вообще не предназначены для хранения больших объемов данных, т.к. передаются при каждом запросе и неустойчивы к взлому. Кроме того, хранение данных на клиенте чувствительно к его сбоям, не омниканально и усложняет процесс обновления клиента, т.к. приходится обновлять и его кэш. Хранить состояние нужно либо в БД, либо в расределенном кэше
3) stateless критически важно не потому, что идеолог REST написал так в своей статье) stateless дает нам легкое горизонтальное масштабирование с помощью k8s
4) не везде об этом говорится, но исходя из идеи прозрачности REST - по возможности стоит переиспользовать коды ошибок протокола HTTP. Их кстати довольно много https://ru.wikipedia.org/wiki/Список_кодов_состояния_HTTP Хотя все равно может не хватить в вашем конкретном случае, тогда можно расширить список кодов через body

#REST #API #http