Blokada Liquibase - powody?

262

Rozumiem to, gdy uruchamiam wiele skryptów Liquibase przeciwko serwerowi Oracle. SomeComputer to ja.

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)

Czy to możliwe, że osiągnięto liczbę jednoczesnych sesji / transakcji? Czy ktoś ma jakieś pomysły?

Peter Isberg
źródło
2
Czy zabiłeś JVM, kiedy likibase trzymał zamek? To jedyny przypadek, w którym to się dla mnie zdarza.
Christoph Leiter
Wygląda na to, że w grę wchodzi inny komputer: Konsultpc74. Może jednocześnie uruchomiłeś likibase na różnych komputerach? Jeśli nie, czy masz wyjaśnienie dla drugiego komputera?
Jens
Edytowałem dzienniki i przypadkowo zapomniałem zmienić to na SomeComputer
Peter Isberg
Czy wykonujesz zestawy zmian jednocześnie? Myślałem, że każdy plik i każdy zestaw zmian jest wykonywany jeden po drugim. Przynajmniej używam tego w ten sposób. Mam jeden główny plik zmian, który obejmuje wszystkie inne i wszystko jest uruchamiane jeden po drugim.
Jens

Odpowiedzi:

572

Czasami, jeśli aplikacja aktualizacji zostanie nagle zatrzymana, blokada pozostaje zablokowana.

Potem biegnij

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

przeciwko bazie danych pomaga.

Lub możesz po prostu upuścić DATABASECHANGELOGLOCKtabelę, zostanie ona odtworzona.

Adrian Ber
źródło
24
Musiałem zmienić 0na FALSE, ale poza tym działało dobrze. Dzięki
mattalxndr,
7
W Liquibase jest wbudowane polecenie o nazwie releaseLocks, które wykonałoby odpowiedź @Adrian Ber, ale myślę, że jest niezależna od bazy danych.
1
Właśnie pojawiał się ten błąd w moim środowisku programistycznym. Naprawienie tabeli DATABASECHANGELOGLOCK rozwiązało to.
Naymesh Mistry
1
Musiałem wyłączyć FALSEforb'0'
OrangePot,
2
To prawidłowe rozwiązanie, nie próbuj opróżniać tabeli, ponieważ to nie pomoże. Opuść go lub zaktualizuj ZABLOKOWANĄ flagę do „FAŁSZ”
Aditya T
55

Edytuj czerwca 2020 r

Nie stosuj się do tej porady. Przez lata sprawiało wielu ludziom kłopoty. Dawno temu działało to dla mnie i opublikowałem je w dobrej wierze, ale najwyraźniej nie jest to odpowiedni sposób. Tabela DATABASECHANGELOCK musi mieć w sobie różne elementy, więc usunięcie tego wszystkiego jest złym pomysłem.

Na przykład Leos Literak zastosował się do tych instrukcji i serwer nie uruchomił się.

Oryginalna odpowiedź

Jest to prawdopodobnie spowodowane tym, że proces zabitego likibazy nie zwalnia blokady na stole DATABASECHANGELOGLOCK. Następnie,

DELETE FROM DATABASECHANGELOGLOCK;

może ci pomóc.

Edycja: odpowiedź @Adriana Bera stanowi lepsze rozwiązanie niż to. Zrób to tylko, jeśli masz problemy z jego rozwiązaniem.

e18r
źródło
1
To nie daje odpowiedzi na pytanie. Aby skrytykować lub poprosić autora o wyjaśnienia, zostaw komentarz pod postem.
Rachcha
@Rachcha Wyjaśniłem to lepiej. Mam nadzieję, że podoba Ci się to bardziej.
e18r
12
Nie stosuj się do powyższych wskazówek. DATABASECHANGELOGLOCK musi zawierać wiersze bez wierszy, otrzymasz wyjątek
odedsh
To nie pomaga, próbowałem tego, zamiast upuszczać tabelę lub aktualizować stan zablokowany na „false”. Nie zadziałało.
Aditya T
Jeśli zastosujesz się do tej odpowiedzi, istnieje duże prawdopodobieństwo, że przyszłe skrypty nie zostaną uruchomione, ponieważ oczekują, że blokada będzie istnieć. Jeśli już to zrobiłeś, możesz dodać pustą blokadę, aby rozwiązać problem INSERT INTO yourdb.DATABASECHANGELOGLOCK VALUES (1, 0, null, null);
Rudi Kershaw
24

Problemem była błędna implementacja SequenceExists w Liquibase. Ponieważ zestawy zmian z tymi instrukcjami zajęły bardzo dużo czasu i zostały przypadkowo przerwane. Następnie następna próba wykonania skryptów likibase blokada została wstrzymana.

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

W celu obejścia tego problemu należy użyć zwykłego kodu 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>

Dane blokady są przechowywane w tabeli DATABASECHANGELOCK. Aby pozbyć się blokady, wystarczy zmienić 1 na 0 lub upuścić tę tabelę i odtworzyć.

