Заметки о бэкенде: Java, PostgreSQL, очереди и интеграции. Записываю то, что пригодилось в работе, чтобы не искать решение второй раз.
Частичный индекс вместо обычного
Таблица заказов на десятки миллионов строк, а в работе — несколько тысяч со статусом NEW. Обычный индекс по (status, created_at) хранит всё подряд и растёт вместе с архивом. Частичный индекс хранит только нужные строки и помещается в память целиком.
CREATE INDEX CONCURRENTLY orders_new_created_idx
ON orders (created_at)
WHERE status = 'NEW';
Подвох один: условие запроса должно совпадать с условием индекса. Если статус приходит параметром, планировщик в generic-плане может не доказать, что индекс подходит, — смотрите EXPLAIN на реальном запросе из приложения.
postgresql
Повторы в RabbitMQ без бесконечного цикла
Сообщение не обработалось — отправляем его в очередь ожидания с TTL. Когда TTL истекает, dead-letter exchange возвращает его в рабочую очередь. Число попыток видно в заголовке x-death, после пятой сообщение уходит в отдельную очередь на разбор.
Так повторы не забивают рабочую очередь и не крутятся вечно, а сломанные сообщения не теряются.
rabbitmqинтеграции
Graceful shutdown в Spring Boot
При выкатке под Kubernetes часть запросов падала с 502. Spring Boot умеет дожидаться активных запросов, но Kubernetes убирает под из балансировки не мгновенно — сначала нужно немного подождать, и только потом останавливаться.
Плюс preStop-хук со sleep на 5–10 секунд в манифесте: за это время под исчезает из эндпоинтов, и новые запросы на него уже не приходят.
springjava
Идемпотентность входящих сообщений
Партнёр может прислать одно и то же сообщение дважды — после таймаута или своего перезапуска. Храним идентификаторы обработанных сообщений и вставляем новый в той же транзакции, что и бизнес-изменение.
INSERT INTO processed_message (message_id, received_at)
VALUES (:messageId, now())
ON CONFLICT (message_id) DO NOTHING;
Вставилась строка — обрабатываем. Не вставилась — это повтор, отвечаем тем же результатом и ничего не делаем.
интеграцииpostgresql
Виртуальные потоки: где помогли, а где нет
Сервис ходит к десятку внешних систем и в основном ждёт ответа. Перевод этих вызовов на виртуальные потоки убрал возню с размером пула: каждому запросу свой поток, и памяти это почти не стоит.
На расчётных задачах выигрыша нет: процессор от этого не прибавляется. И в Java 21 блок synchronized пришпиливает виртуальный поток к несущему — в JDK 24 это исправили (JEP 491).