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

264

Я получаю это, когда запускаю много скриптов 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)

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

Питер Исберг
источник
2
Вы убили JVM, пока жидкостьбаза держала замок? Это единственный случай, когда это происходит для меня.
Кристоф Лейтер
Похоже, здесь задействован другой компьютер: Konsultpc74. Может быть, вы запускали жидкость на разных компьютерах одновременно? Если нет, у вас есть объяснение для другого ПК?
Дженс
Я отредактировал журналы и случайно забыл изменить их на SomeComputer
Питер Исберг
Вы выполняете наборы изменений одновременно? Я думал, что каждый файл и каждый набор изменений в нем выполняется один за другим. По крайней мере, я использую это так. У меня есть один главный файл набора изменений, который включает все остальные, и все запускается один за другим.
Йенс

Ответы:

574

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

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

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

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

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

Адриан Бер
источник
24
Мне нужно было отключить 0для FALSE, но в остальном, он работал нормально. Спасибо
mattalxndr
7
В Liquibase есть встроенная команда releaseLocks, которая будет выполнять то, что ответил @Adrian Ber, но я думаю, что это не зависит от базы данных.
1
Я только что получил эту ошибку в моей среде разработки. Исправление таблицы DATABASECHANGELOGLOCK решило это.
Наймеш Мисти
1
Мне нужно было отключить FALSEдляb'0'
OrangePot
2
Это правильное решение, не пытайтесь очистить таблицу, так как это не поможет. Либо УДАЛИТЕ его, либо ОБНОВИТЕ флаг LOCKED до «FALSE»
Aditya T
55

Изменить июнь 2020

Не следуй этому совету. Это вызвало проблемы у многих людей на протяжении многих лет. Это сработало для меня давным-давно, и я написал это добросовестно, но это явно не способ сделать это. В таблице DATABASECHANGELOCK должно быть что-то, так что плохая идея - просто удалить из нее все.

Например , Leos Literak следовал этим инструкциям, и сервер не запускался.

Оригинальный ответ

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

DELETE FROM DATABASECHANGELOGLOCK;

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

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

e18r
источник
1
Это не дает ответа на вопрос. Чтобы критиковать или запросить разъяснения у автора, оставьте комментарий под своим постом.
Рахча
@Rachcha Я объяснил это лучше. Надеюсь, вам понравится больше, как это.
e18r
12
Не следуйте совету выше. DATABASECHANGELOGLOCK должен содержать строки без каких-либо строк, вы получите исключение
odedsh
Это не помогает, я попробовал это вместо того, чтобы сбросить таблицу или обновить заблокированное состояние на «ложь». Это не сработало.
Aditya T
Если вы последуете этому ответу, есть большая вероятность, что будущие сценарии не будут работать, потому что они ожидают, что блокировка существует. Если вы уже сделали это, вы можете добавить пустой замок, чтобы исправить проблему INSERT INTO yourdb.DATABASECHANGELOGLOCK VALUES (1, 0, null, null);
Руди Кершоу,
24

Проблема заключалась в ошибочной реализации 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 или отбросьте эту таблицу и создайте заново.

Питер Исберг
источник
1
В liquibase 3.0.2 (версия, которую я использую) не удаляйте одну строку из таблицы блокировок, иначе при следующем запуске liquibase у вас будет другая ошибка, потому что liquibase ожидает, что там будет одна строка (или вся таблица отсутствует). Точно так же, как сказал Питер, просто хотел добавить эту информацию, потому что в более старых версиях, кажется, работал, чтобы удалить строку.
Карим
7

Не упоминается, какая среда используется для выполнения 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.

k_o_
источник
Не могли бы вы предоставить более подробное описание вашего решения? Мы используем Spring Boot 2 и liquibase и не хотим каждый раз вручную удалять состояние блокировки в БД. Но я не понял, как вы вводите ForceReleaseLockService в ликвидазу. Разве я не должен размещать аннотацию Service / Component над этим классом, чтобы Spring выбрал его в качестве основного компонента?
Андрей Тихонов
1
В последнем предложении упоминается: «Класс должен быть помещен в пакет liquibase.ext и будет выбран автоматической настройкой Spring Boot 2».
k_o_
Как вы размещаете класс liquibase.ext, мне нужно определить этот пакет в моем проекте?
19
Я определил этот пакет в своем проекте, кажется, он работает, но я не могу проверить его. Я определил @PostConstructметод с помощью сообщения журнала, но не вижу его распечатанным.
19
@ akuma8: Да, просто создайте пакет в своем проекте с таким именем. Где вы определили @PostConstructметод? В ForceReleaseLockService? Это не служба Spring, поэтому она не будет вызываться.
k_o_
3

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

SQL:

SELECT gid FROM pg_prepared_xacts WHERE database='database_name';

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

ROLLBACK PREPARED 'gid_obtained_from_above_SQL';
Кушик Равулапелли
источник
1

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

DROP TABLE DATABASECHANGELOGLOCK;
Майк
источник
0

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

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

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

DarthPablo
источник