Зачем Redis для задач по расписанию?
На прошлой неделе писала про задачки по расписанию и аннотацию Scheduled. Сегодня расскажу интересный кейс, когда для такой задачи используется очередь Redis.
Типичная реакция джавистов: "Чего? Как? Зачем???"🤯
Сходу даже сложно придумать, как использовать очередь для отложенных задач. Тем не менее это базовый паттерн в питоне.
Как вы помните по прошлому посту, сервис на питоне работает в одном потоке ОС. Чтобы задачки выполнялись параллельно по-настоящему, запускаются дополнительные процессы. Как это организовано для отложенных задач:
✍️ Основной процесс описывает задачу, которую нужно выполнить по расписанию
✍️ Отдельный сервис-планировщик следит, когда наступит указанное время
✍️ В нужный момент задача сериализуется и отправляется в очередь Redis
✍️ Сервис-исполнитель забирает задачу из очереди и выполняет
✍️ При необходимости результат отправляется обратно в Redis, и основной сервис его забирает
Основной сервис и сервис-исполнитель - это разные процессы, у них нет разделяемой памяти. Redis нужен, чтобы передать задачу из одного процесса в другой. Ну и как бонус — распределить задачки между исполнителями.
Очередь для такой схемы очень упрощает реализацию. Планировщик просто кидает задачу в очередь. Процесс-исполнитель ничего не выбирает, не сортирует, просто достаёт задачу из начала, и она тут же удаляется. Минимум усилий с обеих сторон.
Плюсы и минусы питоновской реализации:
👎 Больше компонентов, больше инфраструктуры
👎 В рэдисе добавляется служебная очередь для обмена данными
👎 Проблемы конкретных библиотек влияют на инфраструктуру
Например, если задача запланирована НЕ в ближайшие полчаса, она может выполниться несколько раз. Проблема известная и лечится настройками редиса.
Но есть большой плюс:
✅ Низкая когнитивная сложность. В джаве возможны варианты, поэтому надо думать, выбирать и знать о возможных проблемах. В питоне решение одно. Неважно, сколько сервисов и задач, задачи всегда выполняются в отдельном процессе.
Зачем изучать подходы в других языках?
Как я писала в прошлом посте, модель многопоточности влияет на архитектуру и инфраструктуру. У каждого языка свои "стандартные решения". Если сервис написан на Python, надо подкручивать определенные настройки Redis. В JS модель многопоточности как в питоне, но задачи по расписанию чаще реализуют через crontab и Mongo.
Это непрозрачно, повышает связность и сложность. Но таковы реалии. Когда вы техлид или высокоранговый сеньор в большой компании, придется взаимодействовать с другими стеками и понимать, что там происходит.
И, конечно, инженерный интерес! У разных инструментов разные подходы, свои преимущества и ограничения. Разбираться, как и за счёт чего решаются задачи, оценивать трейдоффы и выбирать подходящее решение очень интересно. Такие задачки - мои самые любимые🥰
На прошлой неделе писала про задачки по расписанию и аннотацию Scheduled. Сегодня расскажу интересный кейс, когда для такой задачи используется очередь Redis.
Типичная реакция джавистов: "Чего? Как? Зачем???"🤯
Сходу даже сложно придумать, как использовать очередь для отложенных задач. Тем не менее это базовый паттерн в питоне.
Как вы помните по прошлому посту, сервис на питоне работает в одном потоке ОС. Чтобы задачки выполнялись параллельно по-настоящему, запускаются дополнительные процессы. Как это организовано для отложенных задач:
✍️ Основной процесс описывает задачу, которую нужно выполнить по расписанию
✍️ Отдельный сервис-планировщик следит, когда наступит указанное время
✍️ В нужный момент задача сериализуется и отправляется в очередь Redis
✍️ Сервис-исполнитель забирает задачу из очереди и выполняет
✍️ При необходимости результат отправляется обратно в Redis, и основной сервис его забирает
Основной сервис и сервис-исполнитель - это разные процессы, у них нет разделяемой памяти. Redis нужен, чтобы передать задачу из одного процесса в другой. Ну и как бонус — распределить задачки между исполнителями.
Очередь для такой схемы очень упрощает реализацию. Планировщик просто кидает задачу в очередь. Процесс-исполнитель ничего не выбирает, не сортирует, просто достаёт задачу из начала, и она тут же удаляется. Минимум усилий с обеих сторон.
Плюсы и минусы питоновской реализации:
👎 Больше компонентов, больше инфраструктуры
👎 В рэдисе добавляется служебная очередь для обмена данными
👎 Проблемы конкретных библиотек влияют на инфраструктуру
Например, если задача запланирована НЕ в ближайшие полчаса, она может выполниться несколько раз. Проблема известная и лечится настройками редиса.
Но есть большой плюс:
✅ Низкая когнитивная сложность. В джаве возможны варианты, поэтому надо думать, выбирать и знать о возможных проблемах. В питоне решение одно. Неважно, сколько сервисов и задач, задачи всегда выполняются в отдельном процессе.
Зачем изучать подходы в других языках?
Как я писала в прошлом посте, модель многопоточности влияет на архитектуру и инфраструктуру. У каждого языка свои "стандартные решения". Если сервис написан на Python, надо подкручивать определенные настройки Redis. В JS модель многопоточности как в питоне, но задачи по расписанию чаще реализуют через crontab и Mongo.
Это непрозрачно, повышает связность и сложность. Но таковы реалии. Когда вы техлид или высокоранговый сеньор в большой компании, придется взаимодействовать с другими стеками и понимать, что там происходит.
И, конечно, инженерный интерес! У разных инструментов разные подходы, свои преимущества и ограничения. Разбираться, как и за счёт чего решаются задачи, оценивать трейдоффы и выбирать подходящее решение очень интересно. Такие задачки - мои самые любимые🥰
🔥94👍38❤15👎10
Мы создаём индекс по столбцу f: CREATE INDEX idx ON t(f). Какие действия доступны в таблице во время построения индекса?
Anonymous Poll
13%
Добавить новую строку
24%
Изменять все поля, кроме f
14%
Изменять поле f у строк, которые уже проиндексированы
15%
Читать все значения кроме поля f
73%
Читать любые строки и значения
🔥15👍5
Как создать неблокирующий индекс в Postgres?
Сегодня расскажу про опцию CONCURRENTLY при построении индекса.
Идея проста. Обычный CREATE INDEX блокирует изменения в таблице, возможно только чтение данных. Пока индекс строится, запросы на запись подвисают. Блокировка может длиться от нескольких минут до часа. Вряд ли пользователям это понравится.
Опция CONCURRENTLY решает эту проблему и не блокирует обновления, с таблицей можно продолжать работать. Выглядит опция так:
Если всё так круто, почему опция не выбрана по умолчанию?
Потому что появляются новые проблемы.
Обычный индекс один раз проходит по таблице, которая не меняется, поэтому успех неизбежен.
Если индекс строится во время активной работы с базой — привет гонки, параллельные транзакции и вся классика многопоточных проблем. В итоге
▫️ Индекс строится гораздо дольше. Скан таблицы происходит 2 раза, индекс постоянно ждёт завершения транзакций и борется с внутренними противоречиями
▫️ Индекс может не получиться и остаться в статусе invalid. В таких случаях надо снести неполучившийся индекс и начать построение заново. Возможно не один раз💔
Второй важный момент, который касается неблокирующих индексов — партиционированные таблицы.
Если добавить обычный индекс для "основной" таблицы, для текущих и будущих(!) партиций индекс создаётся автоматически. Очень удобно
Но для CONCURRENTLY индекса такая схема не работает. Чтобы создать неблокирующие индексы для партиций придётся делать так:
▫️ Создать индекс на "основную" таблицу
▫️ Создать неблокирующий индекс для каждой партиции
▫️ Присоединить эти индексы к "основному"
Примерно так:
В общем, при создании индекса для большой таблицы придётся делать выбор:
🪑 CREATE INDEX и заблокировать работу с таблицей на десятки минут
🪑 CREATE INDEX CONCURRENTLY и выполнить кучу дополнительной работы, но меньше затронуть пользователя
Универсального решения нет, выбираем стул в зависимости от ситуации:)
Сегодня расскажу про опцию CONCURRENTLY при построении индекса.
Идея проста. Обычный CREATE INDEX блокирует изменения в таблице, возможно только чтение данных. Пока индекс строится, запросы на запись подвисают. Блокировка может длиться от нескольких минут до часа. Вряд ли пользователям это понравится.
Опция CONCURRENTLY решает эту проблему и не блокирует обновления, с таблицей можно продолжать работать. Выглядит опция так:
CREATE INDEX CONCURRENTLY idx ON t(f);
Если всё так круто, почему опция не выбрана по умолчанию?
Потому что появляются новые проблемы.
Обычный индекс один раз проходит по таблице, которая не меняется, поэтому успех неизбежен.
Если индекс строится во время активной работы с базой — привет гонки, параллельные транзакции и вся классика многопоточных проблем. В итоге
▫️ Индекс строится гораздо дольше. Скан таблицы происходит 2 раза, индекс постоянно ждёт завершения транзакций и борется с внутренними противоречиями
▫️ Индекс может не получиться и остаться в статусе invalid. В таких случаях надо снести неполучившийся индекс и начать построение заново. Возможно не один раз💔
Второй важный момент, который касается неблокирующих индексов — партиционированные таблицы.
Если добавить обычный индекс для "основной" таблицы, для текущих и будущих(!) партиций индекс создаётся автоматически. Очень удобно
Но для CONCURRENTLY индекса такая схема не работает. Чтобы создать неблокирующие индексы для партиций придётся делать так:
▫️ Создать индекс на "основную" таблицу
▫️ Создать неблокирующий индекс для каждой партиции
▫️ Присоединить эти индексы к "основному"
Примерно так:
CREATE INDEX idx ON ONLY t(f);
CREATE INDEX CONCURRENTLY p1_idx ON p1_t(f);
ALTER INDEX idx ATTACH PARTITION p1_idx;
В общем, при создании индекса для большой таблицы придётся делать выбор:
🪑 CREATE INDEX и заблокировать работу с таблицей на десятки минут
🪑 CREATE INDEX CONCURRENTLY и выполнить кучу дополнительной работы, но меньше затронуть пользователя
Универсального решения нет, выбираем стул в зависимости от ситуации:)
👍90🔥54❤20
Как подготовиться к собесам на Middle/Senior позицию и заполнить пробелы по самым горячим навыкам на рынке
Найм сейчас сложный. Даже хорошему спецу сложно попасть на собеседование. На каждую вакансию летят 500 откликов в первые секунды, потом подключаются автофильтры по неведомым критериям.
Сами собеседования стали жестче. Конкуренция такая, что недостаточно просто рассказать про свой опыт. Собеседования проходят те, кто лучше к ним подготовился.
За последние полгода я прослушала 60 собеседований на Middle и Senior позицию. Выписала вопросы, где люди чаще всего косячат. На основе этих данных сделала бота, который помогает прокачаться в сложных вопросах. Чтобы закрыть все пробелы и быть звездой на собесе🌟
@awesome_java_bot
Как это работает: каждый день бот спрашивает несколько вопросов. Основные темы: Java, БД, Спринг, Кафка, многопоточка, микросервисы.
💪 Ответили правильно → +5 к уверенности в своих навыках
💪 Что-то пошло не так → читаете пояснение к вопросу, это если побыстрее. Если хотите углубиться, читаете конспект из базы знаний и доп.материалы.
Кому точно надо:
✅ Если планируете осенний рейд по собесам. Тогда надо 100000%
✅ Если хотите подтянуть знания и заполнить пробелы по популярным на рынке технологиям
Бот платный. Есть 3 пробных дня, чтобы оценить формат и убедиться, что уровень материалов топ. Для любимых подписчиков сниженная цена до вечера пятницы, потом будет дороже
@awesome_java_bot
Формат у бота слегка непривычный, но, уверена, что вы разберетесь:)
Найм сейчас сложный. Даже хорошему спецу сложно попасть на собеседование. На каждую вакансию летят 500 откликов в первые секунды, потом подключаются автофильтры по неведомым критериям.
Сами собеседования стали жестче. Конкуренция такая, что недостаточно просто рассказать про свой опыт. Собеседования проходят те, кто лучше к ним подготовился.
За последние полгода я прослушала 60 собеседований на Middle и Senior позицию. Выписала вопросы, где люди чаще всего косячат. На основе этих данных сделала бота, который помогает прокачаться в сложных вопросах. Чтобы закрыть все пробелы и быть звездой на собесе🌟
@awesome_java_bot
Как это работает: каждый день бот спрашивает несколько вопросов. Основные темы: Java, БД, Спринг, Кафка, многопоточка, микросервисы.
💪 Ответили правильно → +5 к уверенности в своих навыках
💪 Что-то пошло не так → читаете пояснение к вопросу, это если побыстрее. Если хотите углубиться, читаете конспект из базы знаний и доп.материалы.
Кому точно надо:
✅ Если планируете осенний рейд по собесам. Тогда надо 100000%
✅ Если хотите подтянуть знания и заполнить пробелы по популярным на рынке технологиям
Бот платный. Есть 3 пробных дня, чтобы оценить формат и убедиться, что уровень материалов топ. Для любимых подписчиков сниженная цена до вечера пятницы, потом будет дороже
@awesome_java_bot
Формат у бота слегка непривычный, но, уверена, что вы разберетесь:)
🔥100👍21❤15👎14
Последний день скидки
Ну что, вот и прошло 3 дня после анонса бота. Спасибо тем, кто поддерживал и тем, кто взял подписку❤️
Хочу ответить на вопрос, который задавали в том или ином виде:
Вместо тысячи слов - возьмите пробный период и оцените сами
@awesome_java_bot
А если хочется слов, вот 4 главных плюса:
1️⃣ Вопросы
Вопросы крутятся вокруг тем и моментов, с которыми у многих случаются сложности. Это не просто список с собесов. Я дополнила их вопросами на понимание, вопросами-кейсами, формулировками с другого ракурса.
В одном отзыве на курс многопоточки ученик написал, что я использую метод Сократа. Из википедии:
Это прекрасно описывает стратегию бота. Многие вопросы не встречаются на собеседовании в явном виде, но помогают найти пробелы в знаниях или понимании темы.
Ни одна нейросеть не соберёт вам такое.
2️⃣ Активная работа
Читать списки вопросов или смотреть видео - это пассивное потребление информации. Мозг включается минимально, кажется, что всё понятно и легко. А через минуту забываешь, что вообще смотрел и читал.
Тесты - не идеальный вариант проверки знаний, но уже переводит мозг из пассивного режима в активный. Потому что надо обдумать варианты и сделать выбор.
Плюс мгновенная обратная связь. Сразу понятно, есть ли пробел с какой-то темой или всё чётко.
3️⃣ Материалы
Краткое пояснение, конспект в базе знаний, ссылки на доп.материалы. Удобно и экономит время.
4️⃣ Напоминания
Каждый день бот предлагает повторить какую-то тему. Фича простая, но помогает делать маленькие регулярные шаги, которые в перспективе дают отличный результат.
Короче, кому надо — тот оценит. В ближайшее время буду активно наполнять бот вопросами. Когда этот эпичный труд будет закончен, цена станет более справедливой.
Сейчас это копейки. Плюс до конца дня действует скидка для любимых подписчиков. Отличный шанс зафиксировать низкую цену:
@awesome_java_bot
Популярный вопрос - можно ли оплатить не российской картой? Можно🌸
Ну что, вот и прошло 3 дня после анонса бота. Спасибо тем, кто поддерживал и тем, кто взял подписку❤️
Хочу ответить на вопрос, который задавали в том или ином виде:
В интернете есть списки вопросов, на ютубе мок собесы, а ещё ИИ, чем бот лучше?
Вместо тысячи слов - возьмите пробный период и оцените сами
@awesome_java_bot
А если хочется слов, вот 4 главных плюса:
1️⃣ Вопросы
Вопросы крутятся вокруг тем и моментов, с которыми у многих случаются сложности. Это не просто список с собесов. Я дополнила их вопросами на понимание, вопросами-кейсами, формулировками с другого ракурса.
В одном отзыве на курс многопоточки ученик написал, что я использую метод Сократа. Из википедии:
Этот метод часто подразумевает дискуссию, в которой собеседник, отвечая на заданные вопросы, высказывает суждения, обнаруживая свои знания или, напротив, своё неведение.
Это прекрасно описывает стратегию бота. Многие вопросы не встречаются на собеседовании в явном виде, но помогают найти пробелы в знаниях или понимании темы.
Ни одна нейросеть не соберёт вам такое.
2️⃣ Активная работа
Читать списки вопросов или смотреть видео - это пассивное потребление информации. Мозг включается минимально, кажется, что всё понятно и легко. А через минуту забываешь, что вообще смотрел и читал.
Тесты - не идеальный вариант проверки знаний, но уже переводит мозг из пассивного режима в активный. Потому что надо обдумать варианты и сделать выбор.
Плюс мгновенная обратная связь. Сразу понятно, есть ли пробел с какой-то темой или всё чётко.
3️⃣ Материалы
Краткое пояснение, конспект в базе знаний, ссылки на доп.материалы. Удобно и экономит время.
4️⃣ Напоминания
Каждый день бот предлагает повторить какую-то тему. Фича простая, но помогает делать маленькие регулярные шаги, которые в перспективе дают отличный результат.
Короче, кому надо — тот оценит. В ближайшее время буду активно наполнять бот вопросами. Когда этот эпичный труд будет закончен, цена станет более справедливой.
Сейчас это копейки. Плюс до конца дня действует скидка для любимых подписчиков. Отличный шанс зафиксировать низкую цену:
@awesome_java_bot
Популярный вопрос - можно ли оплатить не российской картой? Можно🌸
🔥33❤10👍10
Релиз Java 25
будет завтра. А сегодня - краткий обзор фич новой версии.
Пропущу JEP в статусе Preview, хотя среди них много интересных вроде Stable Values или Structured Concurrency. Расскажу только о новшествах, которые обрели финальную форму.
🍑 JEP 519: Compact Object Headers
Самая полезная фича в новом релизе. В чем суть. У каждого объекта есть заголовок, в котором хранится служебная информация для JVM.
В Java 25 размер заголовка сократился с 12 байт до 8.
Вроде немного, но в одном бенчмарке это сэкономило аж 22% памяти и 8% CPU. В реальных приложениях результат будет скромнее, но всё равно приятно.
Включается флажком -XX:+UseCompactObjectHeaders, по умолчанию Compact Headers выключены.
Следующие 2 фичи вряд ли потребуются каждый день, но знать о них стоит.
🍑 JEP 513: Flexible Constructor Bodies
Писала о них отдельный пост, поэтому кратко - в конструкторах перед вызовом super можно добавить проверки аргументов:
🍑 JEP 506: Scoped Values
ScopedValue - новый класс, чтобы передавать служебные данные между методами без явной передачи через параметры.
Традиционно для этих целей используется ThreadLocal. Яркий пример - SecurityContext из Spring Security. Мы не тащим SecurityContext в каждый метод, но информация о текущем пользователе всегда под рукой.
Scoped Value - это ThreadLocal Premium:
👑 Доступен только в пределах заданной области
👑 Значение неизменяемое
👑 Автоматически удаляется после использования
👑 Совместим с виртуальными потоками
Остальные фичи довольно специфичны, опишу их кратко
🍑 JEP 510: Key Derivation Function API
Квантовые компьютеры активно развиваются, они очень мощные и будут щёлкать текущие алгоритмы как орешки. Этот JEP - часть работ по внедрению в джаву Post-Quantum Cryptography - новых алгоритмов безопасности.
🍑 JEP 511: Импорт модулей
Вместо
Можно написать короче:
Любая IDE управляет импортами за нас, поэтому полезность фичи под вопросом. Возможно, разработчики джавы не вспомнили названия модулей, когда подтверждали свои навыки на HH. Но это не точно.
🍑 JEP 503: Remove the 32-bit x86 Port
Не учитывать в дальнейшей разработке 32х-битные процессоры
🍑 JEP 512: Compact Source Files and Instance Main Methods
Более короткий Hello World:
🍑 JEP 520: JFR Method Timing & Tracing, JEP 515: Ahead-of-Time Method Profiling, JEP 520: JFR Method Timing & Tracing
Профайлинг стал точнее и стартует чуть быстрее.
🍑 JEP 521: Generational Shenandoah
Сборщик мусора Shenandoah теперь делит объекты на старые и новые, раньше это была экспериментальная фича.
Такой вот релиз будет завтра✨
будет завтра. А сегодня - краткий обзор фич новой версии.
Пропущу JEP в статусе Preview, хотя среди них много интересных вроде Stable Values или Structured Concurrency. Расскажу только о новшествах, которые обрели финальную форму.
🍑 JEP 519: Compact Object Headers
Самая полезная фича в новом релизе. В чем суть. У каждого объекта есть заголовок, в котором хранится служебная информация для JVM.
В Java 25 размер заголовка сократился с 12 байт до 8.
Вроде немного, но в одном бенчмарке это сэкономило аж 22% памяти и 8% CPU. В реальных приложениях результат будет скромнее, но всё равно приятно.
Включается флажком -XX:+UseCompactObjectHeaders, по умолчанию Compact Headers выключены.
Следующие 2 фичи вряд ли потребуются каждый день, но знать о них стоит.
🍑 JEP 513: Flexible Constructor Bodies
Писала о них отдельный пост, поэтому кратко - в конструкторах перед вызовом super можно добавить проверки аргументов:
class Employee extends Person {
Employee(int age) {
if (age < 18)
throw new IllegalArgumentException(...);
super(age);
}
}🍑 JEP 506: Scoped Values
ScopedValue - новый класс, чтобы передавать служебные данные между методами без явной передачи через параметры.
Традиционно для этих целей используется ThreadLocal. Яркий пример - SecurityContext из Spring Security. Мы не тащим SecurityContext в каждый метод, но информация о текущем пользователе всегда под рукой.
Scoped Value - это ThreadLocal Premium:
👑 Доступен только в пределах заданной области
👑 Значение неизменяемое
👑 Автоматически удаляется после использования
👑 Совместим с виртуальными потоками
Остальные фичи довольно специфичны, опишу их кратко
🍑 JEP 510: Key Derivation Function API
Квантовые компьютеры активно развиваются, они очень мощные и будут щёлкать текущие алгоритмы как орешки. Этот JEP - часть работ по внедрению в джаву Post-Quantum Cryptography - новых алгоритмов безопасности.
🍑 JEP 511: Импорт модулей
Вместо
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
Можно написать короче:
import module java.base;
Любая IDE управляет импортами за нас, поэтому полезность фичи под вопросом. Возможно, разработчики джавы не вспомнили названия модулей, когда подтверждали свои навыки на HH. Но это не точно.
🍑 JEP 503: Remove the 32-bit x86 Port
Не учитывать в дальнейшей разработке 32х-битные процессоры
🍑 JEP 512: Compact Source Files and Instance Main Methods
Более короткий Hello World:
void main() {
IO.println("Hello, World!");
}🍑 JEP 520: JFR Method Timing & Tracing, JEP 515: Ahead-of-Time Method Profiling, JEP 520: JFR Method Timing & Tracing
Профайлинг стал точнее и стартует чуть быстрее.
🍑 JEP 521: Generational Shenandoah
Сборщик мусора Shenandoah теперь делит объекты на старые и новые, раньше это была экспериментальная фича.
Такой вот релиз будет завтра✨
🔥119👍42❤15
Spring и паттерн Singleton
В мае я писала про групповой транс на собеседованиях при обсуждении SOLID. Люди на автомате обмениваются дежурными фразами, совершенно не вслушиваясь в суть. Сегодня расскажу ещё один пример.
Часто на собесах обсуждается паттерн Singleton примерно в таком ключе:
🤵♀️: Синглтон - это когда один экземпляр на приложение
💁: А пример привести можете, где он встречается?
🤵♀️: Ну, бины в спринге
💁: Ага, ок
Вроде логично. У бинов по умолчанию скоуп Singleton. Создаётся один экземпляр. Значит, бины по умолчанию синглтоны.
Но нет.
Суть паттерна Singleton не в том, что в приложении один экземпляр класса. А в том, что он гарантированно один, и никаким легальным способом в системе не может появиться ещё один экземпляр. Для этого проводится комплекс мер - конструктор становится приватным, доступ к объекту предоставляется только через метод. Плюс всякие приёмы для ленивой инициализации и thread-safe.
В случае спринга таких мер нет:
👯♀️ Можно создать 2 бина одного типа
👯♀️ Можно в любом месте сделать new
Scope Singleton означает лишь, что бин с таким id будет в контексте один. Но это не реализация паттерна Singleton.
———————————————
Фан факт для зумеров: слово "синглтон" активно использовалось в районе 2010, чтобы обозначить людей, которые живут одни и довольны этим. В газетах были статьи типа "Я - синглтон", а в новостях — сюжеты, что в России "расцветает синглтонство".
В мае я писала про групповой транс на собеседованиях при обсуждении SOLID. Люди на автомате обмениваются дежурными фразами, совершенно не вслушиваясь в суть. Сегодня расскажу ещё один пример.
Часто на собесах обсуждается паттерн Singleton примерно в таком ключе:
🤵♀️: Синглтон - это когда один экземпляр на приложение
💁: А пример привести можете, где он встречается?
🤵♀️: Ну, бины в спринге
💁: Ага, ок
Вроде логично. У бинов по умолчанию скоуп Singleton. Создаётся один экземпляр. Значит, бины по умолчанию синглтоны.
Но нет.
Суть паттерна Singleton не в том, что в приложении один экземпляр класса. А в том, что он гарантированно один, и никаким легальным способом в системе не может появиться ещё один экземпляр. Для этого проводится комплекс мер - конструктор становится приватным, доступ к объекту предоставляется только через метод. Плюс всякие приёмы для ленивой инициализации и thread-safe.
В случае спринга таких мер нет:
👯♀️ Можно создать 2 бина одного типа
👯♀️ Можно в любом месте сделать new
Scope Singleton означает лишь, что бин с таким id будет в контексте один. Но это не реализация паттерна Singleton.
———————————————
Фан факт для зумеров: слово "синглтон" активно использовалось в районе 2010, чтобы обозначить людей, которые живут одни и довольны этим. В газетах были статьи типа "Я - синглтон", а в новостях — сюжеты, что в России "расцветает синглтонство".
🔥74👍34👎14❤12
Какой ты паттерн сегодня?
Anonymous Poll
15%
Синглтон
21%
Цепочка обязанностей
8%
Строитель
25%
Приспособленец
5%
Посетитель
7%
Декоратор
19%
Абстрактная фабрика
❤31🔥10👍4
SemVer и как меня расстроил JUnit 6
Эта осень богата на релизы. Недавно вышла Java 25, в ноябре выйдет Spring 7 и Spring Boot 4. Другие библиотеки тоже выпускают обновления. Сегодня обратим внимание на два камбэка:
✨ JUnit 6. Пятая версия вышла 8 лет назад, в 2017 году
✨ Jackson 3. Jackson - верный помощник в перекладывании джейсонов, вторая версия вышла больше 10 лет назад
Когда выходит новая версия после большого перерыва, душа трепетно ждёт серьезных изменений. Потом смотришь release notes, а там все скучно. Что-то переименовали, что-то удалили, повысили версию джавы😒
Но если нет ничего нового, зачем повышать версию?
Ответ прост. На большинстве проектов используется семантическое версионирование (SemVer) - популярное соглашение о формате версий. Оно задаёт формат MAJOR.MINOR.PATCH, где
▫️ MAJOR — мажорная версия, меняется при несовместимых изменениях API
▫️ MINOR — минорная версия, добавляет функциональность с обратной совместимостью
▫️ PATCH — меняется при исправлении багов
Дополнительно в версии может быть номер билда, целевой стенд или пользователь, который запустил сборку. Может быть префикс/суффикс/тег, чтобы подчеркнуть особый статус билда. Например, у сборок Spring
🌸 6.2.0-RC3 - release candidate. Билд с зафиксированным набором фич для интенсивного тестирования
🌸 7.0.0-M9. М значит Milestone, большие изменения для сбора обратной связи и тестирования
Короче, мажорная версия не обязательно означает новые фичи и технологическую эволюцию. Это значит, что новая версия несовместима по апи со старой. Поменялся интерфейс, изменились имена классов - всё это примеры несовместимых изменений.
Но это теория. От ожиданий никуда не деться. Даже за пределами IT люди ждут значимых изменений от мажорных версий. Новый айфон, новая модель нейронки. Я вот расстроилась, что в JUnit 6 не завезли ничего интересного💔
Ну да ладно. Зачем нужно знание SemVer на практике?
Чтобы планировать масштаб работ для обновления. При апдейте спринга с 6.1 на 6.2 скорее всего проблем не будет. А вот переход 6.2 -> 7 может затянуться. Кто обновлял Spring Boot со второй версии на третью и менял тысячу импортов - жмите ❤️. Переход Jackson 2 -> Jackson 3 очень похож, основная работа при апдейте - это замена com.fasterxml.jackson на tools.jackson.
Если в JUnit 6 и Jackson 3 ничего особенного, зачем обновляться?
Потому что Spring 7 использует эти новые версии. Если захотите обновить спринг, придётся обновить и эти библиотеки. Так что это неизбежно🌚
Эта осень богата на релизы. Недавно вышла Java 25, в ноябре выйдет Spring 7 и Spring Boot 4. Другие библиотеки тоже выпускают обновления. Сегодня обратим внимание на два камбэка:
✨ JUnit 6. Пятая версия вышла 8 лет назад, в 2017 году
✨ Jackson 3. Jackson - верный помощник в перекладывании джейсонов, вторая версия вышла больше 10 лет назад
Когда выходит новая версия после большого перерыва, душа трепетно ждёт серьезных изменений. Потом смотришь release notes, а там все скучно. Что-то переименовали, что-то удалили, повысили версию джавы😒
Но если нет ничего нового, зачем повышать версию?
Ответ прост. На большинстве проектов используется семантическое версионирование (SemVer) - популярное соглашение о формате версий. Оно задаёт формат MAJOR.MINOR.PATCH, где
▫️ MAJOR — мажорная версия, меняется при несовместимых изменениях API
▫️ MINOR — минорная версия, добавляет функциональность с обратной совместимостью
▫️ PATCH — меняется при исправлении багов
Дополнительно в версии может быть номер билда, целевой стенд или пользователь, который запустил сборку. Может быть префикс/суффикс/тег, чтобы подчеркнуть особый статус билда. Например, у сборок Spring
🌸 6.2.0-RC3 - release candidate. Билд с зафиксированным набором фич для интенсивного тестирования
🌸 7.0.0-M9. М значит Milestone, большие изменения для сбора обратной связи и тестирования
Короче, мажорная версия не обязательно означает новые фичи и технологическую эволюцию. Это значит, что новая версия несовместима по апи со старой. Поменялся интерфейс, изменились имена классов - всё это примеры несовместимых изменений.
Но это теория. От ожиданий никуда не деться. Даже за пределами IT люди ждут значимых изменений от мажорных версий. Новый айфон, новая модель нейронки. Я вот расстроилась, что в JUnit 6 не завезли ничего интересного💔
Ну да ладно. Зачем нужно знание SemVer на практике?
Чтобы планировать масштаб работ для обновления. При апдейте спринга с 6.1 на 6.2 скорее всего проблем не будет. А вот переход 6.2 -> 7 может затянуться. Кто обновлял Spring Boot со второй версии на третью и менял тысячу импортов - жмите ❤️. Переход Jackson 2 -> Jackson 3 очень похож, основная работа при апдейте - это замена com.fasterxml.jackson на tools.jackson.
Если в JUnit 6 и Jackson 3 ничего особенного, зачем обновляться?
Потому что Spring 7 использует эти новые версии. Если захотите обновить спринг, придётся обновить и эти библиотеки. Так что это неизбежно🌚
❤169🔥36👍32👎6
Распределенный лок
Часто коллеги приходят с идеей сделать что-то через распределенный лок. В этом посте расскажу, почему эта задача сложнее, чем кажется, какие подводные камни встречаются и возможные альтернативы.
Небольшое интро.
Распределенный лок помогает сервисам "поделить" какой-то ресурс. Ресурсом может быть задача, обработка файла или какой-то сущности. Сам лок не контролирует доступ к ресурсу, это лишь способ договориться. Кто захватил лок, тот и работает с ресурсом.
В чем сложность работы с распределенным локом?
В комбинации "сложный алгоритм + общение по сети". Работа с локом — это не просто "взял-отпустил". Полный цикл выглядит так:
Создать лок -> Попытаться захватить -> Если не получилось: попробовать ещё раз или встать в очередь -> Отпустить -> Удалить
Каждый участник в любой момент может отвалиться, а запрос - задержаться. В итоге получаем мешок вопросов, которые нужно обдумать:
Многие вопросы снимаются инструментами, но не все. Race condition в распределенных системах встречается сплошь и рядом. Взять хотя бы недавний сбой Амазона, где всё началось с того, что 2 сервера одновременно накатывали апдейт и помешали друг другу.
Ещё одна проблема с локами - тестирование. В большинстве случаев разработчик проверит вручную пару кейсов с помощью Thread.sleep. Но это детский сад, конечно. Написать автоматизированные тесты для распределенных локов очень сложно.
Поэтому даже если система маленькая, всё крутится на одном сервере и сетевые проблемы сведены к минимуму, рекомендую рассмотреть альтернативы. Например
✔️ Сделать задачу идемпотентной и безопасной для многократного выполнения
✔️ Провернуть Inversion of control. Ресурсы распределяются по исполнителям, а не исполнители борются за ресурсы
Оба подхода можно протестировать, и общая логика часто упрощается. Берите на заметку, квинтэссенция многолетнего опыта:)
Но если сердце не видит преград, и сделать лок хочется, вот пара заметок:
💫 Лок можно реализовать на Postgres (SELECT … FOR UPDATE), Redis, Zookeeper, Kubernetes. Гляньте библиотеку ShedLock
💫 ID держателя лока удобно записывать в лок. ID каждого участника должен быть постоянным
💫 Вместо плясок с TTL можно положиться на связь сервиса и лока. В Zookeeper есть ephemeral nodes, которые исчезают, если связь с сервисом пропадает. Транзакция в Postgres может не сразу обнаружить разрыв соединения, но тоже в итоге откатится
Что почитать:
🔥 Cтатья Alibaba Cloud 2024 года. Обзор решений на джаве и их нюансов
🔥 Статья Клепмана (автор книги с кабанчиком) про недостатки распределенного лока в Redis . Статья старая (2016 год) и специфичная, но полезна для полноты картины
Часто коллеги приходят с идеей сделать что-то через распределенный лок. В этом посте расскажу, почему эта задача сложнее, чем кажется, какие подводные камни встречаются и возможные альтернативы.
Небольшое интро.
Распределенный лок помогает сервисам "поделить" какой-то ресурс. Ресурсом может быть задача, обработка файла или какой-то сущности. Сам лок не контролирует доступ к ресурсу, это лишь способ договориться. Кто захватил лок, тот и работает с ресурсом.
В чем сложность работы с распределенным локом?
В комбинации "сложный алгоритм + общение по сети". Работа с локом — это не просто "взял-отпустил". Полный цикл выглядит так:
Создать лок -> Попытаться захватить -> Если не получилось: попробовать ещё раз или встать в очередь -> Отпустить -> Удалить
Каждый участник в любой момент может отвалиться, а запрос - задержаться. В итоге получаем мешок вопросов, которые нужно обдумать:
Что делать, если сервис взял лок, но умер?
Что будет, если один сервис отправит 2 команды захватить лок?
Может ли сервис отпустить лок, который он не держит?
Что делать, если порядок запросов нарушится, и сначала на лок пришла команда "отпустить", а потом "взять"?
Кто будет создавать локи?
Будет ли атомарно работать связка "создать и захватить лок"?
Сколько сервис будет пытаться захватить лок? С какими интервалами?
Если сервис встаёт в очередь к локу - сколько времени ждать? можно ли выйти из очереди?
Кто и когда будет удалять локи?
Многие вопросы снимаются инструментами, но не все. Race condition в распределенных системах встречается сплошь и рядом. Взять хотя бы недавний сбой Амазона, где всё началось с того, что 2 сервера одновременно накатывали апдейт и помешали друг другу.
Ещё одна проблема с локами - тестирование. В большинстве случаев разработчик проверит вручную пару кейсов с помощью Thread.sleep. Но это детский сад, конечно. Написать автоматизированные тесты для распределенных локов очень сложно.
Поэтому даже если система маленькая, всё крутится на одном сервере и сетевые проблемы сведены к минимуму, рекомендую рассмотреть альтернативы. Например
✔️ Сделать задачу идемпотентной и безопасной для многократного выполнения
✔️ Провернуть Inversion of control. Ресурсы распределяются по исполнителям, а не исполнители борются за ресурсы
Оба подхода можно протестировать, и общая логика часто упрощается. Берите на заметку, квинтэссенция многолетнего опыта:)
Но если сердце не видит преград, и сделать лок хочется, вот пара заметок:
💫 Лок можно реализовать на Postgres (SELECT … FOR UPDATE), Redis, Zookeeper, Kubernetes. Гляньте библиотеку ShedLock
💫 ID держателя лока удобно записывать в лок. ID каждого участника должен быть постоянным
💫 Вместо плясок с TTL можно положиться на связь сервиса и лока. В Zookeeper есть ephemeral nodes, которые исчезают, если связь с сервисом пропадает. Транзакция в Postgres может не сразу обнаружить разрыв соединения, но тоже в итоге откатится
Что почитать:
🔥 Cтатья Alibaba Cloud 2024 года. Обзор решений на джаве и их нюансов
🔥 Статья Клепмана (автор книги с кабанчиком) про недостатки распределенного лока в Redis . Статья старая (2016 год) и специфичная, но полезна для полноты картины
🔥82👍32❤26👎3
Value types: основное
На прошлой неделе вышел билд с реализацией value types и основными оптимизациями. Это даже не превью версия, детали ещё поменяются, но первое впечатление составить уже можно. Вся информация есть в JEP 401.
Сегодня расскажу основные технические моменты, отдельным постом напишу мнение про концепт идентичности, на базе которого стоит весь value тип.
Интро
В java 2гендера типа сущностей — примитивы и ссылочные типы. К первой группе относятся int, long, boolean и тд. В таких переменных хранится само значение. Набор действий с примитивами ограничен, зато вычисления происходят с космической скоростью.
К ссылочным типам относятся классы, массивы, интерфейсы и тд. Они хранят указатель на участок памяти, где находится объект. Классы содержат поля и методы, работать с ними приятнее, чем с набором чисел. Но есть минус - при работе с объектами нужно постоянно прыгать по памяти.
Цель value типов - взять лучшее из двух миров: удобство классов и скорость примитивов. Добавляется ключевое слово value, которое ставится перед классом:
Поля класса становятся final, сам класс тоже final. Но главное, что в памяти список LocalDate будет лежать плоско, без лишних заголовков и прыжков по куче.
Глядите на картинку👇 Слева список объектов, справа - набор value. Работа со списками обещает быть blazingly fast🚀
Value types прекрасно вписываются в текущие тренды. Сейчас через сервисы проходит море данных, которые в большинстве своём неизменяемые. Уплощение структуры даст буст в скорости обработки. Поэтому value types так ждут.
Модификатор value уже получили 30 базовых классов:
▫️ обёртки примитивов: Integer, Long, …
▫️ Optional*
▫️ классы дат: LocalDate, LocalDateTime, …
Классы выше - база, поэтому перфоманс подрастёт просто при переходе на JDK с value типами. Такое мы любим.
Ещё немного странного/интересного:
🤔 value можно добавить абстрактному классу. Тогда наследники тоже станут value классами. В JEP написано, что наследники могут отказаться от value модификатора, но как - непонятно:)
🤔 value class vs record
Record - final класс с final полями, идеальный кандидат, чтобы стать value классом по умолчанию. Но это не так, для records нужно явно прописывать value. В самом JEP объяснение сводится к утверждению
Что, безусловно, верно. Но непонятно, почему неверно обратное. Остаётся только додумывать про обратную совместимость с уже существующими рекордс
🤔 String не стал value классом
Опять же, могу найти техническую причину. Хэш строки вычисляется лениво, а в value классе поля должны быть final. Здесь помогла бы другая фича, которая сейчас в превью — StableValue, ленивая инициализация final полей.
Но в JEP пишут, что дело в наличии идентичности (identity) у строки. Про концепт идентичности напишу отдельный пост, но если кратко: identity определяет возможность сравнения 2 объектов с одинаковыми значениями.
Почему у строк есть identity, мне непонятно. Пул строк, дедупликация и прочие оптимизации явно не уважают право String на идентичность.
Это только основное, на что точно следует обратить внимание. В тексте ещё очень много интересного, куча намёков на будущие фичи и оптимизации. Давно не читала JEP с таким интересом😊
На прошлой неделе вышел билд с реализацией value types и основными оптимизациями. Это даже не превью версия, детали ещё поменяются, но первое впечатление составить уже можно. Вся информация есть в JEP 401.
Сегодня расскажу основные технические моменты, отдельным постом напишу мнение про концепт идентичности, на базе которого стоит весь value тип.
Интро
В java 2
К ссылочным типам относятся классы, массивы, интерфейсы и тд. Они хранят указатель на участок памяти, где находится объект. Классы содержат поля и методы, работать с ними приятнее, чем с набором чисел. Но есть минус - при работе с объектами нужно постоянно прыгать по памяти.
Цель value типов - взять лучшее из двух миров: удобство классов и скорость примитивов. Добавляется ключевое слово value, которое ставится перед классом:
value class LocalDate {
int year;
int month;
int day;
// конструкторы, методы
}Поля класса становятся final, сам класс тоже final. Но главное, что в памяти список LocalDate будет лежать плоско, без лишних заголовков и прыжков по куче.
Глядите на картинку👇 Слева список объектов, справа - набор value. Работа со списками обещает быть blazingly fast🚀
Value types прекрасно вписываются в текущие тренды. Сейчас через сервисы проходит море данных, которые в большинстве своём неизменяемые. Уплощение структуры даст буст в скорости обработки. Поэтому value types так ждут.
Модификатор value уже получили 30 базовых классов:
▫️ обёртки примитивов: Integer, Long, …
▫️ Optional*
▫️ классы дат: LocalDate, LocalDateTime, …
Классы выше - база, поэтому перфоманс подрастёт просто при переходе на JDK с value типами. Такое мы любим.
Ещё немного странного/интересного:
🤔 value можно добавить абстрактному классу. Тогда наследники тоже станут value классами. В JEP написано, что наследники могут отказаться от value модификатора, но как - непонятно:)
🤔 value class vs record
Record - final класс с final полями, идеальный кандидат, чтобы стать value классом по умолчанию. Но это не так, для records нужно явно прописывать value. В самом JEP объяснение сводится к утверждению
Не каждый value класс можно сделать record
Что, безусловно, верно. Но непонятно, почему неверно обратное. Остаётся только додумывать про обратную совместимость с уже существующими рекордс
🤔 String не стал value классом
Опять же, могу найти техническую причину. Хэш строки вычисляется лениво, а в value классе поля должны быть final. Здесь помогла бы другая фича, которая сейчас в превью — StableValue, ленивая инициализация final полей.
Но в JEP пишут, что дело в наличии идентичности (identity) у строки. Про концепт идентичности напишу отдельный пост, но если кратко: identity определяет возможность сравнения 2 объектов с одинаковыми значениями.
Почему у строк есть identity, мне непонятно. Пул строк, дедупликация и прочие оптимизации явно не уважают право String на идентичность.
Это только основное, на что точно следует обратить внимание. В тексте ещё очень много интересного, куча намёков на будущие фичи и оптимизации. Давно не читала JEP с таким интересом😊
🔥110👍54❤35👎2
Integer v1 = 2000; Integer v2 = 2000; Что напечатает System.out.println(v1 == v2)?
Anonymous Poll
22%
true
78%
false
Value types: концепт идентичности
Сегодня продолжу разбирать JEP 401: value types. Начало в прошлом посте.
В JEP слово identity встречается 87 раз, практически в каждом абзаце. В этом посте разберу концепт идентичности и что меня в нём смущает.
В систему типов вводится новый термин - идентичность (identity):
▫️ У примитивов идентичности нет. 10 в одном месте ничем не отличается от 10 в другом месте
▫️ У ссылочных типов идентичность есть, каждый объект уникален. 2 объекта UserDTO - это разные объекты, даже если поля одинаковые
▫️ У value классов идентичности нет
С первого взгляда кажется, что идентичность это про == и equals. Но по мере чтения JEP растёт ощущение, что что-то не так. Например
1️⃣ Нет чёткого определения идентичности
Понятия типа identity относятся больше к проектированию модели данных. Value object, агрегаты и прочий DDD.
Java — это всё ещё язык программирования уровня циклы/классы. Примитивы и ссылочные типы описываются техническими характеристиками - стек, куча, наличие методов и тд.
Идейно Identity — что-то про возможность сравнения объектов между собой. Но что это означает с технической точки зрения в java — непонятно.
2️⃣ Сравнение value типов описано в JEP очень противоречиво:
💁🏼 С одной стороны, много раз повторили, что == сравнивает value объекты по значению полей. Как в примитивах, 123 == 123
🙋🏼 Но для некоторых value объектов equals вернет true, а == вернет false. Поэтому жирным шрифтом рекомендуют использовать equals
value класс - это всё ещё класс. Маловероятно, что кто-то будет использовать == для сравнения объектов. И объяснение какое-то есть. Но осадочек, что тема мутная, всё равно остаётся.
🌚 Конспирологическая теория, зачем на самом деле нужна identity
Внятного определения идентичности нет, значит понятие само по себе не важно. Гораздо важнее, что такое "тип без индентичности". Здесь написано чётко. Тип без идентичности
▫️ Immutable: неизменяемый
▫️ Interchangeable: взаимозаменяемый. Необязательно различать две сущности, если у них одинаковые значения
Для сущностей с этими свойствами добавляется пачка оптимизаций. Помимо уплощения кучи в списках есть, например, такая:
Подобная идея лежит в основе пулов для базовых типов. При вызове Integer.valueOf(127) не создаётся новый объект, а берётся уже существующий из пула. Для классов-обёрток это существенная экономия.
Текущая цель модификатора value и концепта identity (вернее его отсутствия) - обозначить, к каким объектам можно применить пачку оптимизаций.
Новый тип и рассуждение про доменные сущности выглядит пока натянуто. Нужно явно обозначить определения, границы и ожидаемые эффекты от value. Типы данных - база, одна из первых тем при освоении языка. Должно быть простое объяснение, чем отличаются типы, без погружения в дебри возможных оптимизаций. Должно быть конкретное определение identity, если оно важно для выбора типа.
Годы работы и преподавания не проходят бесследно. Если что-то может быть понятно неверно - оно будет понято неверно💯 Поэтому пока оцениваю новую фичу на троечку
И ответ на вопрос перед постом. Сейчас в консоли будет false, объекты сравниваются по ссылке. Возможно, после введения value types тот же код вернёт true, и популярный вопрос с собеседований получит вторую жизнь. Но может и нет💅
Сегодня продолжу разбирать JEP 401: value types. Начало в прошлом посте.
В JEP слово identity встречается 87 раз, практически в каждом абзаце. В этом посте разберу концепт идентичности и что меня в нём смущает.
В систему типов вводится новый термин - идентичность (identity):
▫️ У примитивов идентичности нет. 10 в одном месте ничем не отличается от 10 в другом месте
▫️ У ссылочных типов идентичность есть, каждый объект уникален. 2 объекта UserDTO - это разные объекты, даже если поля одинаковые
▫️ У value классов идентичности нет
С первого взгляда кажется, что идентичность это про == и equals. Но по мере чтения JEP растёт ощущение, что что-то не так. Например
1️⃣ Нет чёткого определения идентичности
Понятия типа identity относятся больше к проектированию модели данных. Value object, агрегаты и прочий DDD.
Java — это всё ещё язык программирования уровня циклы/классы. Примитивы и ссылочные типы описываются техническими характеристиками - стек, куча, наличие методов и тд.
Идейно Identity — что-то про возможность сравнения объектов между собой. Но что это означает с технической точки зрения в java — непонятно.
2️⃣ Сравнение value типов описано в JEP очень противоречиво:
💁🏼 С одной стороны, много раз повторили, что == сравнивает value объекты по значению полей. Как в примитивах, 123 == 123
🙋🏼 Но для некоторых value объектов equals вернет true, а == вернет false. Поэтому жирным шрифтом рекомендуют использовать equals
value класс - это всё ещё класс. Маловероятно, что кто-то будет использовать == для сравнения объектов. И объяснение какое-то есть. Но осадочек, что тема мутная, всё равно остаётся.
🌚 Конспирологическая теория, зачем на самом деле нужна identity
Внятного определения идентичности нет, значит понятие само по себе не важно. Гораздо важнее, что такое "тип без индентичности". Здесь написано чётко. Тип без идентичности
▫️ Immutable: неизменяемый
▫️ Interchangeable: взаимозаменяемый. Необязательно различать две сущности, если у них одинаковые значения
Для сущностей с этими свойствами добавляется пачка оптимизаций. Помимо уплощения кучи в списках есть, например, такая:
При создании value объекта через new, JVM может не создавать новый объект, а найти уже такой же существующий. == для таких объектов будет возвращать true.
Подобная идея лежит в основе пулов для базовых типов. При вызове Integer.valueOf(127) не создаётся новый объект, а берётся уже существующий из пула. Для классов-обёрток это существенная экономия.
Текущая цель модификатора value и концепта identity (вернее его отсутствия) - обозначить, к каким объектам можно применить пачку оптимизаций.
Новый тип и рассуждение про доменные сущности выглядит пока натянуто. Нужно явно обозначить определения, границы и ожидаемые эффекты от value. Типы данных - база, одна из первых тем при освоении языка. Должно быть простое объяснение, чем отличаются типы, без погружения в дебри возможных оптимизаций. Должно быть конкретное определение identity, если оно важно для выбора типа.
Годы работы и преподавания не проходят бесследно. Если что-то может быть понятно неверно - оно будет понято неверно💯 Поэтому пока оцениваю новую фичу на троечку
И ответ на вопрос перед постом. Сейчас в консоли будет false, объекты сравниваются по ссылке. Возможно, после введения value types тот же код вернёт true, и популярный вопрос с собеседований получит вторую жизнь. Но может и нет💅
👍62🔥31❤21👎4
Лучшие посты и новости
Ребята, не теряйте. У меня сейчас в жизни хайлоад, и до постов руки совсем не доходят. Но как станет полегче - продолжим исследовать чудный мир бэкенд разработки💅
Топ-5 постов этого года
▫️ База по Postgres: почему коннекшены это дорого, и что делает буфер и WAL
▫️ Почему однопоточный Redis быстрее многопоточного ConcurrentHashMap
▫️ CRaC — новая фича с большим потенциалом
▫️ Как прогреть кэши в Spring Boot
▫️ Как устроена многопоточность в разных языках
Мой фаворит - пост Почему в Set.of нельзя добавить дубликаты Маленькое расследование и теперь моя любимая байка про джаву
Новости для пользователей hard skills бота
1. Если ваша подписка попадает на январские праздники, вам добавляются две недели подписки. Хотите отдохнуть - отдыхайте, оплаченные дни никуда не пропадут.
(да, если подписаться сейчас, тоже будет 1.5 месяца по цене 1)
2. Появилась кнопка с итогами года, зацените!
Традиционное спасибо
Любимые подписчики, спасибо, что читаете. Вы все умнички! Что бы ни принёс следующий год, мы со всем справимся. Счастья нам всем в новом году💖
Ребята, не теряйте. У меня сейчас в жизни хайлоад, и до постов руки совсем не доходят. Но как станет полегче - продолжим исследовать чудный мир бэкенд разработки💅
Топ-5 постов этого года
▫️ База по Postgres: почему коннекшены это дорого, и что делает буфер и WAL
▫️ Почему однопоточный Redis быстрее многопоточного ConcurrentHashMap
▫️ CRaC — новая фича с большим потенциалом
▫️ Как прогреть кэши в Spring Boot
▫️ Как устроена многопоточность в разных языках
Мой фаворит - пост Почему в Set.of нельзя добавить дубликаты Маленькое расследование и теперь моя любимая байка про джаву
Новости для пользователей hard skills бота
1. Если ваша подписка попадает на январские праздники, вам добавляются две недели подписки. Хотите отдохнуть - отдыхайте, оплаченные дни никуда не пропадут.
(да, если подписаться сейчас, тоже будет 1.5 месяца по цене 1)
2. Появилась кнопка с итогами года, зацените!
Традиционное спасибо
Любимые подписчики, спасибо, что читаете. Вы все умнички! Что бы ни принёс следующий год, мы со всем справимся. Счастья нам всем в новом году💖
❤115🎄67🔥36
Какие у вас отношения с чистой архитектурой (Clean architecture)?
Anonymous Poll
10%
Видел на практике, работало отлично
28%
Видел на практике, но реализация была далека от "чистой"
29%
Знаю теорию, на практике не встречал
34%
Слышал, но пока не вникал
❤4👍2
Чистая архитектура: главы 1-2
Начала читать книгу Clean architecture Роберта Мартина.
Давно чувствую в этой теме какой-то пробел. Идея о непорочной бизнес-логике мне нравится, но на практике я не видела хороших примеров.
Видела сервисы, которые задумывались как "чистые", но в итоге становились проблемными. Протекающие слои, костыли, проблемы с транзакциями и атомарностью в целом. Не всегда понятно, как вписать некоторые задачи в рамку чистой архитектуры.
У меня нет больших ожиданий. Книга 2017 года, да и Мартин вряд ли плотно работал в энтерпрайзе, когда её писал. Но мне интересно почитать первоисточник и идейную составляющую.
И по первым впечатлениям - книга очень противоречивая.
Поднимается много тем - от парадигм программирования до структуры папок в проекте. Есть что обсудить👌
Но книгу читать сложно. Бесконечно много исторических справок и отступлений. Не всегда понятно, что хочет сказать автор, и как это связано с чистой архитектурой.
Поэтому решила делиться с вами процессом чтения. Это не будет подробный конспект, скорее спидран/реакция/саммари. Буду отмечать, что мне показалось интересным. Думаю, получится полезно.
Главы 1 и 2 - это введение. Большой смысловой нагрузки пока нет.
⭐️ Глава 1 ⭐️
Чем лучше архитектура, тем меньше усилий требуется на новые фичи и поддержку. Поддерживать плохую архитектуру со временем становится долго и дорого. При этом разработчики грустят, и менеджеры проваливают сроки
⭐️ Глава 2 ⭐️
Бизнес всегда требует работать над функционалом. Следить за архитектурой и состоянием системы, отстаивать необходимость рефакторинга и времени на проектирование - обязанность инженера.
Собственно, и всё.
Согласна с основной идеей второй главы, но читать крайне не рекомендую. В тексте очень много снобизма. Красной нитью идет мысль, что большинство разработчиков и менеджеров - узколобые макаки, которым плевать на архитектуру и лишь бы закрыть задачки поскорее.
В целом, Мартин кажется противоречивым персонажем, поэтому интересно, что будет дальше. Идеалистические концепты? Конкретные инструкции? 400 страниц воды? Посмотрим, обсудим
Начала читать книгу Clean architecture Роберта Мартина.
Давно чувствую в этой теме какой-то пробел. Идея о непорочной бизнес-логике мне нравится, но на практике я не видела хороших примеров.
Видела сервисы, которые задумывались как "чистые", но в итоге становились проблемными. Протекающие слои, костыли, проблемы с транзакциями и атомарностью в целом. Не всегда понятно, как вписать некоторые задачи в рамку чистой архитектуры.
У меня нет больших ожиданий. Книга 2017 года, да и Мартин вряд ли плотно работал в энтерпрайзе, когда её писал. Но мне интересно почитать первоисточник и идейную составляющую.
И по первым впечатлениям - книга очень противоречивая.
Поднимается много тем - от парадигм программирования до структуры папок в проекте. Есть что обсудить👌
Но книгу читать сложно. Бесконечно много исторических справок и отступлений. Не всегда понятно, что хочет сказать автор, и как это связано с чистой архитектурой.
Поэтому решила делиться с вами процессом чтения. Это не будет подробный конспект, скорее спидран/реакция/саммари. Буду отмечать, что мне показалось интересным. Думаю, получится полезно.
Главы 1 и 2 - это введение. Большой смысловой нагрузки пока нет.
⭐️ Глава 1 ⭐️
Чем лучше архитектура, тем меньше усилий требуется на новые фичи и поддержку. Поддерживать плохую архитектуру со временем становится долго и дорого. При этом разработчики грустят, и менеджеры проваливают сроки
⭐️ Глава 2 ⭐️
Бизнес всегда требует работать над функционалом. Следить за архитектурой и состоянием системы, отстаивать необходимость рефакторинга и времени на проектирование - обязанность инженера.
Собственно, и всё.
Согласна с основной идеей второй главы, но читать крайне не рекомендую. В тексте очень много снобизма. Красной нитью идет мысль, что большинство разработчиков и менеджеров - узколобые макаки, которым плевать на архитектуру и лишь бы закрыть задачки поскорее.
В целом, Мартин кажется противоречивым персонажем, поэтому интересно, что будет дальше. Идеалистические концепты? Конкретные инструкции? 400 страниц воды? Посмотрим, обсудим
🔥114👍47❤22🎄4👎3
Чистая архитектура. Главы 3-5
Продолжаем спидран по Clean Architecture Роберта Мартина.
⭐️ Глава 3. Парадигмы программирования ⭐️
определяют способы написания и организации кода. Их 3:
▫️ Структурная - ход программы описывается явно через условия и циклы. Используется для описания алгоритмов
▫️ Обьектно-ориентированная - программа описывается как взаимодействие объектов. Используется полиморфизм, ход работы не всегда очевиден
▫️ Функциональная - базируется на неизменяемости
⭐️ Глава 4. Структурное программирование ⭐️
Код делится на небольшие функции, которые можно протестировать. Но если тесты проходят - не факт, что программа работает корректно. Большую часть главы занимают истории, как Дейкстра боролся против goto и пытался математически доказать корректность программ.
⭐️ Глава 5. Обьектно-ориентированное (ОО) программирование ⭐️
Роберт рассуждает, что полезного и нового предлагает ООП
▫️ Инкапсуляция - не уникальна для ООП, плюс в современных языках не решает свою задачу.
Исходная цель инкапсуляции - скрыть данные и дать пользователю ограниченный набор методов.
В С (предшественник С++) определение функции и реализация разнесены по разным файлам, там инкапсуляция нормальная. В современных ОО языках инкапсуляция слабая. Нет разделения на определение и реализацию. Чтобы посмотреть, что умеет класс, надо зайти в класс, и тут открывается вся его внутрянка. Детали реализации как на ладони.
(мысль интересная, но с текущими IDE это не актуально. Выпадающие списки методов делают большой вклад в сокрытие сложности)
▫️ Наследование - всего лишь переопределение переменных/функций в ограниченном контексте. Добавочной ценности для архитектуры нет.
▫️ Полиморфизм легко и безопасно реализован в ОО языках, и в этом его главная ценность. Благодаря полиморфизму расцвел концепт инверсии зависимостей.
Дальше автор объясняет dependency inversion. Тут меня знатно побомбило.
DI - базовый кирпичик и первый шаг к чистой архитектуре. Это самая важная часть раздела. И она объяснена очень плохо:
▪️ Роберт вводит 2 типа стрелочек: source code dependency и flow of control. Не объясняя, что это такое
▪️ Рисует схему, где стрелочки идут вместе
▪️ Добавляет в схему ещё один квадрат (интерфейс) и поворачивает одну стрелку в другую сторону
На этом всё. Ни одного примера с кодом, только квадратики и стрелки. Этого недостаточно, чтобы каждый читатель понял, что нужно делать и в чём инверсия. Просто добавить интерфейс к каждому классу? Service и ServiceImpl это и есть тот самый power move?
Особенно дико такое объяснение выглядит на контрасте с менее важными темами. Рядом есть детальный разбор
✔️ разницы инкапсуляции в С и С++
✔️ как в С (язык без классов) можно имитировать наследование
✔️ как работают атомики в Clojure
Всё это разжевано гораздо подробнее, чем DI🤦
В 11 главе Роберт сделает второй заход в dependency inversion, но (спойлер) всё останется на уровне "просто добавь интерфейс". Это рекомендации по форме, но не по содержанию.
Интерфейсом может быть не только дополнительный квадратик, но и набор публичных методов класса. Чем больше этот набор скрывает детали реализации и упрощает работу с классом, тем лучше. И чтобы уменьшить связность, не обязательно множить сущности.
Продолжаем спидран по Clean Architecture Роберта Мартина.
⭐️ Глава 3. Парадигмы программирования ⭐️
определяют способы написания и организации кода. Их 3:
▫️ Структурная - ход программы описывается явно через условия и циклы. Используется для описания алгоритмов
▫️ Обьектно-ориентированная - программа описывается как взаимодействие объектов. Используется полиморфизм, ход работы не всегда очевиден
▫️ Функциональная - базируется на неизменяемости
⭐️ Глава 4. Структурное программирование ⭐️
Код делится на небольшие функции, которые можно протестировать. Но если тесты проходят - не факт, что программа работает корректно. Большую часть главы занимают истории, как Дейкстра боролся против goto и пытался математически доказать корректность программ.
⭐️ Глава 5. Обьектно-ориентированное (ОО) программирование ⭐️
Роберт рассуждает, что полезного и нового предлагает ООП
▫️ Инкапсуляция - не уникальна для ООП, плюс в современных языках не решает свою задачу.
Исходная цель инкапсуляции - скрыть данные и дать пользователю ограниченный набор методов.
В С (предшественник С++) определение функции и реализация разнесены по разным файлам, там инкапсуляция нормальная. В современных ОО языках инкапсуляция слабая. Нет разделения на определение и реализацию. Чтобы посмотреть, что умеет класс, надо зайти в класс, и тут открывается вся его внутрянка. Детали реализации как на ладони.
(мысль интересная, но с текущими IDE это не актуально. Выпадающие списки методов делают большой вклад в сокрытие сложности)
▫️ Наследование - всего лишь переопределение переменных/функций в ограниченном контексте. Добавочной ценности для архитектуры нет.
▫️ Полиморфизм легко и безопасно реализован в ОО языках, и в этом его главная ценность. Благодаря полиморфизму расцвел концепт инверсии зависимостей.
Дальше автор объясняет dependency inversion. Тут меня знатно побомбило.
DI - базовый кирпичик и первый шаг к чистой архитектуре. Это самая важная часть раздела. И она объяснена очень плохо:
▪️ Роберт вводит 2 типа стрелочек: source code dependency и flow of control. Не объясняя, что это такое
▪️ Рисует схему, где стрелочки идут вместе
▪️ Добавляет в схему ещё один квадрат (интерфейс) и поворачивает одну стрелку в другую сторону
На этом всё. Ни одного примера с кодом, только квадратики и стрелки. Этого недостаточно, чтобы каждый читатель понял, что нужно делать и в чём инверсия. Просто добавить интерфейс к каждому классу? Service и ServiceImpl это и есть тот самый power move?
Особенно дико такое объяснение выглядит на контрасте с менее важными темами. Рядом есть детальный разбор
✔️ разницы инкапсуляции в С и С++
✔️ как в С (язык без классов) можно имитировать наследование
✔️ как работают атомики в Clojure
Всё это разжевано гораздо подробнее, чем DI🤦
В 11 главе Роберт сделает второй заход в dependency inversion, но (спойлер) всё останется на уровне "просто добавь интерфейс". Это рекомендации по форме, но не по содержанию.
Интерфейсом может быть не только дополнительный квадратик, но и набор публичных методов класса. Чем больше этот набор скрывает детали реализации и упрощает работу с классом, тем лучше. И чтобы уменьшить связность, не обязательно множить сущности.
👍63❤25🔥17👎5
Тестовый контекст поднимается 2 минуты. У нас 4 класса с интеграционными тестами, их конфигурация ниже. Настройки прогона тестов стандартные. Сколько времени займет выполнение этих тестов?
@SpringBootTest
public class UserIT{...}
@ActiveProfiles("test")
@SpringBootTest
public class BidProcessingIT{...}
@SpringBootTest(properties = {
"user.rebalance.enabled=true"
})
public class RebalanceAccountIT{...}
@ActiveProfiles("test")
@SpringBootTest
public class AccountProcessingIT{...}
❤5
Сколько времени займет общий прогон тестов выше?
Anonymous Poll
16%
Чуть больше 2 минут
10%
Чуть больше 4 минут
17%
Чуть больше 6 минут
20%
Чуть больше 8 минут
15%
(количество методов с аннотацией @Test)*2
22%
👀
Как переиспользовать контекст в интеграционных тестах
Сегодня расскажу базовый минимум для написания интеграционных тестов в Spring: как работать с контекстом, чтобы тесты шли не по 2 часа.
В спринге интеграционным тестом называется тот, для которого нужен контекст. Контекст - это набор бинов, свойств, профилей и тд. Поднимать его для каждого теста долго, поэтому Spring по возможности переиспользует созданные ранее контексты.
Если в коде теста какой-то бин помечен
К новому контексту приводит переопределение свойств, указание профиля, конфигурация MockMvc и тд
Ответ на вопрос перед постом - прогон тестов будет занимать чуть больше 6 минут, потому что формируется 3 контекста. Для тестов BidProcessingIT и AccountProcessingIT используется один контекст.
🤔 Как оптимизировать выполнение тестов?
Популярный прием, который помогает зафиксировать контекст - базовый класс с тестовой конфигурацией. В базовом классе настраивается тестовый конфиг, тестконтейнеры, решается вопрос с секьюрити, наполнением и очисткой БД и тд
Остальные классы наследуются от базового:
При такой схеме важно следить, чтобы в наследниках не заменялись бины, свойства и тд. Иначе для них будет создан новый контекст.
На практике редко используется один базовый класс, чаще это иерархия под разные случаи. При грамотном проектировании и сознательности разработчиков интеграционные тесты выполняются за адекватное время даже для больших проектов👌
Сегодня расскажу базовый минимум для написания интеграционных тестов в Spring: как работать с контекстом, чтобы тесты шли не по 2 часа.
В спринге интеграционным тестом называется тот, для которого нужен контекст. Контекст - это набор бинов, свойств, профилей и тд. Поднимать его для каждого теста долго, поэтому Spring по возможности переиспользует созданные ранее контексты.
Если в коде теста какой-то бин помечен
@MockitoBean, @MockitoSpyBean или @TestBean, Spring создает для него прокси. Получается уникальный контекст, который нужно поднимать отдельно и нельзя в дальнейшем переиспользовать.К новому контексту приводит переопределение свойств, указание профиля, конфигурация MockMvc и тд
Ответ на вопрос перед постом - прогон тестов будет занимать чуть больше 6 минут, потому что формируется 3 контекста. Для тестов BidProcessingIT и AccountProcessingIT используется один контекст.
🤔 Как оптимизировать выполнение тестов?
Популярный прием, который помогает зафиксировать контекст - базовый класс с тестовой конфигурацией. В базовом классе настраивается тестовый конфиг, тестконтейнеры, решается вопрос с секьюрити, наполнением и очисткой БД и тд
@SpringBootTest
@AutoConfigureMockMvc
@TestPropertySource(…)
@TestExecutionListeners(…)
@ActiveProfile("test")
// куча других аннотаций
public abstract class BaseIntegrationTest {
// тестконтейнеры
// заглушки
}
Остальные классы наследуются от базового:
class UserIT extends BaseIntegrationTest {
@Autowired
private MockMvc mockMvc;
@Test
public void shouldCreateUser() {…}
}При такой схеме важно следить, чтобы в наследниках не заменялись бины, свойства и тд. Иначе для них будет создан новый контекст.
На практике редко используется один базовый класс, чаще это иерархия под разные случаи. При грамотном проектировании и сознательности разработчиков интеграционные тесты выполняются за адекватное время даже для больших проектов👌
❤57👍34🔥21👎2