Замок Liquibase - причины?

Я получаю это, когда запускаю много скриптов liquibase для Oracle-сервера. SomeComputer это я.

Waiting for changelog lock....
Waiting for changelog lock....
Waiting for changelog lock....
Waiting for changelog lock....
Waiting for changelog lock....
Waiting for changelog lock....
Waiting for changelog lock....
Liquibase Update Failed: Could not acquire change log lock.  Currently locked by SomeComputer (192.168.15.X) since 2013-03-20 13:39
SEVERE 2013-03-20 16:59:liquibase: Could not acquire change log lock.  Currently locked by SomeComputer (192.168.15.X) since 2013-03-20 13:39
liquibase.exception.LockException: Could not acquire change log lock.  Currently locked by SomeComputer (192.168.15.X) since 2013-03-20 13:39
        at liquibase.lockservice.LockService.waitForLock(LockService.java:81)
        at liquibase.Liquibase.tag(Liquibase.java:507)
        at liquibase.integration.commandline.Main.doMigration(Main.java:643)
        at liquibase.integration.commandline.Main.main(Main.java:116)

Может ли быть так, что количество одновременных сеансов / транзакций достигнуто? У кого-нибудь есть идеи?

12 ответов

Решение

Проблема заключалась в ошибочной реализации SequenceExists в Liquibase. Поскольку изменения с этими утверждениями заняли очень много времени и были случайно прерваны. Затем следующая попытка выполнения сценариев liquibase блокировка была удержана.

  <changeSet author="user" id="123">
    <preConditions onFail="CONTINUE">
      <not><sequenceExists sequenceName="SEQUENCE_NAME_SEQ" /></not>
    </preConditions>
    <createSequence sequenceName="SEQUENCE_NAME_SEQ"/>
  </changeSet>

Обходной путь - использовать простой SQL, чтобы проверить это:

  <changeSet author="user" id="123">
    <preConditions onFail="CONTINUE">
            <sqlCheck expectedResult="0">
              select count(*) from user_sequences where sequence_name = 'SEQUENCE_NAME_SEQ';
            </sqlCheck>
    </preConditions>
    <createSequence sequenceName="SEQUENCE_NAME_SEQ"/>
  </changeSet>

Данные блокировки хранятся в таблице DATABASECHANGELOCK. Чтобы снять блокировку, просто измените 1 на 0 или отбросьте эту таблицу и создайте заново.

Иногда, если приложение обновления внезапно останавливается, блокировка остается заблокированной.

Потом работает

UPDATE DATABASECHANGELOGLOCK SET LOCKED=FALSE, LOCKGRANTED=null, LOCKEDBY=null where ID=1;

против базы данных помогает.

Или вы можете просто бросить DATABASECHANGELOGLOCK стол, он будет воссоздан.

Возможно, это связано с тем, что процесс уничтоженной ликвидазы не снимает блокировку с таблицы DATABASECHANGELOGLOCK. Затем,

DELETE FROM DATABASECHANGELOGLOCK;

может помочь тебе

Редактировать: ответ @Adrian Ber обеспечивает лучшее решение, чем это. Делайте это только в том случае, если у вас возникнут проблемы с его решением.

Не упоминается, какая среда используется для выполнения Liquibase. В случае Spring Boot 2 его можно расширить liquibase.lockservice.StandardLockService без необходимости запуска прямых операторов SQL, что намного чище. Например:

/**
 * This class is enforcing to release the lock from the database.
 *
 */
 public class ForceReleaseLockService extends StandardLockService {

    @Override
    public int getPriority() {
        return super.getPriority()+1;
    }

    @Override
    public void waitForLock() throws LockException {
        try {
            super.forceReleaseLock();
        } catch (DatabaseException e) {
            throw new LockException("Could not enforce getting the lock.", e);
        }
        super.waitForLock();
    }
}

Код обеспечивает освобождение блокировки. Это может быть полезно в тестовых установках, где вызов release может не вызываться в случае ошибок или когда отладка отменяется.

Класс должен быть помещен в liquibase.ext пакет и будет подхвачен автоконфигурацией Spring Boot 2.

Вы можете безопасно удалить таблицу вручную или с помощью запроса. Он будет воссоздан автоматически.

