Bloqueio do Liquibase - razões?

262

Eu recebo isso ao executar muitos scripts liquibase em um servidor Oracle. SomeComputer sou eu.

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)

Será que o número de sessões / transações simultâneas é atingido? Alguém tem alguma idéia?

Peter Isberg
fonte
2
Você matou a JVM enquanto o liquibase mantinha a trava? Esse é o único caso em que isso ocorre para mim.
precisa
Parece haver outro PC envolvido: Konsultpc74. Talvez você tenha executado o liquibase de PCs diferentes ao mesmo tempo? Caso contrário, você tem uma explicação para o outro PC?
Jens
Eu editei os logs e eu acidentalmente esqueceu de mudar isso para SomeComputer
Peter Isberg
Você está executando os conjuntos de alterações simultaneamente? Eu pensei que cada arquivo e cada conjunto de alterações nele é executado um por um. Pelo menos eu uso dessa maneira. Eu tenho um arquivo principal do changeset que inclui todos os outros e tudo é executado um por um.
Jens

Respostas:

573

Às vezes, se o aplicativo de atualização for interrompido abruptamente, o bloqueio permanecerá travado.

Então correndo

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

contra o banco de dados ajuda.

Ou você pode simplesmente largar a DATABASECHANGELOGLOCKmesa, ela será recriada.

Adrian Ber
fonte
24
Eu precisava mudar o 0for FALSE, mas fora isso, funcionou bem. Obrigado
mattalxndr
7
Existe um comando interno no Liquibase chamado releaseLocks que executaria o que o @Adrian Ber respondeu, mas acho que é independente de banco de dados.
1
Eu estava recebendo esse erro no meu ambiente de desenvolvimento. A correção da tabela DATABASECHANGELOGLOCK a resolveu.
Naymesh Mistry
1
Eu precisava mudar o FALSEforb'0'
OrangePot 10/10
2
Esta é a solução correta, não tente esvaziar a mesa, pois isso não ajudará. DROP it ou UPDATE the flag LOCKED para 'FALSE'
Aditya T 23-23
55

Editar junho 2020

Não siga este conselho. Causou problemas para muitas pessoas ao longo dos anos. Funcionou para mim há muito tempo e eu publiquei de boa fé, mas claramente não é a maneira de fazê-lo. A tabela DATABASECHANGELOCK precisa ter coisas nela, por isso é uma má idéia apenas excluir tudo dela.

O Leos Literak , por exemplo, seguiu estas instruções e o servidor falhou ao iniciar.

Resposta original

Possivelmente, devido a um processo liquibase morto, não liberando seu bloqueio na tabela DATABASECHANGELOGLOCK. Então,

DELETE FROM DATABASECHANGELOGLOCK;

pode ajudá-lo.

Edit: @Adrian Ber resposta fornece uma solução melhor do que isso. Faça isso apenas se você tiver algum problema na solução dele.

e18r
fonte
1
Isso não fornece uma resposta para a pergunta. Para criticar ou solicitar esclarecimentos a um autor, deixe um comentário abaixo da postagem.
Rachcha
@Rachcha eu expliquei melhor. Espero que você goste mais assim.
e18r 22/05/19
12
Não siga o conselho acima. DATABASECHANGELOGLOCK deve conter linhas sem nenhuma linha, você receberá uma exceção
odedsh
Isso não ajuda, tentei isso em vez de soltar a tabela ou atualizar o estado bloqueado para 'false'. Não deu certo.
Aditya T
Se você seguir esta resposta, há uma boa chance de futuros scripts não serem executados porque eles esperam que o bloqueio exista. Se você tiver feito isso já é possível adicionar um bloqueio vazia para corrigir o problema INSERT INTO yourdb.DATABASECHANGELOGLOCK VALUES (1, 0, null, null);
Rudi Kershaw
24

O problema foi a implementação incorreta do SequenceExists no Liquibase. Como os conjuntos de alterações com essas declarações levaram muito tempo e foram abortados acidentalmente. Em seguida, na próxima tentativa de executar os scripts liquibase, o bloqueio foi mantido.

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

Uma solução alternativa é usar SQL simples para verificar isso:

  <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>

Os dados de bloqueio são armazenados na tabela DATABASECHANGELOCK. Para se livrar do bloqueio, basta alterar 1 para 0 ou largar a tabela e recriar.

