<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>karandashov.com – Postgresql</title><link>https://karandashov.com/tags/postgresql/</link><description>Recent content in Postgresql on karandashov.com</description><generator>Hugo -- gohugo.io</generator><language>ru</language><managingEditor>vlad.karandashov.tech@gmail.com (Влад Карандашов)</managingEditor><webMaster>vlad.karandashov.tech@gmail.com (Влад Карандашов)</webMaster><lastBuildDate>Thu, 18 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://karandashov.com/tags/postgresql/index.xml" rel="self" type="application/rss+xml"/><item><title>Spring Boot 4, Flyway и Postgres: как не стрелять по ногам</title><link>https://karandashov.com/posts/2026-06-18-flyway/</link><pubDate>Thu, 18 Jun 2026 00:00:00 +0000</pubDate><author>vlad.karandashov.tech@gmail.com (Влад Карандашов)</author><guid>https://karandashov.com/posts/2026-06-18-flyway/</guid><description>
&lt;p&gt;&lt;figure&gt;
&lt;img src="https://karandashov.com/posts/2026-06-18-flyway/cover.png" title="Spring Boot 4, Flyway и PostgreSQL" alt="Spring Boot 4, Flyway и PostgreSQL" loading="lazy" /&gt;
&lt;figcaption&gt;Spring Boot 4, Flyway и PostgreSQL&lt;/figcaption&gt;
&lt;/figure&gt;&lt;/p&gt;
&lt;div class="hx:overflow-x-auto hx:mt-6 hx:flex hx:rounded-lg hx:border hx:py-2 hx:ltr:pr-4 hx:rtl:pl-4 hx:contrast-more:border-current hx:contrast-more:dark:border-current hx:border-blue-200 hx:bg-blue-100 hx:text-blue-900 hx:dark:border-blue-200/30 hx:dark:bg-blue-900/30 hx:dark:text-blue-200"&gt;
&lt;div class="hx:ltr:pl-3 hx:ltr:pr-2 hx:rtl:pr-3 hx:rtl:pl-2"&gt;&lt;svg height=1.2em class="hx:inline-block hx:align-middle" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"&gt;&lt;path stroke-linecap="round" stroke-linejoin="round" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/&gt;&lt;/svg&gt;&lt;/div&gt;
&lt;div class="hx:w-full hx:min-w-0 hx:leading-7"&gt;
&lt;div class="hx:mt-6 hx:leading-7 hx:first:mt-0"&gt;Эта статья была &lt;a
href="https://habr.com/ru/articles/1047382/"target="_blank" rel="noopener"&gt;опубликована на Habr&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;По Flyway миграциям уже написано много туториалов: как их называть, куда класть и зачем нужна таблица &lt;code&gt;flyway_schema_history&lt;/code&gt;.
Это полезно, но сегодня мы закопаемся поглубже в детали актуальной интеграции Flyway в приложение.&lt;/p&gt;
&lt;p&gt;В этой статье не будет пересказа базовых команд Flyway. Вместо этого разберем несколько интересных нюансов:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;что приносят и как устроены &lt;code&gt;spring-boot-starter-flyway&lt;/code&gt; и &lt;code&gt;spring-boot-starter-flyway-test&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;зачем нужны &lt;code&gt;flyway-database-postgresql&lt;/code&gt; и другие адаптеры;&lt;/li&gt;
&lt;li&gt;как настройка &lt;code&gt;transactional-lock&lt;/code&gt; влияет на нашу жизнь;&lt;/li&gt;
&lt;li&gt;какие бонусные фичи может нам предложить Flyway в Spring Boot сервисе.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Признаюсь честно: триггером к детальному разбору Flyway и его настройкам послужила весьма банальная проблема с зависшей миграцией.
Простое создание индекса намертво не давало запуститься моему сервису. Хочу поделиться этим опытом и сэкономить другим часы жизни.&lt;/p&gt;
&lt;p&gt;Постараемся внедрить Flyway в Spring Boot 4.x качественно! И посмотрим на интересные фичи.&lt;/p&gt;
&lt;div class="hextra-cards hx:mt-4 hx:gap-4 hx:grid not-prose" style="--hextra-cards-grid-cols: 3;"&gt;
&lt;a
class="hextra-card hx:group hx:flex hx:flex-col hx:justify-start hx:overflow-hidden hx:rounded-lg hx:border hx:border-gray-200 hx:text-current hx:no-underline hx:dark:shadow-none hx:hover:shadow-gray-100 hx:dark:hover:shadow-none hx:shadow-gray-100 hx:active:shadow-sm hx:active:shadow-gray-200 hx:transition-all hx:duration-200 hx:hover:border-gray-300 hx:bg-transparent hx:shadow-xs hx:dark:border-neutral-800 hx:hover:bg-slate-50 hx:hover:shadow-md hx:dark:hover:border-neutral-700 hx:dark:hover:bg-neutral-900"href="https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Migration-Guide"
target="_blank" rel="noreferrer"&gt;&lt;div class="hx:mt-auto"&gt;
&lt;span class="hextra-card-icon hx:flex hx:font-semibold hx:items-start hx:gap-2 hx:pt-4 hx:px-4 hx:text-gray-700 hx:hover:text-gray-900 hx:dark:text-neutral-200 hx:dark:hover:text-neutral-50"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"&gt;&lt;path stroke-linecap="round" stroke-linejoin="round" d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"/&gt;&lt;/svg&gt;Spring Boot 4 migration guide&lt;/span&gt;&lt;div class="hextra-card-subtitle hx:line-clamp-3 hx:text-sm hx:font-normal hx:text-gray-500 hx:dark:text-gray-400 hx:px-4 hx:mb-4 hx:mt-2"&gt;Контекст модульной перестройки starter&amp;rsquo;ов&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;
&lt;a
class="hextra-card hx:group hx:flex hx:flex-col hx:justify-start hx:overflow-hidden hx:rounded-lg hx:border hx:border-gray-200 hx:text-current hx:no-underline hx:dark:shadow-none hx:hover:shadow-gray-100 hx:dark:hover:shadow-none hx:shadow-gray-100 hx:active:shadow-sm hx:active:shadow-gray-200 hx:transition-all hx:duration-200 hx:hover:border-gray-300 hx:bg-transparent hx:shadow-xs hx:dark:border-neutral-800 hx:hover:bg-slate-50 hx:hover:shadow-md hx:dark:hover:border-neutral-700 hx:dark:hover:bg-neutral-900"href="https://documentation.red-gate.com/flyway/reference/database-driver-reference/postgresql-database"
target="_blank" rel="noreferrer"&gt;&lt;div class="hx:mt-auto"&gt;
&lt;span class="hextra-card-icon hx:flex hx:font-semibold hx:items-start hx:gap-2 hx:pt-4 hx:px-4 hx:text-gray-700 hx:hover:text-gray-900 hx:dark:text-neutral-200 hx:dark:hover:text-neutral-50"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"&gt;&lt;path stroke-linecap="round" stroke-linejoin="round" d="M4 7v10c0 2.21 3.582 4 8 4s8-1.79 8-4V7M4 7c0 2.21 3.582 4 8 4s8-1.79 8-4M4 7c0-2.21 3.582-4 8-4s8 1.79 8 4m0 5c0 2.21-3.582 4-8 4s-8-1.79-8-4"/&gt;&lt;/svg&gt;Flyway PostgreSQL&lt;/span&gt;&lt;div class="hextra-card-subtitle hx:line-clamp-3 hx:text-sm hx:font-normal hx:text-gray-500 hx:dark:text-gray-400 hx:px-4 hx:mb-4 hx:mt-2"&gt;Database plugin, locks и PostgreSQL-specific options&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;
&lt;a
class="hextra-card hx:group hx:flex hx:flex-col hx:justify-start hx:overflow-hidden hx:rounded-lg hx:border hx:border-gray-200 hx:text-current hx:no-underline hx:dark:shadow-none hx:hover:shadow-gray-100 hx:dark:hover:shadow-none hx:shadow-gray-100 hx:active:shadow-sm hx:active:shadow-gray-200 hx:transition-all hx:duration-200 hx:hover:border-gray-300 hx:bg-transparent hx:shadow-xs hx:dark:border-neutral-800 hx:hover:bg-slate-50 hx:hover:shadow-md hx:dark:hover:border-neutral-700 hx:dark:hover:bg-neutral-900"href="https://www.postgresql.org/docs/current/sql-createindex.html"
target="_blank" rel="noreferrer"&gt;&lt;div class="hx:mt-auto"&gt;
&lt;span class="hextra-card-icon hx:flex hx:font-semibold hx:items-start hx:gap-2 hx:pt-4 hx:px-4 hx:text-gray-700 hx:hover:text-gray-900 hx:dark:text-neutral-200 hx:dark:hover:text-neutral-50"&gt;&lt;svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"&gt;&lt;path stroke-linecap="round" stroke-linejoin="round" d="M10 20l4-16m4 4l4 4-4 4M6 16l-4-4 4-4"/&gt;&lt;/svg&gt;CREATE INDEX&lt;/span&gt;&lt;div class="hextra-card-subtitle hx:line-clamp-3 hx:text-sm hx:font-normal hx:text-gray-500 hx:dark:text-gray-400 hx:px-4 hx:mb-4 hx:mt-2"&gt;Ограничения PostgreSQL для concurrent index build&lt;/div&gt;&lt;/div&gt;&lt;/a&gt;
&lt;/div&gt;
&lt;h2&gt;Spring Boot 4.x и новые стартеры&lt;span class="hx:absolute hx:-mt-20" id="spring-boot-4x-и-новые-стартеры"&gt;&lt;/span&gt;
&lt;a href="#spring-boot-4x-%d0%b8-%d0%bd%d0%be%d0%b2%d1%8b%d0%b5-%d1%81%d1%82%d0%b0%d1%80%d1%82%d0%b5%d1%80%d1%8b" class="subheading-anchor" aria-label="Ссылка на этот раздел"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Начинаем с базы. Раньше требовалось подключить &lt;code&gt;flyway-core&lt;/code&gt; + &lt;code&gt;flyway-database-postgresql&lt;/code&gt; (или другой адаптер).
Но в Spring Boot 4.x изменилась модель зависимостей: многие интеграции получили собственные main/test starters.
Теперь используется отдельный &lt;code&gt;spring-boot-starter-flyway&lt;/code&gt;, это часть &lt;a
href="https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-4.0-Migration-Guide"target="_blank" rel="noopener"&gt;общей модульной перестройки SB4&lt;/a&gt;:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div class="hextra-code-filename not-prose" dir="auto"&gt;build.gradle.kts&lt;/div&gt;&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;dependencies {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; implementation(&lt;span style="color:#e6db74"&gt;&amp;#34;org.springframework.boot:spring-boot-starter-flyway&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; runtimeOnly(&lt;span style="color:#e6db74"&gt;&amp;#34;org.flywaydb:flyway-database-postgresql&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; testImplementation(&lt;span style="color:#e6db74"&gt;&amp;#34;org.springframework.boot:spring-boot-starter-flyway-test&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-8"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Скопировать код"
aria-label="Скопировать код"
data-copied-label="Скопировано!"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Если заглянуть в зависимости starter&amp;rsquo;а, там нет большого сюрприза:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div class="hextra-code-filename not-prose" dir="auto"&gt;spring-boot-starter-flyway&lt;/div&gt;&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;spring-boot-starter-flyway
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ├─ spring-boot-starter
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ├─ spring-boot-starter-jdbc
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ├─ spring-boot-flyway
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; └─ spring-boot-jdbc&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-8"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Скопировать код"
aria-label="Скопировать код"
data-copied-label="Скопировано!"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Auto-configuration включается при нескольких условиях:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;в classpath есть &lt;code&gt;org.flywaydb.core.Flyway&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;есть подходящий &lt;code&gt;DataSource&lt;/code&gt; или отдельные connection details для Flyway;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;spring.flyway.enabled&lt;/code&gt; не выключен.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;После этого Boot не просто вызывает &lt;code&gt;Flyway.configure()&lt;/code&gt; и запускает миграции. Он собирает полноценный &lt;code&gt;Flyway&lt;/code&gt; bean из
нескольких источников конфигурации.
Происходит примерно следующее:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;берется основной &lt;code&gt;DataSource&lt;/code&gt; приложения или отдельный &lt;code&gt;DataSource&lt;/code&gt;, указанный для Flyway;&lt;/li&gt;
&lt;li&gt;читаются значения из &lt;code&gt;spring.flyway.*&lt;/code&gt; настроек;&lt;/li&gt;
&lt;li&gt;из Spring context подхватываются callbacks и Java migrations;&lt;/li&gt;
&lt;li&gt;пользовательские &lt;code&gt;FlywayConfigurationCustomizer&lt;/code&gt; получают последний шанс изменить конфигурацию;&lt;/li&gt;
&lt;li&gt;после этого создается готовый объект &lt;code&gt;Flyway&lt;/code&gt;, который Spring Boot сможет запустить при старте приложения.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Callbacks и Java migrations можно подключать как Spring beans (об этом ниже). Это удобно, когда callback должен использовать
инфраструктуру приложения.&lt;/p&gt;
&lt;h2&gt;Зачем нужен &lt;code&gt;flyway-database-postgresql&lt;/code&gt;&lt;span class="hx:absolute hx:-mt-20" id="зачем-нужен-flyway-database-postgresql"&gt;&lt;/span&gt;
&lt;a href="#%d0%b7%d0%b0%d1%87%d0%b5%d0%bc-%d0%bd%d1%83%d0%b6%d0%b5%d0%bd-flyway-database-postgresql" class="subheading-anchor" aria-label="Ссылка на этот раздел"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Начиная с новых версий Flyway поддержка многих баз &lt;a
href="https://documentation.red-gate.com/flyway/reference/database-driver-reference/postgresql-database"target="_blank" rel="noopener"&gt;вынесена в отдельные plugin modules&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Этот модуль:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;регистрирует PostgreSQL database type как Flyway plugin;&lt;/li&gt;
&lt;li&gt;содержит PostgreSQL parser;&lt;/li&gt;
&lt;li&gt;знает особенности PostgreSQL DDL;&lt;/li&gt;
&lt;li&gt;определяет, какие statements могут выполняться в транзакции;&lt;/li&gt;
&lt;li&gt;реализует PostgreSQL advisory lock для &lt;code&gt;flyway_schema_history&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;предоставляет &lt;code&gt;PostgreSQLConfigurationExtension&lt;/code&gt;, куда Spring Boot передает &lt;code&gt;spring.flyway.postgresql.*&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Например, в &lt;code&gt;PostgreSQLParser&lt;/code&gt; есть отдельное правило для &lt;code&gt;CREATE INDEX CONCURRENTLY&lt;/code&gt;:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div class="hextra-code-filename not-prose" dir="auto"&gt;PostgreSQLParser.java&lt;/div&gt;&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;private&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;static&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; Pattern CREATE_INDEX_CONCURRENTLY_REGEX &lt;span style="color:#f92672"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Pattern.&lt;span style="color:#a6e22e"&gt;compile&lt;/span&gt;(&lt;span style="color:#e6db74"&gt;&amp;#34;^(CREATE|DROP)( UNIQUE)? INDEX CONCURRENTLY&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;protected&lt;/span&gt; Boolean &lt;span style="color:#a6e22e"&gt;detectCanExecuteInTransaction&lt;/span&gt;(String simplifiedStatement, List&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;lt;Token&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;gt; keywords) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (CREATE_INDEX_CONCURRENTLY_REGEX.&lt;span style="color:#a6e22e"&gt;matcher&lt;/span&gt;(simplifiedStatement).&lt;span style="color:#a6e22e"&gt;matches&lt;/span&gt;() &lt;span style="color:#75715e"&gt;/* ... */&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// ...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-8"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Скопировать код"
aria-label="Скопировать код"
data-copied-label="Скопировано!"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;То есть Flyway не просто отправляет SQL как строку в JDBC. Он парсит миграцию, понимает типы statements и строит модель
выполнения. Это особенно важно для PostgreSQL, где большинство DDL можно откатить транзакцией, но некоторые команды
принципиально должны идти вне transaction block.&lt;/p&gt;
&lt;h2&gt;Базовая настройка в приложении&lt;span class="hx:absolute hx:-mt-20" id="базовая-настройка-в-приложении"&gt;&lt;/span&gt;
&lt;a href="#%d0%b1%d0%b0%d0%b7%d0%be%d0%b2%d0%b0%d1%8f-%d0%bd%d0%b0%d1%81%d1%82%d1%80%d0%be%d0%b9%d0%ba%d0%b0-%d0%b2-%d0%bf%d1%80%d0%b8%d0%bb%d0%be%d0%b6%d0%b5%d0%bd%d0%b8%d0%b8" class="subheading-anchor" aria-label="Ссылка на этот раздел"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Если &lt;code&gt;spring.flyway.url&lt;/code&gt; не задан, Boot использует основной &lt;code&gt;DataSource&lt;/code&gt;.
Но можно и разнести доступы:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div class="hextra-code-filename not-prose" dir="auto"&gt;application.yaml&lt;/div&gt;&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spring&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;flyway&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;url&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;${DATABASE_URL}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;user&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;${DATABASE_USERNAME}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;password&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;${DATABASE_PASSWORD}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;default-schema&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;${DATABASE_SCHEMA}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-8"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Скопировать код"
aria-label="Скопировать код"
data-copied-label="Скопировано!"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Еще одна полезная деталь — &lt;code&gt;{vendor}&lt;/code&gt; в &lt;code&gt;locations&lt;/code&gt;:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div class="hextra-code-filename not-prose" dir="auto"&gt;application.yaml&lt;/div&gt;&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spring&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;flyway&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;locations&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;classpath:db/migration/common&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;classpath:db/migration/{vendor}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-8"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Скопировать код"
aria-label="Скопировать код"
data-copied-label="Скопировано!"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Внутри &lt;code&gt;FlywayAutoConfiguration.LocationResolver&lt;/code&gt; Spring Boot определяет vendor по JDBC URL и заменяет &lt;code&gt;{vendor}&lt;/code&gt; на
идентификатор базы. Для PostgreSQL это будет &lt;code&gt;postgresql&lt;/code&gt;. При аккуратном использовании это позволяет держать общие
миграции отдельно от database-specific SQL. Но злоупотреблять этим не стоит: если приложение официально поддерживает
только PostgreSQL, отдельная папка &lt;code&gt;postgresql&lt;/code&gt; может создать лишнюю иллюзию переносимости.&lt;/p&gt;
&lt;h2&gt;Что дает &lt;code&gt;spring-boot-starter-flyway-test&lt;/code&gt;&lt;span class="hx:absolute hx:-mt-20" id="что-дает-spring-boot-starter-flyway-test"&gt;&lt;/span&gt;
&lt;a href="#%d1%87%d1%82%d0%be-%d0%b4%d0%b0%d0%b5%d1%82-spring-boot-starter-flyway-test" class="subheading-anchor" aria-label="Ссылка на этот раздел"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;В Spring Boot 4.x у многих технологий появился companion test starter. Для Flyway это:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div class="hextra-code-filename not-prose" dir="auto"&gt;build.gradle.kts&lt;/div&gt;&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-kotlin" data-lang="kotlin"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;testImplementation(&lt;span style="color:#e6db74"&gt;&amp;#34;org.springframework.boot:spring-boot-starter-flyway-test&amp;#34;&lt;/span&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-8"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Скопировать код"
aria-label="Скопировать код"
data-copied-label="Скопировано!"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Это не отдельный большой фреймворк для тестирования миграций, просто агрегация:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div class="hextra-code-filename not-prose" dir="auto"&gt;spring-boot-starter-flyway-test&lt;/div&gt;&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;spring-boot-starter-flyway
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;spring-boot-starter-test
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;spring-boot-starter-jdbc-test&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-8"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Скопировать код"
aria-label="Скопировать код"
data-copied-label="Скопировано!"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;То есть он подтягивает обычную test-инфраструктуру Boot, JDBC test support и сам Flyway starter. Это соответствует новой
модели Spring Boot 4.x: в тестах подключаем starter&amp;rsquo;ы тех технологий, которые реально находятся под тестом.&lt;/p&gt;
&lt;p&gt;Если ваше приложение и так живет в экосистеме Spring Boot: подключать дополнительно &lt;code&gt;spring-boot-starter-flyway-test&lt;/code&gt; едва ли потребуется.&lt;/p&gt;
&lt;h2&gt;Главный кейс: &lt;code&gt;CREATE INDEX CONCURRENTLY&lt;/code&gt;&lt;span class="hx:absolute hx:-mt-20" id="главный-кейс-create-index-concurrently"&gt;&lt;/span&gt;
&lt;a href="#%d0%b3%d0%bb%d0%b0%d0%b2%d0%bd%d1%8b%d0%b9-%d0%ba%d0%b5%d0%b9%d1%81-create-index-concurrently" class="subheading-anchor" aria-label="Ссылка на этот раздел"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;В PostgreSQL создание индекса — не всегда безобидная операция. Обычный &lt;code&gt;CREATE INDEX&lt;/code&gt; блокирует записи в таблицу на
время построения индекса. Для больших production-таблиц это может быть неприемлемо, поэтому PostgreSQL поддерживает:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div class="hextra-code-filename not-prose" dir="auto"&gt;V13__idx_orders_created_at_concurrently.sql&lt;/div&gt;&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;CREATE&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;INDEX&lt;/span&gt; CONCURRENTLY idx_orders_created_at
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ON&lt;/span&gt; orders (created_at);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-8"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Скопировать код"
aria-label="Скопировать код"
data-copied-label="Скопировано!"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;В документации &lt;a
href="https://www.postgresql.org/docs/current/sql-createindex.html"target="_blank" rel="noopener"&gt;PostgreSQL 18 по &lt;code&gt;CREATE INDEX&lt;/code&gt;&lt;/a&gt; описаны
компромиссы: concurrent build не блокирует обычные insert/update/delete, но требует больше работы, выполняет несколько
проходов по таблице, ждет старые транзакции и при ошибке может оставить invalid index, который придется отдельно удалить
или перестроить.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ключевое ограничение: &lt;code&gt;CREATE INDEX CONCURRENTLY&lt;/code&gt; нельзя выполнять внутри transaction block&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Flyway это знает. Как было показано выше, PostgreSQL parser помечает такой statement как non-transactional. Кажется, на
этом проблема должна закончиться: Flyway увидел non-transactional migration и запустил ее вне транзакции.&lt;/p&gt;
&lt;div class="hx:overflow-x-auto hx:mt-6 hx:flex hx:rounded-lg hx:border hx:py-2 hx:ltr:pr-4 hx:rtl:pl-4 hx:contrast-more:border-current hx:contrast-more:dark:border-current hx:border-amber-200 hx:bg-amber-100 hx:text-amber-900 hx:dark:border-amber-200/30 hx:dark:bg-amber-900/30 hx:dark:text-amber-200"&gt;
&lt;div class="hx:ltr:pl-3 hx:ltr:pr-2 hx:rtl:pr-3 hx:rtl:pl-2"&gt;&lt;svg height=1.2em class="hx:inline-block hx:align-middle" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"&gt;&lt;path stroke-linecap="round" stroke-linejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/&gt;&lt;/svg&gt;&lt;/div&gt;
&lt;div class="hx:w-full hx:min-w-0 hx:leading-7"&gt;
&lt;div class="hx:mt-6 hx:leading-7 hx:first:mt-0"&gt;Но есть второй уровень — lock самого Flyway. Именно он часто оказывается неочевидной причиной зависшей или упавшей PostgreSQL-миграции.&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Перед миграцией Flyway должен защититься от ситуации, когда два экземпляра приложения одновременно начинают менять
схему. В PostgreSQL для этого используется advisory lock. В &lt;code&gt;flyway-database-postgresql&lt;/code&gt; это класс:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div class="hextra-code-filename not-prose" dir="auto"&gt;PostgreSQLAdvisoryLockTemplate&lt;/div&gt;&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;org.flywaydb.database.postgresql.PostgreSQLAdvisoryLockTemplate&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-8"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Скопировать код"
aria-label="Скопировать код"
data-copied-label="Скопировано!"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Сильно упрощенный фрагмент:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div class="hextra-code-filename not-prose" dir="auto"&gt;PostgreSQLAdvisoryLockTemplate.java&lt;/div&gt;&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;lt;T&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;gt; T &lt;span style="color:#a6e22e"&gt;execute&lt;/span&gt;(Callable&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;lt;T&lt;span style="color:#f92672"&gt;&amp;amp;&lt;/span&gt;gt; callable) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; PostgreSQLConfigurationExtension extension &lt;span style="color:#f92672"&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; configuration.&lt;span style="color:#a6e22e"&gt;getPluginRegister&lt;/span&gt;().&lt;span style="color:#a6e22e"&gt;getExact&lt;/span&gt;(PostgreSQLConfigurationExtension.&lt;span style="color:#a6e22e"&gt;class&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (extension.&lt;span style="color:#a6e22e"&gt;isTransactionalLock&lt;/span&gt;()) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;new&lt;/span&gt; TransactionalExecutionTemplate(jdbcTemplate.&lt;span style="color:#a6e22e"&gt;getConnection&lt;/span&gt;(), &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; .&lt;span style="color:#a6e22e"&gt;execute&lt;/span&gt;(() &lt;span style="color:#f92672"&gt;-&amp;gt;&lt;/span&gt; execute(callable, &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;::tryLockTransactional));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;else&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; execute(callable, &lt;span style="color:#66d9ef"&gt;this&lt;/span&gt;::tryLock);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;finally&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; unlock(rethrow);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-8"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Скопировать код"
aria-label="Скопировать код"
data-copied-label="Скопировано!"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;А внутри:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div class="hextra-code-filename not-prose" dir="auto"&gt;PostgreSQL advisory locks&lt;/div&gt;&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;SELECT&lt;/span&gt; pg_try_advisory_xact_lock(...)
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;--или
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;SELECT&lt;/span&gt; pg_try_advisory_lock(...)&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-8"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Скопировать код"
aria-label="Скопировать код"
data-copied-label="Скопировано!"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Разница принципиальная. PostgreSQL advisory locks бывают transaction-level и session-level.
В &lt;a
href="https://www.postgresql.org/docs/current/explicit-locking.html"target="_blank" rel="noopener"&gt;документации PostgreSQL 18&lt;/a&gt; это описано так:
transaction-level lock автоматически освобождается в конце транзакции, а session-level lock живет до явного unlock или
завершения сессии.&lt;/p&gt;
&lt;p&gt;По умолчанию Flyway для PostgreSQL использует transactional lock. Это удобно для большинства миграций: lock привязан к
транзакции, rollback гарантированно освобождает его. Но для &lt;code&gt;CREATE INDEX CONCURRENTLY&lt;/code&gt; такой подход может стать
проблемой.&lt;/p&gt;
&lt;p&gt;Почему? Потому что lock template сам открывает транзакционную обертку вокруг участка, внутри которого Flyway применяет
миграцию. Даже если сама SQL-миграция распознана как non-transactional, внешний transaction-level advisory lock уже
создал transaction block. Для PostgreSQL этого достаточно, чтобы &lt;code&gt;CREATE INDEX CONCURRENTLY&lt;/code&gt; оказался в недопустимом
контексте.&lt;/p&gt;
&lt;p&gt;Именно поэтому у Flyway есть PostgreSQL-specific настройка:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div class="hextra-code-filename not-prose" dir="auto"&gt;application.yaml&lt;/div&gt;&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spring&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;flyway&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;postgresql&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;transactional-lock&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-8"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Скопировать код"
aria-label="Скопировать код"
data-copied-label="Скопировано!"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;В терминах чистого Flyway это соответствует:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div class="hextra-code-filename not-prose" dir="auto"&gt;flyway.conf&lt;/div&gt;&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-properties" data-lang="properties"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;flyway.postgresql.transactional.lock&lt;/span&gt;&lt;span style="color:#f92672"&gt;=&lt;/span&gt;&lt;span style="color:#e6db74"&gt;false&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-8"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Скопировать код"
aria-label="Скопировать код"
data-copied-label="Скопировано!"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Redgate в &lt;a
href="https://documentation.red-gate.com/flyway/reference/database-driver-reference/postgresql-database"target="_blank" rel="noopener"&gt;документации по PostgreSQL&lt;/a&gt;
прямо указывает этот кейс: transactional lock по умолчанию может создавать проблемы с некоторыми SQL statements,
особенно с &lt;code&gt;CREATE INDEX CONCURRENTLY&lt;/code&gt; — в таком случае его заменяют session-level lock.&lt;/p&gt;
&lt;p&gt;После переключения Flyway все еще сериализует миграции advisory lock&amp;rsquo;ом, но больше не заворачивает выполнение в
transaction-level lock. &lt;code&gt;CREATE INDEX CONCURRENTLY&lt;/code&gt; получает нужное ему условие: отсутствие внешнего transaction block.&lt;/p&gt;
&lt;h3&gt;Нужно ли всегда делать &lt;code&gt;transactional-lock: false&lt;/code&gt;&lt;span class="hx:absolute hx:-mt-20" id="нужно-ли-всегда-делать-transactional-lock-false"&gt;&lt;/span&gt;
&lt;a href="#%d0%bd%d1%83%d0%b6%d0%bd%d0%be-%d0%bb%d0%b8-%d0%b2%d1%81%d0%b5%d0%b3%d0%b4%d0%b0-%d0%b4%d0%b5%d0%bb%d0%b0%d1%82%d1%8c-transactional-lock-false" class="subheading-anchor" aria-label="Ссылка на этот раздел"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Короткий ответ: нет, но для PostgreSQL-проектов с online DDL это нормальная осознанная настройка.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style="text-align: left"&gt;Сценарий&lt;/th&gt;
&lt;th style="text-align: left"&gt;Что выбрать&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style="text-align: left"&gt;Все миграции транзакционные&lt;/td&gt;
&lt;td style="text-align: left"&gt;Можно оставить default &lt;code&gt;transactional-lock=true&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left"&gt;Есть &lt;code&gt;CREATE INDEX CONCURRENTLY&lt;/code&gt;, &lt;code&gt;VACUUM&lt;/code&gt;, &lt;code&gt;REINDEX&lt;/code&gt; и похожие statements&lt;/td&gt;
&lt;td style="text-align: left"&gt;Рассмотреть &lt;code&gt;spring.flyway.postgresql.transactional-lock=false&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style="text-align: left"&gt;Несколько реплик стартуют одновременно&lt;/td&gt;
&lt;td style="text-align: left"&gt;Дополнительно проверить &lt;code&gt;spring.flyway.lock-retry-count&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Если в проекте все миграции транзакционные, дефолтный transactional lock хорош: он проще в плане жизненного цикла и
автоматически освобождается вместе с транзакцией. Это надежный default.&lt;/p&gt;
&lt;p&gt;Если у вас есть или планируются миграции с:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CREATE INDEX CONCURRENTLY&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;DROP INDEX CONCURRENTLY&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;некоторыми вариантами &lt;code&gt;REINDEX&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;VACUUM&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ALTER SYSTEM&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;другими командами, которые PostgreSQL запрещает внутри transaction block,&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;то &lt;code&gt;spring.flyway.postgresql.transactional-lock=false&lt;/code&gt; стоит рассматривать как часть стандартной конфигурации.&lt;/p&gt;
&lt;p&gt;Компромисс такой: session-level lock требует явного освобождения. Flyway делает &lt;code&gt;pg_advisory_unlock(...)&lt;/code&gt; в &lt;code&gt;finally&lt;/code&gt;, а
PostgreSQL дополнительно освобождает session locks при завершении соединения. Это достаточно надежно, но семантически
уже не так чисто, как transaction-level lock. Если соединение остается жить в pool, а unlock по какой-то причине не
прошел, lock может прожить дольше, чем хотелось бы. Это редкий сценарий, но именно поэтому default остается
консервативным.&lt;/p&gt;
&lt;p&gt;Отдельно стоит подчеркнуть: &lt;code&gt;spring.flyway.execute-in-transaction=false&lt;/code&gt; не является полным заменителем
&lt;code&gt;postgresql.transactional-lock=false&lt;/code&gt;. Первое управляет тем, будет ли Flyway выполнять SQL migrations внутри транзакции.
Второе управляет тем, как Flyway берет lock вокруг миграционного процесса. Для &lt;code&gt;CREATE INDEX CONCURRENTLY&lt;/code&gt; важны оба
уровня, и ловушка часто находится именно во втором.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;group&lt;/code&gt;, &lt;code&gt;mixed&lt;/code&gt; и транзакционная модель&lt;span class="hx:absolute hx:-mt-20" id="group-mixed-и-транзакционная-модель"&gt;&lt;/span&gt;
&lt;a href="#group-mixed-%d0%b8-%d1%82%d1%80%d0%b0%d0%bd%d0%b7%d0%b0%d0%ba%d1%86%d0%b8%d0%be%d0%bd%d0%bd%d0%b0%d1%8f-%d0%bc%d0%be%d0%b4%d0%b5%d0%bb%d1%8c" class="subheading-anchor" aria-label="Ссылка на этот раздел"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Вокруг этого кейса легко перепутать несколько похожих настроек.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;spring.flyway.group=true&lt;/code&gt; говорит Flyway попытаться применить все pending migrations в одной транзакции.
В &lt;a
href="https://documentation.red-gate.com/flyway/reference/configuration/flyway-namespace/flyway-group-setting"target="_blank" rel="noopener"&gt;документации Flyway&lt;/a&gt;
эта настройка рекомендуется только для баз с нормальной поддержкой DDL transactions. PostgreSQL такую поддержку в целом
имеет, но не для всех statements. Если в наборе pending migrations есть &lt;code&gt;CREATE INDEX CONCURRENTLY&lt;/code&gt;, &lt;code&gt;group=true&lt;/code&gt; почти
наверняка не то, что вам нужно.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;spring.flyway.mixed=true&lt;/code&gt; разрешает смешивать transactional и non-transactional statements в одной migration или
migration group. По умолчанию &lt;code&gt;false&lt;/code&gt;, и это хорошо. Если Flyway видит, что в одном SQL-файле смешаны обычный
DDL и &lt;code&gt;CREATE INDEX CONCURRENTLY&lt;/code&gt;, он остановится и скажет об этом. Это раздражает ровно до момента, пока не вспомнить,
что rollback у такой миграции будет частичным или невозможным.&lt;/p&gt;
&lt;p&gt;Практическое правило простое: non-transactional PostgreSQL statements лучше выносить в отдельную миграцию:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div class="hextra-code-filename not-prose" dir="auto"&gt;db/migration&lt;/div&gt;&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;-- V12__add_orders_status.sql
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;ALTER&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;TABLE&lt;/span&gt; orders
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ADD&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;COLUMN&lt;/span&gt; status VARCHAR(&lt;span style="color:#ae81ff"&gt;32&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#75715e"&gt;-- V13__idx_orders_status_concurrently.sql
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;CREATE&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;INDEX&lt;/span&gt; CONCURRENTLY &lt;span style="color:#66d9ef"&gt;IF&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;NOT&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;EXISTS&lt;/span&gt; idx_orders_status
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;ON&lt;/span&gt; orders (status);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-8"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Скопировать код"
aria-label="Скопировать код"
data-copied-label="Скопировано!"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Так проще читать историю, проще понимать rollback-поведение и проще диагностировать сбои.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;spring.flyway.lock-retry-count&lt;/code&gt; — еще одна настройка рядом с этой темой. Flyway берет lock, чтобы параллельные инстансы
не применяли миграции одновременно. Если lock сразу недоступен, он повторяет попытки.
В &lt;a
href="https://documentation.red-gate.com/flyway/reference/configuration/flyway-namespace/flyway-lock-retry-count-setting"target="_blank" rel="noopener"&gt;документации Flyway&lt;/a&gt;
default — &lt;code&gt;50&lt;/code&gt;, попытки идут с интервалом около секунды. В Kubernetes или другом окружении с несколькими стартующими
репликами это может быть важнее, чем кажется: одна реплика мигрирует, остальные ждут lock. Если миграции тяжелые, 50
секунд может быть мало.&lt;/p&gt;
&lt;h2&gt;Несколько свойств, которые стоит знать&lt;span class="hx:absolute hx:-mt-20" id="несколько-свойств-которые-стоит-знать"&gt;&lt;/span&gt;
&lt;a href="#%d0%bd%d0%b5%d1%81%d0%ba%d0%be%d0%bb%d1%8c%d0%ba%d0%be-%d1%81%d0%b2%d0%be%d0%b9%d1%81%d1%82%d0%b2-%d0%ba%d0%be%d1%82%d0%be%d1%80%d1%8b%d0%b5-%d1%81%d1%82%d0%be%d0%b8%d1%82-%d0%b7%d0%bd%d0%b0%d1%82%d1%8c" class="subheading-anchor" aria-label="Ссылка на этот раздел"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;code&gt;FlywayProperties&lt;/code&gt; большой. Делать из статьи полный справочник бессмысленно, но есть настройки, которые регулярно
всплывают в реальных проектах.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;baseline-on-migrate&lt;/code&gt;&lt;span class="hx:absolute hx:-mt-20" id="baseline-on-migrate"&gt;&lt;/span&gt;
&lt;a href="#baseline-on-migrate" class="subheading-anchor" aria-label="Ссылка на этот раздел"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div class="hextra-code-filename not-prose" dir="auto"&gt;application.yaml&lt;/div&gt;&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spring&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;flyway&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;baseline-on-migrate&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-8"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Скопировать код"
aria-label="Скопировать код"
data-copied-label="Скопировано!"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Это автоматический &lt;code&gt;baseline&lt;/code&gt; при &lt;code&gt;migrate&lt;/code&gt;, если схема непустая, но таблицы history еще нет.
В &lt;a
href="https://documentation.red-gate.com/flyway/reference/configuration/flyway-namespace/flyway-baseline-on-migrate-setting"target="_blank" rel="noopener"&gt;официальной документации&lt;/a&gt;
отдельно предупреждают: настройка удобна для первичного внедрения Flyway в существующую production-базу, но убирает
safety net, который защищает от миграции не той базы при ошибке конфигурации.&lt;/p&gt;
&lt;div class="hx:overflow-x-auto hx:mt-6 hx:flex hx:rounded-lg hx:border hx:py-2 hx:ltr:pr-4 hx:rtl:pl-4 hx:contrast-more:border-current hx:contrast-more:dark:border-current hx:border-purple-200 hx:bg-purple-100 hx:text-purple-900 hx:dark:border-purple-200/30 hx:dark:bg-purple-900/30 hx:dark:text-purple-200"&gt;
&lt;div class="hx:ltr:pl-3 hx:ltr:pr-2 hx:rtl:pr-3 hx:rtl:pl-2"&gt;&lt;svg height=1.2em class="hx:inline-block hx:align-middle" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"&gt;&lt;path stroke-linecap="round" stroke-linejoin="round" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/&gt;&lt;/svg&gt;&lt;/div&gt;
&lt;div class="hx:w-full hx:min-w-0 hx:leading-7"&gt;
&lt;div class="hx:mt-6 hx:leading-7 hx:first:mt-0"&gt;&lt;code&gt;baseline-on-migrate&lt;/code&gt; удобно включать при внедрении Flyway в уже существующую базу, но это не настройка &amp;ldquo;на всякий случай&amp;rdquo;: она ослабляет защиту от ошибки окружения.&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Важно не путать:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;baseline&lt;/code&gt; command — зарегистрировать существующее состояние схемы как стартовую точку;&lt;/li&gt;
&lt;li&gt;baseline migration — отдельный тип миграционного скрипта, который может быстро создать новую базу с некоторой
версии.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Кстати, про baseline migrations на Хабре недавно был хороший материал: &lt;a
href="https://habr.com/ru/companies/spring_aio/articles/880486/"target="_blank" rel="noopener"&gt;Flyway Baseline миграция без лишних слов&lt;/a&gt;.
При настройке Boot достаточно понимать, что &lt;code&gt;baseline-on-migrate&lt;/code&gt; автоматизирует baseline для непустой схемы без history table.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;clean-disabled&lt;/code&gt;&lt;span class="hx:absolute hx:-mt-20" id="clean-disabled"&gt;&lt;/span&gt;
&lt;a href="#clean-disabled" class="subheading-anchor" aria-label="Ссылка на этот раздел"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div class="hextra-code-filename not-prose" dir="auto"&gt;application.yaml&lt;/div&gt;&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spring&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;flyway&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;clean-disabled&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-8"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Скопировать код"
aria-label="Скопировать код"
data-copied-label="Скопировано!"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;В Spring Boot default — &lt;code&gt;true&lt;/code&gt;, и это правильно. &lt;code&gt;clean&lt;/code&gt; удаляет объекты схемы, которыми управляет Flyway. В production
это почти всегда команда из категории &amp;ldquo;нельзя случайно&amp;rdquo;. Для локальной разработки или тестов можно отключать защиту в
отдельном profile, но включать &lt;code&gt;clean&lt;/code&gt; в общем конфиге приложения — плохой сигнал.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;out-of-order&lt;/code&gt;&lt;span class="hx:absolute hx:-mt-20" id="out-of-order"&gt;&lt;/span&gt;
&lt;a href="#out-of-order" class="subheading-anchor" aria-label="Ссылка на этот раздел"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div class="hextra-code-filename not-prose" dir="auto"&gt;application.yaml&lt;/div&gt;&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spring&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;flyway&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;out-of-order&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-8"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Скопировать код"
aria-label="Скопировать код"
data-copied-label="Скопировано!"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Разрешает применить миграцию с версией ниже текущей, если она появилась позже. Например, production уже на &lt;code&gt;V10&lt;/code&gt;, а из
долгоживущей ветки приехала &lt;code&gt;V8_1&lt;/code&gt;. С &lt;code&gt;out-of-order=false&lt;/code&gt; Flyway откажется. С &lt;code&gt;true&lt;/code&gt; применит.&lt;/p&gt;
&lt;p&gt;Это может помочь командам с параллельными release branches, но делает историю менее линейной. Если вы включаете
&lt;code&gt;out-of-order&lt;/code&gt;, это должно быть частью release-процесса, а не способом регулярно &amp;ldquo;как-нибудь пронести&amp;rdquo; забытые миграции.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;validate-migration-naming&lt;/code&gt;&lt;span class="hx:absolute hx:-mt-20" id="validate-migration-naming"&gt;&lt;/span&gt;
&lt;a href="#validate-migration-naming" class="subheading-anchor" aria-label="Ссылка на этот раздел"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div class="hextra-code-filename not-prose" dir="auto"&gt;application.yaml&lt;/div&gt;&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spring&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;flyway&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;validate-migration-naming&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-8"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Скопировать код"
aria-label="Скопировать код"
data-copied-label="Скопировано!"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;По умолчанию Boot оставляет &lt;code&gt;false&lt;/code&gt;. В реальной работе я бы рекомендовал ставить &lt;code&gt;true&lt;/code&gt;: лучше упасть на старте из-за файла
&lt;code&gt;stupid_init.sql&lt;/code&gt;, чем тихо проигнорировать миграцию с неправильным именем и получить расхождение окружений.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;ignore-migration-patterns&lt;/code&gt;&lt;span class="hx:absolute hx:-mt-20" id="ignore-migration-patterns"&gt;&lt;/span&gt;
&lt;a href="#ignore-migration-patterns" class="subheading-anchor" aria-label="Ссылка на этот раздел"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Default в Boot — &lt;code&gt;*:future&lt;/code&gt;. Это означает, что future migrations не валят validation. Future migration — запись в
history table, версия которой выше, чем все известные локальному приложению миграции. Такое бывает при rollback
приложения на предыдущую версию кода.&lt;/p&gt;
&lt;p&gt;Default разумный для rolling deployments: старая версия приложения может стартовать против базы, которую уже мигрировала
новая версия. Но если у вас строгая модель &amp;ldquo;код и база всегда разворачиваются атомарно&amp;rdquo;, это поведение можно
пересмотреть.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;target&lt;/code&gt;&lt;span class="hx:absolute hx:-mt-20" id="target"&gt;&lt;/span&gt;
&lt;a href="#target" class="subheading-anchor" aria-label="Ссылка на этот раздел"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div class="hextra-code-filename not-prose" dir="auto"&gt;application.yaml&lt;/div&gt;&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spring&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;flyway&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;target&lt;/span&gt;: &lt;span style="color:#e6db74"&gt;&amp;#34;12&amp;#34;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-8"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Скопировать код"
aria-label="Скопировать код"
data-copied-label="Скопировано!"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Ограничивает версию, до которой Flyway должен мигрировать. В обычном приложении почти всегда &lt;code&gt;latest&lt;/code&gt;. Но &lt;code&gt;target&lt;/code&gt;
полезен в тестах, при воспроизведении проблем и в редких staged rollout сценариях, когда нужно поднять базу только до
конкретного состояния.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;placeholders&lt;/code&gt;&lt;span class="hx:absolute hx:-mt-20" id="placeholders"&gt;&lt;/span&gt;
&lt;a href="#placeholders" class="subheading-anchor" aria-label="Ссылка на этот раздел"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div class="hextra-code-filename not-prose" dir="auto"&gt;application.yaml&lt;/div&gt;&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spring&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;flyway&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;placeholders&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;app_schema&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;app&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-8"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Скопировать код"
aria-label="Скопировать код"
data-copied-label="Скопировано!"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;В миграции:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div class="hextra-code-filename not-prose" dir="auto"&gt;V14__create_events.sql&lt;/div&gt;&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;CREATE&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;TABLE&lt;/span&gt; &lt;span style="color:#960050;background-color:#1e0010"&gt;${&lt;/span&gt;app_schema&lt;span style="color:#960050;background-color:#1e0010"&gt;}&lt;/span&gt;.events
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;(...);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-8"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Скопировать код"
aria-label="Скопировать код"
data-copied-label="Скопировано!"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Механизм удобный, но его стоит использовать ограниченно. Чем больше миграция зависит от runtime-подстановок, тем сложнее
гарантировать, что один и тот же файл создает одну и ту же схему во всех окружениях. Для имен схемы, tablespace или
небольших environment-specific деталей это нормально. Для условной бизнес-логики — нет.&lt;/p&gt;
&lt;h3&gt;callbacks&lt;span class="hx:absolute hx:-mt-20" id="callbacks"&gt;&lt;/span&gt;
&lt;a href="#callbacks" class="subheading-anchor" aria-label="Ссылка на этот раздел"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Flyway callbacks позволяют подключаться к lifecycle событиям: &lt;code&gt;beforeMigrate&lt;/code&gt;, &lt;code&gt;afterMigrate&lt;/code&gt;, &lt;code&gt;afterEachMigrate&lt;/code&gt;,
&lt;code&gt;beforeClean&lt;/code&gt; и так далее. В &lt;a
href="https://documentation.red-gate.com/flyway/flyway-concepts/callbacks"target="_blank" rel="noopener"&gt;документации&lt;/a&gt; среди
типичных сценариев указаны перекомпиляция процедур, обновление materialized views, housekeeping вроде PostgreSQL
&lt;code&gt;VACUUM&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;В Spring Boot есть два способа:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Положить SQL callbacks в configured callback locations.&lt;/li&gt;
&lt;li&gt;Создать bean типа &lt;code&gt;org.flywaydb.core.api.callback.Callback&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Пример Java callback:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div class="hextra-code-filename not-prose" dir="auto"&gt;MigrationMetricsCallback.java&lt;/div&gt;&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#a6e22e"&gt;@Component&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;class&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;MigrationMetricsCallback&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;extends&lt;/span&gt; BaseCallback {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;boolean&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;supports&lt;/span&gt;(Event event, Context context) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt; event &lt;span style="color:#f92672"&gt;==&lt;/span&gt; Event.&lt;span style="color:#a6e22e"&gt;AFTER_MIGRATE&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#a6e22e"&gt;@Override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;public&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; &lt;span style="color:#a6e22e"&gt;handle&lt;/span&gt;(Event event, Context context) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#75715e"&gt;// записать техническую метрику, обновить служебное состояние&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-8"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Скопировать код"
aria-label="Скопировать код"
data-copied-label="Скопировано!"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Callbacks — мощный механизм, но его легко испортить. Хороший callback обслуживает миграционный процесс. Плохой callback
незаметно меняет прикладные данные, ходит во внешние сервисы или начинает зависеть от Spring beans, которые сами зависят
от уже мигрированной схемы.&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;skip-executing-migrations&lt;/code&gt;&lt;span class="hx:absolute hx:-mt-20" id="skip-executing-migrations"&gt;&lt;/span&gt;
&lt;a href="#skip-executing-migrations" class="subheading-anchor" aria-label="Ссылка на этот раздел"&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Настройка опасная и поэтому интересная:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div class="hextra-code-filename not-prose" dir="auto"&gt;application.yaml&lt;/div&gt;&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spring&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;flyway&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;skip-executing-migrations&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-8"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Скопировать код"
aria-label="Скопировать код"
data-copied-label="Скопировано!"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="hx:overflow-x-auto hx:mt-6 hx:flex hx:rounded-lg hx:border hx:py-2 hx:ltr:pr-4 hx:rtl:pl-4 hx:contrast-more:border-current hx:contrast-more:dark:border-current hx:border-red-200 hx:bg-red-100 hx:text-red-900 hx:dark:border-red-200/30 hx:dark:bg-red-900/30 hx:dark:text-red-200"&gt;
&lt;div class="hx:ltr:pl-3 hx:ltr:pr-2 hx:rtl:pr-3 hx:rtl:pl-2"&gt;&lt;svg height=1.2em class="hx:inline-block hx:align-middle" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"&gt;&lt;path stroke-linecap="round" stroke-linejoin="round" d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728A9 9 0 015.636 5.636m12.728 12.728L5.636 5.636"/&gt;&lt;/svg&gt;&lt;/div&gt;
&lt;div class="hx:w-full hx:min-w-0 hx:leading-7"&gt;
&lt;div class="hx:mt-6 hx:leading-7 hx:first:mt-0"&gt;&lt;code&gt;skip-executing-migrations&lt;/code&gt; не ускоритель и не способ пропустить тяжелый SQL. В обычном application startup ему почти никогда не место.&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Она говорит Flyway обновлять schema history, не выполняя содержимое миграций. Это не ускоритель и не способ &amp;ldquo;пропустить
тяжелые SQL&amp;rdquo;. Это инструмент для специальных процедур: ручного выравнивания history table после внешнего применения
изменений, восстановления окружения, редких migration adoption сценариев. В обычном application startup ему почти
никогда не место.&lt;/p&gt;
&lt;h2&gt;Практический шаблон для PostgreSQL&lt;span class="hx:absolute hx:-mt-20" id="практический-шаблон-для-postgresql"&gt;&lt;/span&gt;
&lt;a href="#%d0%bf%d1%80%d0%b0%d0%ba%d1%82%d0%b8%d1%87%d0%b5%d1%81%d0%ba%d0%b8%d0%b9-%d1%88%d0%b0%d0%b1%d0%bb%d0%be%d0%bd-%d0%b4%d0%bb%d1%8f-postgresql" class="subheading-anchor" aria-label="Ссылка на этот раздел"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Для Spring Boot 4.x приложения на PostgreSQL я бы начинал примерно с такого набора:&lt;/p&gt;
&lt;div class="hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code"&gt;
&lt;div class="hextra-code-filename not-prose" dir="auto"&gt;application.yaml&lt;/div&gt;&lt;div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#f92672"&gt;spring&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;flyway&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;enabled&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;url&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;${DATABASE_URL}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;user&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;${DATABASE_USERNAME}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;password&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;${DATABASE_PASSWORD}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;default-schema&lt;/span&gt;: &lt;span style="color:#ae81ff"&gt;${DATABASE_SCHEMA}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;locations&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; - &lt;span style="color:#ae81ff"&gt;classpath:db/migration&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;validate-on-migrate&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;validate-migration-naming&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;clean-disabled&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;postgresql&lt;/span&gt;:
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#f92672"&gt;transactional-lock&lt;/span&gt;: &lt;span style="color:#66d9ef"&gt;false&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-8"&gt;
&lt;button
class="hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50"
title="Скопировать код"
aria-label="Скопировать код"
data-copied-label="Скопировано!"
&gt;
&lt;div class="hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;div class="hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4"&gt;&lt;/div&gt;
&lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Если в проекте принципиально нет online DDL и все миграции транзакционные, &lt;code&gt;transactional-lock&lt;/code&gt; можно оставить true.
Но если вы используете PostgreSQL как PostgreSQL, а не как абстрактную SQL-базу, рано или поздно вам понадобятся
&lt;code&gt;CONCURRENTLY&lt;/code&gt;, &lt;code&gt;VACUUM&lt;/code&gt;, &lt;code&gt;REINDEX&lt;/code&gt; или другие non-transactional statements.&lt;/p&gt;
&lt;h2&gt;Главные выводы&lt;span class="hx:absolute hx:-mt-20" id="главные-выводы"&gt;&lt;/span&gt;
&lt;a href="#%d0%b3%d0%bb%d0%b0%d0%b2%d0%bd%d1%8b%d0%b5-%d0%b2%d1%8b%d0%b2%d0%be%d0%b4%d1%8b" class="subheading-anchor" aria-label="Ссылка на этот раздел"&gt;&lt;/a&gt;&lt;/h2&gt;&lt;div class="hx:overflow-x-auto hx:mt-6 hx:flex hx:rounded-lg hx:border hx:py-2 hx:ltr:pr-4 hx:rtl:pl-4 hx:contrast-more:border-current hx:contrast-more:dark:border-current hx:border-purple-200 hx:bg-purple-100 hx:text-purple-900 hx:dark:border-purple-200/30 hx:dark:bg-purple-900/30 hx:dark:text-purple-200"&gt;
&lt;div class="hx:ltr:pl-3 hx:ltr:pr-2 hx:rtl:pr-3 hx:rtl:pl-2"&gt;&lt;svg height=1.2em class="hx:inline-block hx:align-middle" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" aria-hidden="true"&gt;&lt;path stroke-linecap="round" stroke-linejoin="round" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/&gt;&lt;/svg&gt;&lt;/div&gt;
&lt;div class="hx:w-full hx:min-w-0 hx:leading-7"&gt;
&lt;div class="hx:mt-6 hx:leading-7 hx:first:mt-0"&gt;Главная ловушка статьи: &lt;code&gt;CREATE INDEX CONCURRENTLY&lt;/code&gt; может быть распознан Flyway как non-transactional statement, но PostgreSQL transactional advisory lock все равно создает внешний transaction block. Для online DDL часто нужен &lt;code&gt;spring.flyway.postgresql.transactional-lock=false&lt;/code&gt;.&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code&gt;CREATE INDEX CONCURRENTLY&lt;/code&gt; — хороший пример того, почему миграции нельзя воспринимать как набор SQL.
PostgreSQL запрещает эту команду внутри transaction block. Flyway умеет распознавать ее как non-transactional,
но PostgreSQL transactional advisory lock по умолчанию сам создает внешнюю транзакционную обертку.
Настройка &lt;code&gt;spring.flyway.postgresql.transactional-lock=false&lt;/code&gt; переключает lock на session-level и убирает этот конфликт.&lt;/p&gt;
&lt;p&gt;Остальные свойства &lt;code&gt;FlywayProperties&lt;/code&gt; стоит включать не по привычке, а по конкретной причине. &lt;code&gt;baseline-on-migrate&lt;/code&gt;
полезен при внедрении Flyway в существующую базу, но снижает защиту от ошибки окружения. &lt;code&gt;clean-disabled=true&lt;/code&gt; должен
оставаться production default. &lt;code&gt;group&lt;/code&gt; и &lt;code&gt;mixed&lt;/code&gt; требуют понимания транзакционной модели конкретной базы. &lt;code&gt;callbacks&lt;/code&gt;
хороши для технического обслуживания миграций, но не для скрытой прикладной логики.&lt;/p&gt;
&lt;p&gt;В итоге качественная интеграция Flyway в Spring Boot 4.x — это не только зависимость и папка &lt;code&gt;db/migration&lt;/code&gt;. Это
осознанный выбор database plugin, транзакционной модели, lock strategy, test runtime и набора свойств, которые
соответствуют реальному поведению вашей СУБД.&lt;/p&gt;</description></item></channel></rss>