DROP TABLE DATABASECHANGELOGLOCK;

Я понимаю, что это не проблема ОП, но я столкнулся с этой проблемой недавно по другой причине. Для справки я использовал плагин Liquibase Maven (liquibase-maven-plugin:3.1.1) с SQL Server.

В любом случае, я ошибочно скопировал и вставил оператор SQL Server "use" в один из моих сценариев, который переключает базы данных, поэтому liquibase выполнял и обновлял DATABASECHANGELOGLOCKполучение блокировки в правильной базе данных, но затем переключение баз данных, чтобы применить изменения. Мало того, что я НЕ мог видеть мои изменения или аудит ликвидазы в правильной базе данных, но, конечно, когда я снова запустил ликвидазу, он не смог получить блокировку, так как блокировка была снята в "неправильной" базе данных, и так было все еще заблокирован в "правильной" базе данных. Я ожидал, что liquibase проверит, что блокировка все еще была применена перед ее снятием, и, возможно, это ошибка в liquibase (я еще не проверял), но она может быть исправлена ​​в более поздних версиях! Тем не менее, я полагаю, это можно считать особенностью!

Я знаю, что это небольшая ошибка школьника, но я ее здесь опускаю на случай, если кто-нибудь столкнется с той же проблемой!

Иногда усечение или удаление таблицы DATABASECHANGELOGLOCK не работает. Я использую базу данных PostgreSQL и сталкивался с этой проблемой много раз. То, что я делаю для решения, - это откат подготовленных операторов, работающих в фоновом режиме для этой базы данных. Попробуйте откатить все подготовленные операторы и попробуйте изменения в ликвидабазе снова.

SQL:

SELECT gid FROM pg_prepared_xacts WHERE database='database_name';

Если приведенный выше оператор возвращает какую-либо запись, выполните откат этого подготовленного оператора со следующим оператором SQL

ROLLBACK PREPARED 'gid_obtained_from_above_SQL';

Пожалуйста, позвольте Liquibase обрабатывать свои собственные таблицы. Как упоминает user1434769, правильнее всего использовать команду Liquibase releaseLocks.

При использовании Gradle это будет: gradlew releaseLocks

Раньше я просто обрезал таблицу DatabaseChangeLogLock, когда блокировка оставалась после сбоя (и это обычно все еще работает для меня).

Однако теперь существует этот проект (https://github.com/blagerweij/liquibase-sessionlock), который использует собственные блокировки сеанса базы данных для блокировки Liquibase при использовании Oracle, PostgreSQL и MySQL.

Файл JAR можно поместить в существующее приложение Liquibase 4.x+, и он будет автоматически обнаружен Liquibase. Это будет использовать блокировки сеанса для блокировки ..., которые автоматически освобождаются, если приложение выходит из строя или закрывается, пока блокировка удерживается. Это потенциально предотвратит появление заблокированного состояния (если вы используете одну из поддерживаемых баз данных).

В postgres 12 мне нужно было использовать эту команду:

      UPDATE DATABASECHANGELOGLOCK SET LOCKED=false, LOCKGRANTED=null, LOCKEDBY=null where ID=1;

Таблица по умолчанию для журнала изменений Liquibase — DATABASECHANGELOCK, однако ее можно переопределить или установить в пользовательскую таблицу, установив свойство ниже.

в application.properties

      database-change-log-lock-table=<SOME_CUSTOM_TABLE_NAME>

Если в приложении установлено это свойство, то

      UPDATE <SOME_CUSTOM_TABLE_NAME> SET LOCKED=0, LOCKGRANTED=null, LOCKEDBY=null where ID=1;     

или удалить таблицу

      drop table <SOME_CUSTOM_TABLE_NAME>

Если решение, опубликованное @Adrian, не сработало, вы можете попробовать сделать то же самое в таблице INTERFACE_DATABASECHANGELOGLOCK, которая содержит дополнительную информацию о блокировке.

      UPDATE INTERFACE_DATABASECHANGELOGLOCK SET LOCKED=0, LOCKGRANTED=null, LOCKEDBY=null where ID=1;

или просто очистить таблицу

      TRUNCATE TABLE INTERFACE_DATABASECHANGELOGLOCK
Другие вопросы по тегам