Liquibase lock - razones?

264

Obtengo esto cuando ejecuto muchos scripts de liquibase contra un servidor Oracle. SomeComputer soy yo.

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)

¿Podría ser que se alcance el número de sesiones / transacciones simultáneas? Alguien tiene alguna idea?

Peter Isberg
fuente
2
¿Mataste a la JVM mientras liquibase mantenía la cerradura? Ese es el único caso donde esto ocurre para mí.
Christoph Leiter
Parece que hay otra PC involucrada: Konsultpc74. ¿Quizás ejecutó liquibase desde diferentes PC al mismo tiempo? Si no, ¿tiene una explicación para la otra PC?
Jens
Edité los registros y accidentalmente olvidé cambiar eso a SomeComputer
Peter Isberg
¿Estás ejecutando los conjuntos de cambios simultáneamente? Pensé que cada archivo y cada conjunto de cambios se ejecutan uno por uno. Al menos lo uso de esta manera. Tengo un archivo maestro de conjunto de cambios que incluye todos los demás y todo se ejecuta uno por uno.
Jens

Respuestas:

574

A veces, si la aplicación de actualización se detiene abruptamente, el bloqueo permanece atascado.

Entonces corriendo

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

contra la base de datos ayuda.

O simplemente puede dejar caer la DATABASECHANGELOGLOCKtabla, se volverá a crear.

Adrian Ber
fuente
24
Necesitaba cambiarlo 0por FALSE, pero aparte de eso, funcionó bien. Gracias
mattalxndr
77
Hay un comando incorporado en Liquibase llamado releaseLocks que ejecutaría lo que @Adrian Ber respondió pero creo que es independiente de la base de datos.
1
Estaba recibiendo este error en mi entorno de desarrollo. Arreglando la tabla DATABASECHANGELOGLOCK lo resolvió.
Naymesh Mistry
1
Necesitaba cambiar el FALSEparab'0'
OrangePot
2
Esta es la solución correcta, no intente vaciar la tabla ya que eso no ayudará. O bien dejarlo caer, ni actualizar el indicador LOCKED 'false'
Aditya T
55

Editar junio 2020

No sigas este consejo. Ha causado problemas a muchas personas a lo largo de los años. Funcionó para mí hace mucho tiempo y lo publiqué de buena fe, pero claramente no es la forma de hacerlo. La tabla DATABASECHANGELOCK debe contener elementos, por lo que es una mala idea simplemente eliminar todo de ella.

Leos Literak , por ejemplo, siguió estas instrucciones y el servidor no pudo iniciarse.

Respuesta original

Posiblemente se deba a un proceso de liquibase muerto que no libera su bloqueo en la tabla DATABASECHANGELOGLOCK. Luego,

DELETE FROM DATABASECHANGELOGLOCK;

podría ayudarte

Editar: la respuesta de @Adrian Ber proporciona una solución mejor que esta. Solo haga esto si tiene algún problema para hacer su solución.

e18r
fuente
1
Esto no proporciona una respuesta a la pregunta. Para criticar o solicitar una aclaración de un autor, deje un comentario debajo de su publicación.
Rachcha
@Rachcha lo expliqué mejor. Espero que les guste más así.
e18r
12
No sigas los consejos anteriores. DATABASECHANGELOGLOCK debe contener filas sin filas, obtendrá una excepción
odedsh
Esto no ayuda, intenté esto en lugar de soltar la tabla o actualizar el estado bloqueado a 'falso'. No funcionó.
Aditya T
Si sigue esta respuesta, existe una buena posibilidad de que los scripts futuros no se ejecuten porque esperan que exista el bloqueo. Si ya ha hecho esto, puede agregar un candado vacío para solucionar el problema INSERT INTO yourdb.DATABASECHANGELOGLOCK VALUES (1, 0, null, null);
Rudi Kershaw
24

El problema fue la implementación defectuosa de SequenceExists en Liquibase. Dado que los conjuntos de cambios con estas declaraciones tomaron mucho tiempo y fueron abortados accidentalmente. Luego, en el siguiente intento de ejecutar las secuencias de comandos liquibase, se mantuvo el bloqueo.

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

Una solución alternativa es usar SQL simple para verificar esto en su lugar:

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

Lockdata se almacena en la tabla DATABASECHANGELOCK. Para deshacerse del bloqueo, simplemente cambie 1 a 0 o suelte esa tabla y vuelva a crearla.