Peter Isberg
źródło
1
W Liquibase 3.0.2 (wersja, której używam), nie usuwaj jednego wiersza ze tabeli blokowania, inaczej wystąpi inny błąd przy następnym uruchomieniu Liquibase, ponieważ Liquibase oczekuje, że będzie tam jeden wiersz (lub brak całego stołu). Dokładnie tak, jak powiedział Peter, chciałem tylko dodać te informacje, ponieważ w starszych wersjach wydaje się, że działało również, aby usunąć wiersz.
Kariem
7

Nie wspomniano, które środowisko jest używane do uruchamiania Liquibase. W przypadku Spring Boot 2 istnieje możliwość rozszerzenia liquibase.lockservice.StandardLockServicebez potrzeby uruchamiania bezpośrednich instrukcji SQL, co jest znacznie bardziej przejrzyste. Na przykład:

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

Kod wymusza zwolnienie blokady. Może to być przydatne w zestawach testowych, w których wywołanie wydania może nie zostać wywołane w przypadku błędów lub przerwania debugowania.

Klasa musi zostać umieszczona w liquibase.extpakiecie i zostanie odebrana przez automatyczną konfigurację Spring Boot 2.

k_o_
źródło
Czy możesz podać bardziej szczegółowy opis swojego rozwiązania? Używamy Spring Boot 2 i Liquibase i nie chcemy ręcznie usuwać stanu blokady w db. Ale nie rozumiem, jak wstrzyknąć ForceReleaseLockService do likibazy. Czy nie muszę umieszczać adnotacji Service / Component nad tą klasą, że Spring wybrał ją jako fasolę podstawową?
Andrej Tihonov
1
W ostatnim zdaniu wspomniano: „Klasa musi zostać umieszczona w pakiecie liquibase.ext i zostanie odebrana przez automatyczną konfigurację Spring Boot 2”.
k_o_
Jak umieścić klasę liquibase.ext, czy muszę zdefiniować ten pakiet w moim projekcie?
akuma8
Zdefiniowałem ten pakiet w moim projekcie, wydaje się, że działa, ale nie mogę go zweryfikować. Zdefiniowałem @PostConstructmetodę z komunikatem dziennika, ale nie widzę jej wydrukowanej.
akuma8
@ akuma8: Tak, po prostu stwórz pakiet w swoim projekcie o tej nazwie. Gdzie zdefiniowałeś @PostConstructmetodę? W ForceReleaseLockService? To nie jest usługa wiosenna, więc nie zostanie wywołana.
k_o_
3

Czasami obcinanie lub upuszczanie tabeli DATABASECHANGELOGLOCK nie działa. Korzystam z bazy danych PostgreSQL i wiele razy natknąłem się na ten problem. To, co robię w celu rozwiązania, to wycofanie przygotowanych instrukcji działających w tle dla tej bazy danych. Spróbuj wycofać wszystkie przygotowane wyciągi i spróbuj ponownie wprowadzić zmiany w likibazie.

SQL:

SELECT gid FROM pg_prepared_xacts WHERE database='database_name';

Jeśli powyższa instrukcja zwraca dowolny rekord, wówczas wycofaj tę przygotowaną instrukcję z następującą instrukcją SQL.

ROLLBACK PREPARED 'gid_obtained_from_above_SQL';
Koushik Ravulapelli
źródło
1

Możesz bezpiecznie usunąć tabelę ręcznie lub za pomocą zapytania. Zostanie odtworzony automatycznie.

DROP TABLE DATABASECHANGELOGLOCK;
Mikrofon
źródło
0

Rozumiem, że to nie był problem PO, ale ostatnio natknąłem się na ten problem z innej przyczyny. Dla porównania użyłem wtyczki Liquibase Maven (liquibase-maven-plugin: 3.1.1) z SQL Server.

W każdym razie, błędnie skopiowałem i wkleiłem instrukcję SQL Server „use” do jednego ze moich skryptów, które zmieniają bazy danych, więc likibase działało i aktualizowało DATABASECHANGELOGLOCK, uzyskiwało blokadę w poprawnej bazie danych, ale następnie przełączało bazy danych, aby zastosować zmiany. Nie tylko NIE mogłem zobaczyć moich zmian lub audytu likibase w prawidłowej bazie danych, ale oczywiście, kiedy ponownie uruchomiłem likibase, nie mógł uzyskać blokady, ponieważ blokada została zwolniona w „niewłaściwej” bazie danych, i tak też było wciąż jest zamknięty w „poprawnej” bazie danych. Oczekiwałem, że Liquibase sprawdzi, czy zamek został jeszcze zastosowany przed jego zwolnieniem, i być może jest to błąd w Liquibase (jeszcze tego nie sprawdziłem), ale można go rozwiązać w późniejszych wersjach! To powiedziawszy, przypuszczam, że można to uznać za funkcję!

Wiem, że to trochę błąd ucznia, ale podnoszę go tutaj, na wypadek, gdyby ktoś napotkał ten sam problem!

DarthPablo
źródło