25698
Все самое полезное для Java-разработчика в одном канале. Список наших каналов: https://t.me/proglibrary/9197 Обратная связь: @proglibrary_feedback_bot По рекламе: @proglib_adv Прайс: @proglib_advertising
⌛ Сохраняйте шпаргалку по спрингу
Собрали шпаргалку по Spring для тех, кто только начинает.
Карта экосистемы, главные аннотации и стартеры, типичные ошибки новичков всего на двух страницах.
══════ Навигация ══════
Вакансии • Задачи • Собесы
🐸 Библиотека джависта
#Enterprise
🔧 Hibernate Batch Processing
@BatchSize и hibernate.jdbc.batch_size — две разные вещи, которые иногда путают. Разбираем обе.
🔹 Решение
▪️ JDBC batch insert/update
spring:
jpa:
properties:
hibernate:
jdbc:
batch_size: 50 # отправлять INSERT пачками по 50
order_inserts: true # группировать INSERT по типу сущности
order_updates: true # то же для UPDATE
cache:
use_second_level_cache: false
@Transactional
public void saveAll(List<Order> orders) {
for (int i = 0; i < orders.size(); i++) {
entityManager.persist(orders.get(i));
if (i % 50 == 0) {
entityManager.flush();
entityManager.clear(); // освобождаем first-level cache
}
}
}
@Entity
public class Author {
@OneToMany(mappedBy = "author", fetch = FetchType.LAZY)
@BatchSize(size = 25)
private List<Book> books;
}
@Entity
@BatchSize(size = 25)
public class Book { ... }
spring:
jpa:
show-sql: true
datasource:
url: jdbc:postgresql://...?reWriteBatchedInserts=true # для PostgreSQL
🐸 Библиотека джависта
#DevLife
🏃♀️ Уже завтра стартует курс по разработке AI-агентов.
Про AI-агентов часто думают, что это просто модная обертка над джпт для пет-проектов. Кажется, прикрутил API к скрипту и типа готово. А вот и нет! Когда дело доходит до прода, начинаются настоящие проблемы.
Зачем глубоко копать мультиагентные системы, если можно обойтись старым добрым кодом? Как контролировать расходы на токены, чтобы новая фича не разорила бизнес? Как заставить агента работать стабильно и предсказуемо, а не галлюцинировать?
🦾 Надоело чинить «упавших» ИИ-агентов после каждого микросбоя внешних сервисов?
Анонсируем старт продаж большого курса по AgentOps. Мы собрали опыт десятков разработчиков и сделали программу, которая учит выводить ИИ в стабильный прод.
🗓 Ждем вас 28 апреля в 19:00 МСК на эфире: «Как эффективно управлять контекстным окном LLM в мультиагентных системах и не сливать бюджет на токены».
👉 Кто вещает и в чем польза?
Спикер Кирилл Кухарев (Senior AI Engineer в Raft, спикер AI Conf и Highload++). Он реализовал более 50 коммерческих проектов в GenAI и на вебинаре покажет, как взять под контроль работу нескольких агентов, чтобы они не перекидывали друг другу лишний контекст и не сжигали ваши деньги.
В прямом эфире разберем:
• Как формируется контекст в LLM при маршрутизации между агентами;
• Куда утекают лишние токены и возникает перерасход;
• Практические методы: как сжимать историю, грамотно делить задачи, лимитировать передачу контекста и собирать промпты прямо в процессе запроса пользователя.
🔥 Два способа получить максимум:
1. Приходите на вебинар 28 апреля. Дарим участникам промокод на 5.000 ₽ (работает 3 дня после эфира - это шанс забрать курс по самому низу рынка).
2. Выбирайте Инженерный трек. В подарок к нему идет полный доступ к записям и автопроверкам завершенного курса «Разработка ИИ-агентов».
👉 Занять место на вебинаре и стать профи в AgentOps
🐸 Библиотека джависта
#DevLife
🔥 Spring Boot мертв?
Наткнулся на реддите на обсуждение, что спринг бут уже не тот. Как по мне уже лет 10 пытаются похоронить спринг.
Сначала Quarkus, потом Micronaut.. «вот оно, будущее». Теперь ещё и virtual threads добавились в список «убийц».
А Spring Boot как стоял в проде у банков, ритейла и корпораций так и стоит.
💬 Пишите в комментарии своё мнение умрёт ли спринг 😁
🐸 Библиотека джависта
#DevLife
🐸 Библиотека джависта
#DevLife
🔁 Паттерн Proxy
Proxy — структурный паттерн, который подставляет вместо реального объекта его заменитель. Заменитель перехватывает вызовы к оригиналу и может добавлять логику до и после или вместо реального вызова.
Разновидности и их применение
🔹 Виртуальный прокси. Откладывает создание дорогого объекта до первого реального обращения к нему. Ленивая инициализация без изменения клиентского кода.
🔹 Защитный прокси. Проверяет права доступа перед делегированием вызова. Авторизация как отдельный слой, не смешанный с бизнес-логикой.
🔹 Удалённый прокси. Скрывает сетевое взаимодействие. Клиент вызывает метод как локальный, прокси сериализует и отправляет запрос. Основа RPC и gRPC-стабов.
🔹 Кеширующий прокси. Запоминает результат вызова и возвращает его при повторных запросах с теми же параметрами.
Преимущества
1️⃣ Прозрачность для клиента
Прокси реализует тот же интерфейс, что и оригинал. Клиентский код не меняется — он не знает, с чем работает.
2️⃣ Разделение ответственности
Безопасность, кеширование, логирование — каждое в своём прокси. Бизнес-логика оригинального объекта не засорена инфраструктурными деталями.
3️⃣ Управление ресурсами
Виртуальный прокси позволяет работать с тысячами «объектов», реально создавая только те, к которым обращались. Экономия памяти и времени инициализации.
В Spring
Spring AOP — это прокси. @Transactional, @Cacheable, @PreAuthorize — всё это прокси-обёртки, которые Spring генерирует динамически через CGLIB или JDK Dynamic Proxy. Паттерн, который вы используете каждый день, иногда даже не осознавая этого.
══════ Навигация ══════
Вакансии • Задачи • Собесы
🐸 Библиотека джависта
#CoreJava
☕️ Настройка Testcontainers + PostgreSQL
Надоели моки репозиториев, которые не ловят реальные SQL-баги?
Настраиваем Testcontainers, запускаем настоящий PostgreSQL прямо в тестах, без внешних зависимостей.
⚙️ Шаг 1 — Зависимости (Maven)
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.19.7</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>1.19.7</version>
<scope>test</scope>
</dependency>
testImplementation "org.testcontainers:postgresql:1.19.7"@SpringBootTest
@Testcontainers
class UserRepositoryTest {
@Container
static PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>("postgres:16-alpine");
@DynamicPropertySource
static void configureProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
registry.add("spring.datasource.password", postgres::getPassword);
}
@Autowired
UserRepository userRepository;
@Test
void shouldSaveAndFindUser() {
var user = new User("alice@example.com");
userRepository.save(user);
var found = userRepository.findByEmail("alice@example.com");
assertThat(found).isPresent();
}
}
@SpringBootTest
@Testcontainers
public abstract class BaseIntegrationTest {
@Container
static final PostgreSQLContainer<?> POSTGRES =
new PostgreSQLContainer<>("postgres:16-alpine")
.withReuse(true); // ← ключевая строка
@DynamicPropertySource
static void props(DynamicPropertyRegistry r) {
r.add("spring.datasource.url", POSTGRES::getJdbcUrl);
r.add("spring.datasource.username", POSTGRES::getUsername);
r.add("spring.datasource.password", POSTGRES::getPassword);
}
}
POSTGRES.followOutput(new Slf4jLogConsumer(
LoggerFactory.getLogger("postgres-container")
));
☕️ JPA 4: Hibernate переосмысливают
JPA — та самая штука, с которой вы работаете каждый день, не особо задумываясь. @OneToMany, FetchType.LAZY, сессии, dirty-checking... всё привычно и предсказуемо.
Но в JPA 4 всё меняется серьёзнее, чем кажется на первый взгляд.
Вводят EntityAgent — работа с сущностями в обход Persistence Context. Звучит как мелочь, но по факту меняет подход к целому классу задач. А ещё новый FetchType.DEFAULT, после которого глядя на код вы уже не сможете сразу ответить: EAGER или LAZY? Ответ теперь зависит от конфигурации Persistence Unit, о которой вы, скорее всего, «не думали» — Spring Boot же сам настраивает.
Статья разбирает ключевые изменения спецификации без воды, с конкретными примерами и комментариями людей, которые реально в этом варятся.
🔗 Читать подробнее
══════ Навигация ══════
Вакансии • Задачи • Собесы
🐸 Библиотека джависта
#CoreJava
🎁 Паттерн Decorator (Декоратор)
Decorator — структурный паттерн, который добавляет объектам новое поведение, оборачивая их в объекты-обёртки. Альтернатива наследованию, когда комбинаций поведения много и они меняются в рантайме.
Использование
🔹 Когда нужно добавить поведение объекту, не меняя его класс и не трогая остальные объекты.
🔹 Когда наследование порождает взрывной рост классов из-за комбинаций поведений.
🔹 Когда поведение нужно добавлять и убирать динамически в рантайме.
Преимущества
1️⃣ Гибкость комбинирования поведений
Каждый декоратор делает одно дело. Нужно кеширование + логирование + метрики? Оборачиваете в три слоя в любом порядке. Не нужны метрики — просто не добавляете этот слой.
2️⃣ Единая ответственность
Кеширование живёт в одном классе, логирование — в другом. Каждый декоратор легко читать, тестировать и заменять независимо.
3️⃣ Прозрачность для клиента
Декоратор реализует тот же интерфейс, что и оригинал. Клиентский код не знает, с чем работает — с оригиналом или с обёрткой.
Важный нюанс
Порядок декораторов имеет значение. Метрики снаружи кеша видят кеш-хиты корректно. Метрики внутри — не видят их вообще. Весь пакет java.io в стандартной библиотеке построен на этом паттерне.
Когда не нужен
Если комбинаций нет и поведение статично — обычное наследование проще. В Spring большинство cross-cutting concerns уже закрыто AOP-прокси — дублировать вручную смысла нет.
══════ Навигация ══════
Вакансии • Задачи • Собесы
🐸 Библиотека джависта
#CoreJava
🤯 Представьте, что ваш AI-агент работает так же предсказуемо, как обычный микросервис. Звучит утопически, но это именно то, к чему должна прийти разработка в 2026 году.
Основная боль текущих реализаций — полная непредсказуемость поведения. Сегодня агент выполнил задачу за два шага, а завтра ушёл в рекурсию и потратил все лимиты.
Наш обновлённый курс «Разработка AI-агентов» научит, как приручить этот хаос с помощью Python и современных фреймворков. Мы не будем учить «общаться» с нейросетью, мы будем строить из неё надёжный инструмент.
✅ Что вы получите:
— понимание того, как управлять логикой агента на уровне кода;
— навыки работы с LangChain и библиотеками оркестрации;
— готовые паттерны для обработки ошибок и галлюцинаций;
— опыт создания систем, которые реально экономят время.
Есть пара мест со скидкой до завтра, решайтесь 👈🏻
✔️ Java-тест: задача выполняется, результат теряется
Никаких ошибок в логах. Никаких алертов. Данные просто не сохраняются 👇
📦 Задание — code review
Сервис нотификаций: после оплаты заказа — отправить email и записать событие в БД. Оба действия независимы, сделали асинхронно.
@Service
@RequiredArgsConstructor
public class NotificationService {
private final EmailClient emailClient;
private final EventRepository eventRepository;
private final Executor taskExecutor;
public void notifyOrderPaid(Order order) {
CompletableFuture.runAsync(
() -> emailClient.sendOrderConfirmation(order),
taskExecutor
);
CompletableFuture.runAsync(
() -> {
Event event = Event.orderPaid(order.getId());
eventRepository.save(event);
},
taskExecutor
);
}
}
🔧 Spring Security 6: конфигурация без устаревших паттернов
WebSecurityConfigurerAdapter удалён в Spring Security 6. Новый подход — компонентная модель через SecurityFilterChain. Разбираем актуальную конфигурацию.
🔹 Решение
▪️ Базовая конфигурация
@Configuration
@EnableWebSecurity
@EnableMethodSecurity // включает @PreAuthorize, @PostAuthorize
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
return http
.csrf(csrf -> csrf
.csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse())
.ignoringRequestMatchers("/api/webhooks/**"))
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/health").permitAll()
.requestMatchers("/api/public/**").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated())
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(jwt -> jwt.jwtAuthenticationConverter(jwtConverter())))
.exceptionHandling(ex -> ex
.authenticationEntryPoint(new BearerTokenAuthenticationEntryPoint())
.accessDeniedHandler(new BearerTokenAccessDeniedHandler()))
.build();
}
@Bean
public JwtAuthenticationConverter jwtConverter() {
JwtGrantedAuthoritiesConverter converter = new JwtGrantedAuthoritiesConverter();
converter.setAuthoritiesClaimName("roles");
converter.setAuthorityPrefix("ROLE_");
JwtAuthenticationConverter jwtConverter = new JwtAuthenticationConverter();
jwtConverter.setJwtGrantedAuthoritiesConverter(converter);
return jwtConverter;
}
}
@Bean
@Order(1)
public SecurityFilterChain actuatorChain(HttpSecurity http) throws Exception {
return http
.securityMatcher("/actuator/**")
.authorizeHttpRequests(auth -> auth
.requestMatchers("/actuator/health").permitAll()
.anyRequest().hasIpAddress("10.0.0.0/8"))
.build();
}
@Bean
@Order(2)
public SecurityFilterChain apiChain(HttpSecurity http) throws Exception {
return http
.securityMatcher("/api/**")
.authorizeHttpRequests(auth -> auth.anyRequest().authenticated())
.oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()))
.build();
}
@Service
public class OrderService {
@PreAuthorize("hasRole('ADMIN') or #userId == authentication.name")
public List<Order> getOrders(String userId) { ... }
@PostAuthorize("returnObject.userId == authentication.name")
public Order getOrder(Long orderId) { ... }
}
🗄 Как не «положить» базу данных?
В большинстве веб-приложений (например, Instagram или Twitter) соотношение чтения к записи составляет примерно 10 к 1. То есть люди в 10 раз чаще смотрят чужие посты, чем пишут свои. Эту особенность как раз можно использовать.
🔹 Репликация (Master-Slave / Leader-Follower)
Идея проста: давайте скопируем базу данных на несколько серверов и разделим обязанности.
— Master (Лидер): Это единственная база данных, в которую разрешено ПИСАТЬ (INSERT, UPDATE, DELETE).
— Slave (Ведомые/Реплики): Это точные копии Мастера. Из них можно ТОЛЬКО ЧИТАТЬ (SELECT).
Как это работает:
1. Пользователь публикует фото. Запрос идет на Master-базу.
2. Master сохраняет фото и мгновенно отправляет новые данные всем своим Slave-копиям (реплицирует).
3. 1000 других пользователей открывают ленту. Их запросы на чтение распределяются между тремя Slave-базами.
Итог: нагрузка на чтение размазана, Мастер спокойно занимается только записью.
🔹 Шардирование (Sharding / Горизонтальное партиционирование)
Репликация спасает, когда много читают. Но что делать, если пользователи слишком много пишут (например, это система сбора логов или мессенджер)? Мастер перестает справляться.
Приходит время рубить данные на части - Шардировать.
Мы берем нашу огромную таблицу Users и разбиваем её на несколько независимых баз данных (шардов).
— Шард 1: хранит пользователей с ID от 1 до 1,000,000.
— Шард 2: хранит пользователей с ID от 1,000,001 до 2,000,000.
Каждый шард - это отдельный сервер со своим процессором и диском.
Сложность: теперь вашему приложению (или специальному роутеру) нужно понимать, в какую именно базу отправлять SQL-запрос. А сделать JOIN между таблицами, лежащими на разных серверах, становится практически невозможно.
🔹 Теорема CAP (закон распределенных систем)
Как только ваша база данных перестает жить на одном сервере и разъезжается на несколько (реплики или шарды), вступает в силу закон - теорема CAP.
Она гласит, что в распределенной системе вы можете выбрать только ДВА из ТРЕХ свойств:
1. C (Consistency / Консистентность): все клиенты видят одни и те же данные в один и тот же момент времени. Если я обновил аватарку на Мастере, следующий же запрос к любому Слейву должен вернуть новую аватарку.
2. A (Availability / Доступность): система всегда отвечает на запрос, даже если часть серверов сгорела.
3. P (Partition Tolerance / Устойчивость к разделению): система продолжает работать, даже если между серверами БД пропала сеть (они перестали видеть друг друга).
Суровая реальность: в интернете сеть пропадает всегда. Поэтому свойство P мы обязаны брать по умолчанию. Остается выбор между CP и AP.
🔵 Системы CP (Консистентные): банковские приложения. Если сеть между Мастером и Репликой упала, база откажется отдавать вам баланс, потому что боится отдать устаревшие данные. Лучше выдать ошибку, чем соврать.
🔵 Системы AP (Доступные): соцсети (Instagram, YouTube). Если вы поставили лайк, а сеть внутри дата-центра моргнула, ваш друг может еще пару минут не видеть этот лайк (данные не консистентны). Но сайт при этом не "лежит", лента листается.
══════ Навигация ══════
Вакансии • Задачи • Собесы
🐸 Библиотека джависта
#Enterprise
⚡️ Мы рады представить команду экспертов курса AgentOps!
— Дмитрий Антипов расскажет, как грамотно проверить работу AI-моделей
— Курилл Кухарев поделится, почему компаниям выгодно использовать локальные модели и как их развернуть
— Андрей Носов расскажет, как работать с данными и знаниями в AI-системах: построение RAG, выбор подходов к поиску и организация хранения данных
— Антон Будняк разберет, как обеспечить устойчивость сервиса, в котором используется ИИ
— Александр Ошурков расскажет, как оценивать качество работы LLM в backend-сервисах
— Екатерина Трофимов разберет, как проектировать инструменты для AI-агентов и выстраивать взаимодействие с внешними сервисами
Курс для backend-разработчиков, тимлидов и LLM инженеров о том, как внедрять AI-логику в бэкенд IT-продуктов и сохранять стабильность сервиса.
К концу обучения вы получите:
• Структурированный подход к архитектуре и деплою AI-агентов
• Навыки настройки мониторинга, тестирования и контроля расходов на токены
• Разбор сложных инженерных кейсов из реальной практики
🎁 Доступ к материалам курса «Разработка ИИ-агентов» в подарок при покупке Инженерного трека
👉 Все подробности и программа обучения.
☕️ Магия JVM Flags
Запустите своё приложение с -XX:+PrintFlagsFinal, и вы увидите все настройки JVM.
🔹 Зачем это нужно
— Показывает финальные значения всех JVM-флагов после применения эргономики, дефолтов и ваших переопределений.
— Полезно для Java-сервисов в контейнерах: вы думаете, что выставили -Xmx512m, а JVM видит 4GB хоста и живёт своей жизнью.
— Мгновенно отвечает на вопрос «а почему heap такой большой?».
— Работает на любом JDK без агентов, плагинов и прав суперпользователя.
🔹 Как использовать
— Дамп всех флагов при старте:
java -XX:+PrintFlagsFinal -version
java -XX:+PrintFlagsFinal -version 2>&1 | grep -i heapsize
java -XX:+PrintFlagsFinal -version 2>&1 | grep -i "use.*gc"
jcmd <pid> VM.flags
💬 Обратная связь
Какая рубрика нравится больше? Если забыли, о чём рубрика, можно освежить в памяти тут.
🔥 → #CoreJava
👍🏼 → #Enterprise
👾 → #DevLife
🤔 → #News
❤️ → Всё нравится :))
🐸 Библиотека джависта
#DevLife
✔️ Java-тест: бин-синглтон, который не синглтон
Работает у одного, но ломается у другого. В логах каша из чужих данных 👇
📦 Задание — code review
Команда добавила контекст текущего пользователя в сервис через поле. Локально — всё ок.
На проде с несколькими потоками — пользователи видят чужие данные.
@Service
public class ReportService {
private User currentUser;
private ReportFilter activeFilter;
private final ReportRepository reportRepository;
public ReportService(ReportRepository reportRepository) {
this.reportRepository = reportRepository;
}
public void initContext(User user, ReportFilter filter) {
this.currentUser = user;
this.activeFilter = filter;
}
public List<Report> getReports() {
if (currentUser == null) {
throw new IllegalStateException("Context not initialized");
}
return reportRepository.findByUserAndFilter(
currentUser.getId(),
activeFilter
);
}
public ReportSummary getSummary() {
List<Report> reports = getReports();
return ReportSummary.calculate(reports, currentUser);
}
}
Есть кейс или продукт, о котором хочется рассказать backend-сообществу?
Тогда хорошие новости: 29 августа в Москве пройдет конференция JVM Day — большая встреча инженеров, на которую ищут спикеров.
Вы сможете:
— выступить с докладом — рассказать о кейсе или нестандартном решении. Формат классический: 40-минутный доклад на сцене и вопросы из зала;
— представить продукт в демозоне. У вашей команды будет пространство с экранами и стойками на весь день: можно показывать технологию вживую, общаться с инженерами, собирать обратную связь и находить первых пользователей.
Поддерживают любой формат: можно выступить одному или с коллегой, устроить дискуссию или воркшоп.
Хороший шанс заявить о себе и проверить, как сообщество реагирует на ваш продукт.
Встречаемся в штаб-квартире Т-Банка.
Подайте заявку прямо сейчас
⚡️ Почему record не взлетел даже для DTO
Казалось бы идеальная фича для DTO. Иммутабельность, компактный синтаксис, никакого Lombok.
Но есть одна деталь, которая всё портит. В record геттер называется name(), а не getName().
Звучит мелко? Но это нарушение JavaBean-конвенции, которой 20+ лет. И всё, что на ней завязано ломается или требует допиливания:
→ Jackson — заработал только с версии 2.12. До этого name() не распознавался как property вообще
→ ModelMapper — не видит аксессоры record из коробки, нужна ручная конфигурация
→ Swagger/OpenAPI-генераторы — генерируют дублирующиеся поля или теряют их
→ BeanUtils, PropertyUtils (Apache Commons) — не работают, ждут getName()
→ Любой легаси-код с рефлексией — Introspector.getBeanInfo() record не понимает
И это не баги инструментов — это осознанное решение команды Java. Они сказали: «Мы проектируем для нового кода, а не для поддержки старых конвенций».
Звучит красиво в теории. На практике в энтерпрайзе стек из легаси-либ, и record там как белая ворона.
💬 Работаете record'ами или обходите их стороной?
══════ Навигация ══════
Вакансии • Задачи • Собесы
🐸 Библиотека джависта
#CoreJava
Как и зачем эффективно интегрировать legacy-системы через Apache Kafka?
Ждем вас на открытом практикуме «Интеграция legacy-систем через Apache Kafka: быстрый старт без переписывания всего» от ОТУС! Расписание эфира:
• Проблема: почему просто переписать — это рискованно, а P2P-интеграции ведут к спагетти-архитектуре
• Решение: Apache Kafka как слой реального времени — обзор паттерна «труба и фильтры» для Legacy
• Разбор паттернов: как обмануть старую систему: дуалирование записи (Dual Writes) и чтение из журналов транзакций
• Дорожная карта: 3 шага к отказу от монолита с сохранением работоспособности старой системы (Strangler Fig Pattern)
Ведущий: Сергей Прощаев — ведущий инженер в компании ПАО «Сургутнефтегаз».
Бонусы для участников:
7% скидка на любой курс ОТУС
Доступ к бесплатному пробному периоду корпоративной платформы
Экспертные разборы по Kafka
Ссылка на регистрацию: https://clc.to/G5_Lwg
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576
😮 Топ-вакансий для джавистов за неделю
Разработчик приложений и автоматизации (Middle / Junior+) — от 1100$ до 4000$ — удалённая работа / full-time или частичная занятость
Middle+ Java Backend Developer — IRLIX — удалёнка
Senior Java Developer — Лектон — удалёнка/гибрид (Санкт-Петербург)
➡️ Еще больше топовых вакансий — в нашем канале Java jobs
🐸 Библиотека джависта
#DevLife
🐸 Библиотека джависта
#DevLife
🐳 Магия Docker CLI
Запустите docker stats с правильными флагами, и вы увидите всё в реальном времени.
🔹 Зачем это нужно
— Показывает live-метрики по CPU, памяти, сети и I/O для всех запущенных контейнеров.
— Критично для Java-сервисов: помогает поймать утечки памяти, GC pressure и неожиданный CPU spike прямо в момент нагрузки.
— Работает без установки каких-либо агентов.
— В отличие от top внутри контейнера видит реальные лимиты cgroup, а не ресурсы хоста.
🔹 Как использовать
— Все контейнеры в реальном времени: docker stats
— Конкретный сервис: docker stats my-spring-app
— Один снимок без стриминга: docker stats --no-stream
— Только нужные поля: docker stats --format "table {{.Name}}\t{{.MemUsage}}\t{{.CPUPerc}}"
— В JSON для скриптов: docker stats --no-stream --format json
Флаг --format принимает Go-шаблоны — те же, что и в docker inspect. Можно вывести ровно то, что нужно, и скормить в jq или свой мониторинг-скрипт.
⚠️ Если контейнер запущен без -m / --memory, JVM видит всю память хоста и выставляет heap соответственно. docker stats мгновенно покажет, есть ли лимит — колонка MEM USAGE / LIMIT.
══════ Навигация ══════
Вакансии • Задачи • Собесы
🐸 Библиотека джависта
#Enterprise
🤔 Разрабатываете ИИ-агентов, но всё ещё не уверены в их стабильности и прогнозируемости?
Мы поговорили с десятками разработчиков ИИ-агентов и сделали отдельный курс по AgentOps.
🧠 На нём вы узнаете:
– как оптимизировать траты на токены;
– как на практике оценить качество работы агента;
– как «докручивать» RAG-системы без потери качества;
– как обеспечить устойчивость агента к сбоям внешних сервисов без падения всей системы и про многое-многое другое.
📅 Старт: 19 мая.
👥 Спикеры — практики с опытом в AI и Data Science в крупных IT-компаниях, таких как Яндекс, Huawei, МТС и др.
Длительность: 6-12 недель в зависимости от тарифа.
🔗 Программа курса и другие подробности
🐸 Библиотека джависта
#DevLife
⚡️ Просто о сложном: почему Redis такой быстрый
Стандартный ответ — «потому что in-memory». Это правда, но не объяснение. Куча баз данных хранит данные в памяти и не показывает таких цифр. Настоящий ответ в другом: каждое архитектурное решение Redis убирает конкретный источник latency.
🧵 Один поток
Redis обрабатывает команды последовательно в один поток. Никаких мьютексов, никакого context switching. Тысячи соединений при этом обслуживаются через epoll/kqueue — один поток следит за всеми сокетами через I/O multiplexing и не блокируется ни на одном из них.
📦 Адаптивные структуры
Hash с небольшим числом ключей хранится не как хэш-таблица, а как listpack — плотный байтовый массив, который влезает в один cache line. Никаких pointer chasing. Только когда данных становится много, Redis переключается на полноценную хэш-таблицу. Sorted Set — одновременно хэш-таблица и skiplist. O(1) по ключу и O(log N) для range-запросов без второго прохода.
💾 fork()
Снапшот RDB пишется дочерним процессом после fork(). Благодаря CoW основной процесс продолжает работу и не видит никакой паузы — ОС копирует только изменённые страницы памяти. AOF идёт дальше и логирует каждую команду, давая возможность восстановиться до последней записи. Точнее, но дороже — компромисс осознанный.
🧹 bounded expiry
Ключи с истёкшим TTL удаляются двумя способами: лениво при обращении и активно — каждые 100 мс Redis сэмплирует случайную выборку и чистит просроченные. Если таких больше 25%, повторяет цикл. Это не O(N) по всей базе, а предсказуемая нагрузка с жёстким потолком по CPU.
Redis — это не «база данных в памяти». Это система, где каждый выбор — однопоток, listpack, fork(), bounded expiry — существует потому, что альтернатива добавляла latency. In-memory здесь просто необходимое условие, а не причина скорости.
══════ Навигация ══════
Вакансии • Задачи • Собесы
🐸 Библиотека джависта
#CoreJava