Peter Isberg
fuente
1
En liquibase 3.0.2 (la versión que estoy usando), no elimine una línea de la tabla de bloqueo, o tendrá un error diferente cuando ejecute liquibase la próxima vez, porque liquibase espera que haya una fila allí (o falta toda la mesa). Exactamente como Peter dijo, solo quería agregar esa información, porque en versiones anteriores parece haber funcionado también para eliminar la fila.
Kariem
7

No se menciona qué entorno se utiliza para ejecutar Liquibase. En caso de que sea Spring Boot 2, es posible extenderlo liquibase.lockservice.StandardLockServicesin la necesidad de ejecutar sentencias SQL directas, lo cual es mucho más limpio. P.ej:

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

El código impone la liberación del bloqueo. Esto puede ser útil en configuraciones de prueba en las que la llamada de liberación podría no llamarse en caso de errores o cuando se cancela la depuración.

La clase debe colocarse en el liquibase.extpaquete y será recogida por la configuración automática de Spring Boot 2.

k_o_
fuente
¿Podría por favor proporcionar una descripción más detallada de su solución? Utilizamos Spring Boot 2 y liquibase y no queremos eliminar el estado de bloqueo en la base de datos cada vez de forma manual. Pero no entendí cómo se inyecta ForceReleaseLockService en la liquibase. ¿No tengo que poner una anotación de Servicio / Componente sobre esta clase, que Spring lo elija como un bean primario?
Andrej Tihonov
1
Se menciona en la última oración: "La clase debe colocarse en el paquete liquibase.ext y será recogida por la configuración automática Spring Boot 2".
k_o_
¿Cómo se coloca en la clase liquibase.ext, qué necesito para definir ese paquete en mi proyecto?
akuma8
Definí ese paquete en mi proyecto, parece funcionar pero no puedo verificarlo. Definí un @PostConstructmétodo con un mensaje de registro pero no lo veo impreso.
akuma8
@ akuma8: Sí, solo cree un paquete en su proyecto con ese nombre. ¿Dónde definiste el @PostConstructmétodo? En el ForceReleaseLockService? Este no es un servicio de Spring, por lo que no se invocará.
k_o_
3

A veces, truncar o soltar la tabla DATABASECHANGELOGLOCK no funciona. Utilizo la base de datos PostgreSQL y me encontré con este problema muchas veces. Lo que hago para resolver es revertir las declaraciones preparadas que se ejecutan en segundo plano para esa base de datos. Intente revertir todas las declaraciones preparadas e intente los cambios de liquibase nuevamente.

SQL:

SELECT gid FROM pg_prepared_xacts WHERE database='database_name';

Si la declaración anterior devuelve algún registro, revierta esa declaración preparada con la siguiente declaración SQL.

ROLLBACK PREPARED 'gid_obtained_from_above_SQL';
Koushik Ravulapelli
fuente
1

Puede eliminar de forma segura la tabla manualmente o mediante la consulta. Será recreado automáticamente.

DROP TABLE DATABASECHANGELOGLOCK;
Miguel
fuente
0

Aprecio que este no era el problema del OP, pero recientemente me encontré con este problema con una causa diferente. Como referencia, estaba usando el complemento Liquibase Maven (liquibase-maven-plugin: 3.1.1) con SQL Server.

De todos modos, había copiado y pegado erróneamente una declaración de "uso" de SQL Server en uno de mis scripts que cambia las bases de datos, por lo que liquibase se estaba ejecutando y actualizando DATABASECHANGELOGLOCK, adquiriendo el bloqueo en la base de datos correcta, pero luego cambiando las bases de datos para aplicar los cambios. No solo no pude ver mis cambios o la auditoría de liquibase en la base de datos correcta, sino que, por supuesto, cuando volví a ejecutar liquibase, no pudo adquirir el bloqueo, ya que el bloqueo se había liberado en la base de datos "incorrecta", y así fue todavía bloqueado en la base de datos "correcta". Esperaba que liquibase verificara que el bloqueo todavía se aplicaba antes de liberarlo, y tal vez eso sea un error en liquibase (aún no lo he verificado), ¡pero es posible que se aborde en versiones posteriores! Dicho esto, supongo que podría considerarse una característica.

Es un error de colegial, lo sé, pero lo menciono aquí en caso de que alguien tenga el mismo problema.

DarthPablo
fuente