Как StructuredTaskScope в JDK 27 отменяет лишнюю работу при сбое
Два независимых вызова к сервисам последовательно занимают около двух секунд. Виртуальные потоки с
Сравнение трёх реализаций учитывает седьмую предварительную версию API в JDK 27 и проверяемое исключение
Два независимых вызова к сервисам последовательно занимают около двух секунд. Виртуальные потоки с
Future сокращают успешный запуск примерно до секунды, но при сбое одного вызова через 200 мс второй продолжает работу. Закрытие ExecutorService ждёт его завершения, хотя результат уже не нужен.StructuredTaskScope объединяет подзадачи: ошибка одной прерывает соседнюю, а join() сообщает о сбое всей группы. В примере метод возвращается примерно через 200 мс вместо полной секунды.Сравнение трёх реализаций учитывает седьмую предварительную версию API в JDK 27 и проверяемое исключение
ExecutionException. Для запуска нужен --enable-preview. Ограничение сохраняется: задача должна реагировать на прерывание, а закрытие области ждёт завершения всех подзадач.🤔1
Как настроить MFA в Spring Security 7 для приложения и отдельных эндпоинтов
Spring Security 7, доступный со Spring Boot 4, добавил
Для MFA во всём приложении аннотация
Чтобы усилить только
Spring Security 7, доступный со Spring Boot 4, добавил
FactorGrantedAuthority. После входа по паролю пользователь получает FACTOR_PASSWORD, а после одноразового токена из письма — FACTOR_OTT. Доступ открывается, когда собраны все обязательные факторы.Для MFA во всём приложении аннотация
@EnableMultiFactorAuthentication связывает фильтры входа и создаёт проверку требуемых факторов. Стандартная форма не умеет задавать их порядок, поэтому для последовательного сценария понадобятся собственные страницы.Чтобы усилить только
/admin/**, аннотации передают пустой список факторов, а отдельный AuthorizationManagerFactory назначают этому маршруту. Остальные запросы сохраняют однофакторную проверку. Конфигурация обоих вариантов приведена с кодом.❤1
JDK 25 и G1: почему Arrays.fill замедлился в 265 раз
В JMH-тесте на Apple M4 Max заполнение массивов ссылок заняло у G1 139 мс, а у ParallelGC 0,5 мс. При этом сборка мусора не запускалась: почти всё время ушло в JIT-код
Причина: барьер записи, который JVM добавляет к каждому присваиванию ссылки. Он помечает изменённые участки кучи, чтобы G1 не сканировал всё старое поколение. На результат также повлияли размер регионов G1 и переход массива в старое поколение.
Разбор JMH-бенчмарка и ARM64-инструкций HotSpot объясняет, почему одной инструкции оказалось недостаточно. Замедление относится к этой конфигурации: перед настройкой G1 повторите замер на своей нагрузке.
В JMH-тесте на Apple M4 Max заполнение массивов ссылок заняло у G1 139 мс, а у ParallelGC 0,5 мс. При этом сборка мусора не запускалась: почти всё время ушло в JIT-код
Arrays.fill.Причина: барьер записи, который JVM добавляет к каждому присваиванию ссылки. Он помечает изменённые участки кучи, чтобы G1 не сканировал всё старое поколение. На результат также повлияли размер регионов G1 и переход массива в старое поколение.
Разбор JMH-бенчмарка и ARM64-инструкций HotSpot объясняет, почему одной инструкции оказалось недостаточно. Замедление относится к этой конфигурации: перед настройкой G1 повторите замер на своей нагрузке.
😁3
Spring Boot 4 и BoxLang: как сохранить CFML-шаблоны при миграции
BoxLang Spring Boot Starter позволяет оставить представления на CFML, языке шаблонов ColdFusion, пока контроллеры и другие части приложения переносятся на Spring. В Maven-проект добавляется стартер версии 1.0.0, а свойство
Контроллер остаётся обычным для Spring MVC: возвращает логическое имя представления, а атрибуты
Это даёт путь для поэтапной миграции без одновременной переписи всех представлений. В готовом примере показаны настройка проекта, передача данных из контроллера и запуск шаблонов на Spring Boot 4.
BoxLang Spring Boot Starter позволяет оставить представления на CFML, языке шаблонов ColdFusion, пока контроллеры и другие части приложения переносятся на Spring. В Maven-проект добавляется стартер версии 1.0.0, а свойство
boxlang.suffix=.cfm переключает его обработчик представлений с файлов .bxm на .cfm.Контроллер остаётся обычным для Spring MVC: возвращает логическое имя представления, а атрибуты
Model автоматически становятся переменными CFML. В шаблонах продолжают работать cfoutput, условия, циклы и подключение общих блоков.Это даёт путь для поэтапной миграции без одновременной переписи всех представлений. В готовом примере показаны настройка проекта, передача данных из контроллера и запуск шаблонов на Spring Boot 4.
👍4❤3🤔2😁1
Как измерить экономию памяти от компактных заголовков в Java 27
В Java 27 HotSpot по умолчанию сокращает заголовок объекта с 12 до 8 байт. Но из-за выравнивания по 8 байт объект с одним полем
Воспроизводимый эксперимент сравнивает две JVM с одинаковыми настройками кучи и G1. Java-агент вызывает
Перед уменьшением лимита кучи стоит проверить формы объектов, которые преобладают в сервисе, а затем сравнить живую кучу, нагрузку на GC, задержки и память процесса. Сокращение заголовка само по себе ещё не доказывает экономию ресурсов приложения.
В Java 27 HotSpot по умолчанию сокращает заголовок объекта с 12 до 8 байт. Но из-за выравнивания по 8 байт объект с одним полем
int остался 16-байтным, а с двумя уменьшился с 24 до 16 байт.Воспроизводимый эксперимент сравнивает две JVM с одинаковыми настройками кучи и G1. Java-агент вызывает
getObjectSize() и измеряет только сам объект, без объектов по ссылкам. Во втором запуске компактные заголовки отключаются флагом -XX:-UseCompactObjectHeaders.Перед уменьшением лимита кучи стоит проверить формы объектов, которые преобладают в сервисе, а затем сравнить живую кучу, нагрузку на GC, задержки и память процесса. Сокращение заголовка само по себе ещё не доказывает экономию ресурсов приложения.
👍1😁1
JDK 26: измеряем затраты CPU на сборку мусора
JDK 26 добавляет
Паузы уже не показывают всю цену. В приведённой нагрузке G1 выполнял 79% работы GC конкурентно с приложением. ZGC переносит почти всю тяжёлую работу в конкурентные фазы и держит паузы меньше миллисекунды, но вычисления не исчезают.
В разборе метрики её проверяют через
JDK 26 добавляет
MemoryMXBean.getTotalGcCpuTime(): метод возвращает суммарное процессорное время отдельных потоков GC. Так можно сравнивать размеры кучи на своей нагрузке по затратам сборщика, а не только по длительности пауз.Паузы уже не показывают всю цену. В приведённой нагрузке G1 выполнял 79% работы GC конкурентно с приложением. ZGC переносит почти всю тяжёлую работу в конкурентные фазы и держит паузы меньше миллисекунды, но вычисления не исчезают.
В разборе метрики её проверяют через
-Xlog:cpu на DaCapo и Spring PetClinic. Она учитывает явную работу GC, но не служебный код барьеров в приложении и влияние GC на кэши CPU. Для выбора размера кучи сопоставляйте её с пропускной способностью и задержками.😁2❤1🔥1
Spring переводит патч-релизы на единый Patch Thursday
Регулярные патч-релизы проектов Spring теперь будут собирать в один день. Раньше релизы распределялись по двухнедельному окну: исправления в разных частях экосистемы появлялись постепенно.
Новое окно — четверг после третьего понедельника месяца. Первый регулярный выпуск по этой схеме намечен на 22 октября 2026 года. Команда сохраняет привычный день публикации Spring Boot в Maven Central.
Для сопровождения сервисов это повод пересмотреть календарь обновлений и интеграционных проверок зависимостей. Одновременно Spring обновил раздел security: рекомендации по уязвимостям можно искать по CVE, важности и проекту.
Подробности нового графика
Регулярные патч-релизы проектов Spring теперь будут собирать в один день. Раньше релизы распределялись по двухнедельному окну: исправления в разных частях экосистемы появлялись постепенно.
Новое окно — четверг после третьего понедельника месяца. Первый регулярный выпуск по этой схеме намечен на 22 октября 2026 года. Команда сохраняет привычный день публикации Spring Boot в Maven Central.
Для сопровождения сервисов это повод пересмотреть календарь обновлений и интеграционных проверок зависимостей. Одновременно Spring обновил раздел security: рекомендации по уязвимостям можно искать по CVE, важности и проекту.
Подробности нового графика
Releasing Spring for Modern Challenges
Level up your Java code and explore what Spring can do for you.
❤2
Почему JNI-ссылки могут привести к падению JVM
JNI отдаёт нативному коду дескриптор Java-объекта. Локальная ссылка, созданная в native-методе, привязана к текущему потоку и освобождается при завершении вызова. Сохранить её в статической переменной для следующего вызова — значит оставить недействительный дескриптор.
Для кэширования класса или callback-объекта нужна
Отдельная ошибка — передавать
Разбор ошибок с примерами C++
JNI отдаёт нативному коду дескриптор Java-объекта. Локальная ссылка, созданная в native-методе, привязана к текущему потоку и освобождается при завершении вызова. Сохранить её в статической переменной для следующего вызова — значит оставить недействительный дескриптор.
Для кэширования класса или callback-объекта нужна
NewGlobalRef(). Такая ссылка удерживает объект от сборки мусора, пока нативный код не вызовет DeleteGlobalRef(). Поэтому место освобождения стоит определить сразу.Отдельная ошибка — передавать
JNIEnv* другому потоку. Нативный поток должен получить собственное окружение через подключение к JVM. Для диагностики в HotSpot есть флаг -Xcheck:jni.Разбор ошибок с примерами C++
freeCodeCamp.org
How to Avoid JNI Crashes by Managing Local and Global References Correctly
Most JNI crashes don't come from complicated logic. They come from a small set of mistakes around object references: holding on to a reference after it has become invalid, creating references faster t
❤🔥1
Почему нагрузочный тест в одной JVM может скрыть задержки GC
Если генератор запросов работает в той же JVM, что и тестируемый сервис, пауза GC останавливает оба компонента. Пока сервис не отвечает, генератор тоже не создаёт часть запросов. В измерения не попадает нагрузка, которая пришла бы от независимого клиента.
Исследователи сравнили режимы SPECjbb2015 на OpenJDK 27: с генератором внутри JVM сервиса и в отдельном процессе. Для сборщиков с заметными паузами значения p99 существенно различались; у ZGC такой разницы не наблюдали.
Даже учёт запланированного времени отправки не восстанавливает запросы, которые генератор не смог создать. При проверке хвоста задержек полезно вынести его в отдельную JVM и сопоставить времена запросов с логами GC.
Методика и экспериментальные результаты
Если генератор запросов работает в той же JVM, что и тестируемый сервис, пауза GC останавливает оба компонента. Пока сервис не отвечает, генератор тоже не создаёт часть запросов. В измерения не попадает нагрузка, которая пришла бы от независимого клиента.
Исследователи сравнили режимы SPECjbb2015 на OpenJDK 27: с генератором внутри JVM сервиса и в отдельном процессе. Для сборщиков с заметными паузами значения p99 существенно различались; у ZGC такой разницы не наблюдали.
Даже учёт запланированного времени отправки не восстанавливает запросы, которые генератор не смог создать. При проверке хвоста задержек полезно вынести его в отдельную JVM и сопоставить времена запросов с логами GC.
Методика и экспериментальные результаты
Jonas Norlinder
The Limitations of Running a Workload Generator In the Same JVM as the System-Under-Test
If you care about tail latency in SPECjbb2015, Composite mode hides GC impact. We quantify the discrepancy and recommend Distributed or MultiJVM.
👍1
Maven разрешает зависимости: app → client → core:1.4 и app → adapter → bridge → core:2.0. Обе версии core имеют одинаковые groupId и artifactId; все зависимости — compile, управления версиями нет. Какая версия core попадёт в путь классов приложения?
Anonymous Quiz
23%
Только core:1.4
29%
Только core:2.0
32%
Обе версии core
16%
Сборка завершится ошибкой конфликта версий
👍2❤1
Чашечка Java
Maven разрешает зависимости: app → client → core:1.4 и app → adapter → bridge → core:2.0. Обе версии core имеют одинаковые groupId и artifactId; все зависимости — compile, управления версиями нет. Какая версия core попадёт в путь классов приложения?
Развёрнутое пояснение:
1. При построении дерева зависимостей Maven обнаруживает core:1.4 через client: от приложения до этой зависимости два перехода.
2. Через adapter и bridge обнаруживается core:2.0: до неё три перехода.
3. Поскольку groupId и artifactId совпадают, Maven рассматривает эти зависимости как две версии одного артефакта и разрешает конфликт по правилу ближайшей зависимости.
4. Путь до core:1.4 короче, поэтому в путь классов попадает только версия 1.4. Более высокий номер версии не даёт приоритета, а сам конфликт версий обычно не прерывает сборку.
Почему это важно
Задача проверяет разрешение транзитивных зависимостей Maven. При добавлении библиотеки приложение может получить более старую версию общего артефакта, чем ожидает другой компонент. Если ему нужны методы из версии 2.0, во время выполнения возможен NoSuchMethodError. Дерево зависимостей помогает обнаружить такой выбор, а управление версиями — задать согласованную версию явно.
2. Через adapter и bridge обнаруживается core:2.0: до неё три перехода.
3. Поскольку groupId и artifactId совпадают, Maven рассматривает эти зависимости как две версии одного артефакта и разрешает конфликт по правилу ближайшей зависимости.
4. Путь до core:1.4 короче, поэтому в путь классов попадает только версия 1.4. Более высокий номер версии не даёт приоритета, а сам конфликт версий обычно не прерывает сборку.
Почему это важно
Задача проверяет разрешение транзитивных зависимостей Maven. При добавлении библиотеки приложение может получить более старую версию общего артефакта, чем ожидает другой компонент. Если ему нужны методы из версии 2.0, во время выполнения возможен NoSuchMethodError. Дерево зависимостей помогает обнаружить такой выбор, а управление версиями — задать согласованную версию явно.
👏1
JDK 28: простой API для чтения и создания JSON
Для JDK 28 интегрировали JEP 540: модуль
Такой API пригодится, например, для разбора ответа REST-сервиса или создания небольшого JSON-документа в утилите. Область задач ограничена: автоматического преобразования Java-объектов в JSON и потокового разбора в нём нет.
API находится в стадии инкубации, а JDK 28 доступен в ранних сборках. Для существующего приложения на Jackson переход потребует оценки того, какие возможности оно использует.
Описание и примеры JEP 540
Для JDK 28 интегрировали JEP 540: модуль
jdk.incubator.json позволит работать с JSON без внешней библиотеки. Метод Json.parse() строит дерево JsonValue, по которому можно переходить через get() и извлекать значения нужного типа.Такой API пригодится, например, для разбора ответа REST-сервиса или создания небольшого JSON-документа в утилите. Область задач ограничена: автоматического преобразования Java-объектов в JSON и потокового разбора в нём нет.
API находится в стадии инкубации, а JDK 28 доступен в ранних сборках. Для существующего приложения на Jackson переход потребует оценки того, какие возможности оно использует.
Описание и примеры JEP 540
JDK 28: маленькие аллокации Arena.ofConfined() станут дешевле
Для будущего JDK 28 подготовили оптимизацию FFM API: небольшие выделения нативной памяти в
Пул выдают арене при подходящей аллокации. При закрытии использованную память обнуляют; пул возвращают в кэш либо освобождают, если кэш полон. Сегменты закрытой арены остаются недоступными. Крупные запросы и неподходящее выравнивание используют обычный путь выделения памяти.
Оптимизация работает и с виртуальными потоками: пул берётся у несущего потока и затем принадлежит арене. Переписывать вызовы API не требуется. Выигрыш в JMH измерен для маленьких аллокаций; ускорение всего приложения зависит от его нагрузки.
Устройство пулов и результаты замеров
Для будущего JDK 28 подготовили оптимизацию FFM API: небольшие выделения нативной памяти в
Arena.ofConfined() смогут обслуживаться из переиспользуемых пулов. По умолчанию кэш платформенного потока хранит до четырёх пулов по 64 байта.Пул выдают арене при подходящей аллокации. При закрытии использованную память обнуляют; пул возвращают в кэш либо освобождают, если кэш полон. Сегменты закрытой арены остаются недоступными. Крупные запросы и неподходящее выравнивание используют обычный путь выделения памяти.
Оптимизация работает и с виртуальными потоками: пул берётся у несущего потока и затем принадлежит арене. Переписывать вызовы API не требуется. Выигрыш в JMH измерен для маленьких аллокаций; ускорение всего приложения зависит от его нагрузки.
Устройство пулов и результаты замеров
👏1😁1
Spring Boot 4: настройка сервера авторизации и проверка токенов
Spring Authorization Server вошёл в Spring Security 7 и следует его циклу релизов. Он выдаёт клиентским приложениям токены доступа по OAuth 2.1 и поддерживает OpenID Connect 1.0 для идентификации пользователей.
В руководстве по настройке сервера показаны регистрация клиента через свойства приложения и проверка токенов в HTTP-клиенте IntelliJ. Authorization Code разбирается на примере входа пользователя с подтверждением разрешений. Client Credentials используется для обмена между приложениями без участия пользователя. Затем автор добавляет в JWT, токен с полями данных, список полномочий под именем
Пример хранит пользователей в памяти. Для промышленной эксплуатации автор рекомендует внешний сервис управления пользователями и входом, например Keycloak или Okta.
#spring #java
Spring Authorization Server вошёл в Spring Security 7 и следует его циклу релизов. Он выдаёт клиентским приложениям токены доступа по OAuth 2.1 и поддерживает OpenID Connect 1.0 для идентификации пользователей.
В руководстве по настройке сервера показаны регистрация клиента через свойства приложения и проверка токенов в HTTP-клиенте IntelliJ. Authorization Code разбирается на примере входа пользователя с подтверждением разрешений. Client Credentials используется для обмена между приложениями без участия пользователя. Затем автор добавляет в JWT, токен с полями данных, список полномочий под именем
aut.Пример хранит пользователей в памяти. Для промышленной эксплуатации автор рекомендует внешний сервис управления пользователями и входом, например Keycloak или Okta.
#spring #java
Маршрутизатор запросов к моделям на Spring AI TypeSafe
Spring Boot 4.1.1 и стартер Spring AI TypeSafe 0.1.0: в туториале запрос сначала получает оценку сложности, затем направляется к выбранной модели. Приветствие и задача по проектированию архитектуры могут требовать разных моделей.
В разборе сборки маршрутизатора с нуля четыре уровня моделей описаны в enum. Jev, модель для структурированных решений, получает запрос и выбирает уровень по этим описаниям. Вместе с выбором возвращаются оценка уверенности и вероятности для каждого уровня: по ним можно смотреть, как изменения описаний влияют на решение.
Для маршрутизатора достаточно стартера. Без свойства spring.ai.typesafe.api-key он не создаст бин TypeSafeClient. Проект пока находится в Spring AI Community и не входит в основной репозиторий Spring AI.
#spring #java
Spring Boot 4.1.1 и стартер Spring AI TypeSafe 0.1.0: в туториале запрос сначала получает оценку сложности, затем направляется к выбранной модели. Приветствие и задача по проектированию архитектуры могут требовать разных моделей.
В разборе сборки маршрутизатора с нуля четыре уровня моделей описаны в enum. Jev, модель для структурированных решений, получает запрос и выбирает уровень по этим описаниям. Вместе с выбором возвращаются оценка уверенности и вероятности для каждого уровня: по ним можно смотреть, как изменения описаний влияют на решение.
Для маршрутизатора достаточно стартера. Без свойства spring.ai.typesafe.api-key он не создаст бин TypeSafeClient. Проект пока находится в Spring AI Community и не входит в основной репозиторий Spring AI.
#spring #java
Forwarded from Инструменты программиста
Какими ИИ-ассистентами и агентами вы пользуетесь? Можно выбрать несколько
Anonymous Poll
8%
GitHub Copilot
1%
Codeium
46%
Claude Code
23%
Gemini
48%
ChatGPT
4%
GigaCode (Сбер)
2%
Yandex Code Assistant
29%
DeepSeek
5%
Hermes Agent
12%
Другое (напишу в комментариях)
Forwarded from Инструменты программиста
Read. Write. Edit. Bash.
Это базовый набор операций Pi — агента, на котором построен OpenClaw. Дополнительные возможности появляются через расширения: агент может написать их по запросу пользователя, перезагрузить и проверить в работе.
У такого устройства есть полезные детали:
• состояние расширений хранится в сессии;
• беседу можно разветвить для отладки и вернуться с кратким пересказом;
• инструкции и примеры помогают агенту создавать новые функции.
Армин Ронахер разбирает Pi на собственных примерах: среди расширений, созданных по его запросам, есть список задач и ревью изменений кода. Получается взгляд на выбор ИИ-инструмента через то, что пользователь может настроить в нём под свою работу.
#ии #инструменты
Это базовый набор операций Pi — агента, на котором построен OpenClaw. Дополнительные возможности появляются через расширения: агент может написать их по запросу пользователя, перезагрузить и проверить в работе.
У такого устройства есть полезные детали:
• состояние расширений хранится в сессии;
• беседу можно разветвить для отладки и вернуться с кратким пересказом;
• инструкции и примеры помогают агенту создавать новые функции.
Армин Ронахер разбирает Pi на собственных примерах: среди расширений, созданных по его запросам, есть список задач и ревью изменений кода. Получается взгляд на выбор ИИ-инструмента через то, что пользователь может настроить в нём под свою работу.
#ии #инструменты
Регистрация плагинов по списку через BeanRegistrar в Spring Framework 7
Spring Framework 7 и Spring Boot 4 позволяют программно регистрировать бины через
Регистратор читает свойства
Автор отмечает, что для большинства случаев условной регистрации бинов, по его мнению, достаточно аннотаций семейства
#spring #java
Spring Framework 7 и Spring Boot 4 позволяют программно регистрировать бины через
BeanRegistrar. В руководстве по подключению плагинов разобран пример приложения, где набор плагинов задаётся списком в настройках.Регистратор читает свойства
app.plugins[0], app.plugins[1] и далее через Environment, пока не встретит отсутствующее значение. Затем сопоставляет имена плагинов с классами и регистрирует их через BeanRegistry. Подключение самого регистратора показано через @Import.Автор отмечает, что для большинства случаев условной регистрации бинов, по его мнению, достаточно аннотаций семейства
@ConditionalOn….#spring #java
В Spring включён стандартный режим транзакций через прокси. Контроллер вызывает метод submit сервиса без активной транзакции. Метод submit без аннотаций вызывает публичный метод save того же объекта, помеченный @Transactional. Каковы границы транзакции сервиса?
Anonymous Quiz
34%
Транзакция охватывает только выполнение save
14%
Транзакция охватывает submit целиком
46%
Транзакция сервиса не открывается
5%
Вызов save завершается IllegalTransactionStateException
Чашечка Java
В Spring включён стандартный режим транзакций через прокси. Контроллер вызывает метод submit сервиса без активной транзакции. Метод submit без аннотаций вызывает публичный метод save того же объекта, помеченный @Transactional. Каковы границы транзакции сервиса?
Развёрнутое пояснение:
1. Контроллер обращается к сервису через прокси Spring; активной транзакции в этот момент нет.
2. Для метода submit транзакционные настройки не заданы, поэтому прокси передаёт управление объекту сервиса без открытия транзакции.
3. Метод submit напрямую вызывает save у того же объекта. Такой вызов не проходит через прокси, который применяет транзакционные настройки.
4. Аннотация @Transactional на save сама по себе не запускает транзакцию: в режиме прокси её должен обработать перехватчик Spring. Поэтому транзакция сервиса не открывается, а отсутствие транзакции само по себе не вызывает указанного исключения. Отдельные операции репозитория могут иметь собственные транзакционные границы.
Почему это важно
Задача проверяет понимание прокси и декларативного управления транзакциями Spring. При обработке заказа такой внутренний вызов может оставить несколько записей без общей транзакции: сбой поздней операции не обязательно отменит предыдущие. Если все операции submit должны быть атомарными, транзакционную границу можно задать на submit; если нужна отдельная граница для save, его можно вынести в другой сервис и вызывать через прокси.
2. Для метода submit транзакционные настройки не заданы, поэтому прокси передаёт управление объекту сервиса без открытия транзакции.
3. Метод submit напрямую вызывает save у того же объекта. Такой вызов не проходит через прокси, который применяет транзакционные настройки.
4. Аннотация
Почему это важно
Задача проверяет понимание прокси и декларативного управления транзакциями Spring. При обработке заказа такой внутренний вызов может оставить несколько записей без общей транзакции: сбой поздней операции не обязательно отменит предыдущие. Если все операции submit должны быть атомарными, транзакционную границу можно задать на submit; если нужна отдельная граница для save, его можно вынести в другой сервис и вызывать через прокси.