Peter Isberg
fonte
1
No liquibase 3.0.2 (a versão que estou usando), não remova uma linha da tabela de bloqueio ou você terá um erro diferente ao executar o liquibase da próxima vez, porque o liquibase espera que uma linha esteja lá (ou o falta a tabela inteira). Exatamente como Peter disse, só queria adicionar essas informações, porque em versões mais antigas parece ter funcionado para remover também a linha.
Kariem
7

Não é mencionado qual ambiente é usado para executar o Liquibase. No caso de Spring Boot 2, é possível estender liquibase.lockservice.StandardLockServicesem a necessidade de executar instruções SQL diretas, o que é muito mais limpo. Por exemplo:

/**
 * 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();
    }
}

O código está forçando a liberação do bloqueio. Isso pode ser útil em configurações de teste nas quais a chamada de liberação pode não ser chamada em caso de erros ou quando a depuração é interrompida.

A classe deve ser colocada no liquibase.extpacote e será escolhida pela configuração automática do Spring Boot 2.

k_o_
fonte
Você poderia fornecer uma descrição mais detalhada da sua solução? Usamos o Spring Boot 2 e o liquibase e não queremos excluir manualmente o estado de bloqueio no banco de dados. Mas não entendi como você injeta o ForceReleaseLockService na liquibase. Não tenho que colocar uma anotação Service / Component sobre essa classe, que o Spring a escolha como um bean primário?
Andrej Tihonov
1
Ele é mencionado na última frase: "A classe deve ser colocada no pacote liquibase.ext e será captada pela configuração automática do Spring Boot 2."
k_o_
Como você classifica a classe liquibase.ext, preciso definir esse pacote no meu projeto?
precisa saber é
Eu defini esse pacote no meu projeto, parece funcionar, mas não posso verificar. Eu defini um @PostConstructmétodo com uma mensagem de log, mas não o vejo impresso.
precisa saber é
@ akuma8: Sim, basta criar um pacote no seu projeto com esse nome. Onde você definiu o @PostConstructmétodo? No ForceReleaseLockService? Este não é um serviço Spring, portanto, não será chamado.
k_o_
3

Às vezes, truncar ou soltar a tabela DATABASECHANGELOGLOCK não funciona. Eu uso o banco de dados PostgreSQL e me deparei com esse problema várias vezes. O que faço para resolver é reverter as instruções preparadas em execução em segundo plano para esse banco de dados. Tente reverter todas as instruções preparadas e tente as alterações do liquibase novamente.

SQL:

SELECT gid FROM pg_prepared_xacts WHERE database='database_name';

Se a instrução acima retornar qualquer registro, faça a reversão dessa instrução preparada com a seguinte instrução SQL.

ROLLBACK PREPARED 'gid_obtained_from_above_SQL';
Koushik Ravulapelli
fonte
1

Você pode excluir a tabela com segurança manualmente ou usando a consulta. Será recriado automaticamente.

DROP TABLE DATABASECHANGELOGLOCK;
Mike
fonte
0

Aprecio que esse não foi o problema do OP, mas me deparei com esse problema recentemente por uma causa diferente. Para referência, eu estava usando o plug-in Liquibase Maven (liquibase-maven-plugin: 3.1.1) com o SQL Server.

De qualquer forma, eu copiei e colei erroneamente uma instrução "use" do SQL Server em um dos meus scripts que alterna bancos de dados, para que o liquibase estivesse executando e atualizando o arquivo DATABASECHANGELOGLOCK, adquirindo o bloqueio no banco de dados correto, mas alternando os bancos de dados para aplicar as alterações. Não só NÃO pude ver minhas alterações ou auditoria do liquibase no banco de dados correto, mas é claro que, quando executei o liquibase novamente, ele não pôde adquirir o bloqueio, pois o bloqueio havia sido liberado no banco de dados "errado", e também ainda bloqueado no banco de dados "correto". Eu esperava que o liquibase verifique se o bloqueio ainda foi aplicado antes de liberá-lo, e talvez isso seja um bug do liquibase (ainda não o verifiquei), mas pode ser resolvido em versões posteriores! Dito isto, suponho que possa ser considerado um recurso!

Sei que é um erro de estudante, mas o levanto aqui caso alguém tenha o mesmo problema!

DarthPablo
fonte