Kunci Liquibase - alasan?

262

Saya mendapatkan ini ketika menjalankan banyak skrip liku-liku terhadap server-Oracle. SomeComputer adalah aku.

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)

Mungkinkah jumlah sesi / transaksi simultan tercapai? Adakah yang punya ide?

Peter Isberg
sumber
2
Apakah Anda membunuh JVM saat liquibase memegang kunci? Itulah satu-satunya kasus di mana ini terjadi pada saya.
Christoph Leiter
Tampaknya ada PC lain yang terlibat: Konsultpc74. Mungkin Anda menjalankan liquibase dari ke PC yang berbeda secara bersamaan? Jika tidak, apakah Anda memiliki penjelasan untuk PC lain?
Jens
Saya mengedit log dan saya tidak sengaja lupa untuk mengubahnya ke SomeComputer
Peter Isberg
Apakah Anda menjalankan perubahan secara bersamaan? Saya pikir setiap file dan setiap perubahan di dalamnya dieksekusi satu per satu. Setidaknya saya menggunakannya dengan cara ini. Saya punya satu file master perubahan yang mencakup semua yang lain dan semuanya dijalankan satu per satu.
Jens

Jawaban:

573

Terkadang jika aplikasi pembaruan tiba-tiba berhenti, maka kunci tetap macet.

Lalu berlari

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

terhadap database membantu.

Atau Anda cukup menjatuhkan DATABASECHANGELOGLOCKmeja, itu akan dibuat kembali.

Adrian Ber
sumber
24
Saya perlu mengganti 0untuk FALSE, tetapi selain itu, itu berfungsi dengan baik. Terima kasih
mattalxndr
7
Ada perintah bawaan di Liquibase yang disebut releaseLocks yang akan mengeksekusi apa yang dijawab @Adrian Ber, tetapi saya pikir itu adalah basis data agnostik.
1
Saya baru saja mendapatkan kesalahan ini di lingkungan pengembangan saya. Memperbaiki tabel DATABASECHANGELOGLOCK menyelesaikannya.
Naymesh Mistry
1
Saya harus mengganti FALSEforb'0'
OrangePot
2
Ini adalah solusi yang tepat, jangan coba mengosongkan tabel karena itu tidak akan membantu. Entah DROP atau Perbarui bendera TERKUNCI ke 'FALSE'
Aditya T
55

Edit Juni 2020

Jangan ikuti saran ini. Ini menyebabkan masalah bagi banyak orang selama bertahun-tahun. Itu bekerja untuk saya sejak lama dan saya mempostingnya dengan itikad baik, tetapi jelas bukan cara untuk melakukannya. Tabel DATABASECHANGELOCK perlu memuat beberapa hal di dalamnya, jadi sebaiknya hapus saja semuanya.

Leos Literak , misalnya, mengikuti instruksi ini dan server gagal memulai.

Jawaban asli

Mungkin karena proses liquibase yang terbunuh tidak melepaskan kunci pada tabel DATABASECHANGELOGLOCK. Kemudian,

DELETE FROM DATABASECHANGELOGLOCK;

mungkin bisa membantu Anda.

Sunting: Jawaban @Adrian Ber memberikan solusi yang lebih baik dari ini. Hanya lakukan ini jika Anda memiliki masalah dalam melakukan solusinya.

e18r
sumber
1
Ini tidak memberikan jawaban untuk pertanyaan itu. Untuk mengkritik atau meminta klarifikasi dari penulis, tinggalkan komentar di bawah posting mereka.
Rachcha
@Rachcha saya menjelaskannya dengan lebih baik. Semoga Anda lebih menyukainya seperti ini.
e18r
12
Jangan ikuti saran di atas. DATABASECHANGELOGLOCK harus mengandung baris tanpa baris, Anda akan mendapatkan pengecualian
odedsh
Ini tidak membantu, saya mencoba ini daripada menjatuhkan tabel atau memperbarui status terkunci ke 'false'. Itu tidak berhasil.
Aditya T
Jika Anda mengikuti jawaban ini, ada kemungkinan skrip yang akan datang tidak akan berjalan karena mereka berharap kunci itu ada. Jika Anda sudah melakukan ini, Anda dapat menambahkan kunci kosong untuk memperbaiki masalah INSERT INTO yourdb.DATABASECHANGELOGLOCK VALUES (1, 0, null, null);
Rudi Kershaw
24

Masalahnya adalah implementasi kereta SequenceExists di Liquibase. Karena perubahan dengan pernyataan ini membutuhkan waktu yang sangat lama dan tidak sengaja dibatalkan. Kemudian coba berikutnya menjalankan skrip liku-liku-liku yang dimiliki.

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

Cara mengatasinya menggunakan SQL biasa untuk memeriksa ini sebagai gantinya:

  <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 disimpan dalam tabel DATABASECHANGELOCK. Untuk menghilangkan kunci Anda cukup mengubah 1 ke 0 atau jatuhkan tabel itu dan buat ulang.

Peter Isberg
sumber
1
Dalam liquibase 3.0.2 (versi yang saya gunakan), jangan hapus satu baris dari tabel kunci, atau Anda akan memiliki kesalahan yang berbeda ketika menjalankan liquibase di waktu berikutnya, karena liquibase mengharapkan bahwa satu baris berada di sana (atau seluruh tabel hilang). Persis seperti yang dikatakan Peter, hanya ingin menambahkan info itu, karena dalam versi yang lebih lama sepertinya berfungsi juga untuk menghapus baris.
Kariem
7

Tidak disebutkan lingkungan mana yang digunakan untuk mengeksekusi Liquibase. Dalam hal ini adalah Spring Boot 2 dimungkinkan untuk memperpanjang liquibase.lockservice.StandardLockServicetanpa perlu menjalankan pernyataan SQL langsung yang jauh lebih bersih. Misalnya:

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

Kode ini memberlakukan pelepasan kunci. Ini dapat berguna dalam pengaturan uji di mana panggilan rilis mungkin tidak dipanggil jika terjadi kesalahan atau ketika debugging dibatalkan.

Kelas harus ditempatkan dalam liquibase.extpaket dan akan diambil oleh konfigurasi otomatis Spring Boot 2.

k_o_
sumber
Bisakah Anda memberikan deskripsi yang lebih rinci tentang solusi Anda? Kami menggunakan Spring Boot 2 dan liquibase dan tidak ingin menghapus status kunci di db setiap kali secara manual. Tapi saya tidak mengerti bagaimana Anda menyuntikkan ForceReleaseLockService ke liquibase. Jangan saya harus meletakkan Layanan / Komponen anotasi atas kelas ini, bahwa Spring memilihnya sebagai kacang utama?
Andrej Tihonov
1
Disebutkan dalam kalimat terakhir: "Kelas harus ditempatkan dalam paket liquibase.ext dan akan diambil oleh konfigurasi otomatis Spring Boot 2."
k_o_
Bagaimana Anda menempatkan kelas liquibase.ext, apakah saya perlu mendefinisikan paket itu dalam proyek saya?
akuma8
Saya mendefinisikan paket itu dalam proyek saya, tampaknya berfungsi tetapi saya tidak dapat memverifikasinya. Saya mendefinisikan @PostConstructmetode dengan pesan log tetapi saya tidak melihatnya dicetak.
akuma8
@ akuma8: Ya, buat saja paket di proyek Anda dengan nama itu. Di mana Anda mendefinisikan @PostConstructmetode? Di ForceReleaseLockService? Ini bukan layanan Musim Semi, jadi ini tidak akan diminta.
k_o_
3

Terkadang memotong atau menjatuhkan meja DATABASECHANGELOGLOCK tidak berfungsi. Saya menggunakan database PostgreSQL dan sering menemukan masalah ini. Apa yang saya lakukan untuk menyelesaikannya adalah mengembalikan pernyataan yang sudah disiapkan yang berjalan di latar belakang untuk database itu. Cobalah untuk mengembalikan semua pernyataan yang disiapkan dan mencoba perubahan liquibase lagi.

SQL:

SELECT gid FROM pg_prepared_xacts WHERE database='database_name';

Jika pernyataan di atas mengembalikan catatan apa pun, maka kembalikan pernyataan yang disiapkan itu dengan mengikuti pernyataan SQL.

ROLLBACK PREPARED 'gid_obtained_from_above_SQL';
Koushik Ravulapelli
sumber
1

Anda dapat dengan aman menghapus tabel secara manual atau menggunakan kueri. Ini akan dibuat ulang secara otomatis.

DROP TABLE DATABASECHANGELOGLOCK;
Mike
sumber
0

Saya menghargai ini bukan masalah OP, tetapi saya mengalami masalah ini baru-baru ini dengan alasan yang berbeda. Sebagai referensi, saya menggunakan plugin Liquibase Maven (liquibase-maven-plugin: 3.1.1) dengan SQL Server.

Ngomong-ngomong, saya keliru menyalin dan menempelkan pernyataan "gunakan" SQL Server ke dalam salah satu skrip saya yang memindahkan basis data, jadi liquibase sedang menjalankan dan memutakhirkannya DATABASECHANGELOGLOCK, memperoleh kunci dalam basis data yang benar, tetapi kemudian memindahkan basis data untuk menerapkan perubahan. Bukan saja saya TIDAK bisa melihat perubahan atau audit liquibase di database yang benar, tetapi tentu saja, ketika saya menjalankan liquibase lagi, itu tidak dapat memperoleh kunci, karena kunci telah dirilis di database "salah", dan begitu juga masih terkunci di database "benar". Saya mengharapkan liquibase untuk memeriksa kunci masih diterapkan sebelum melepaskannya, dan mungkin itu adalah bug dalam liquibase (saya belum memeriksa), tetapi mungkin juga dialamatkan dalam versi yang lebih baru! Yang mengatakan, saya kira itu bisa dianggap fitur!

Cukup banyak kesalahan anak sekolah, saya tahu, tapi saya angkat di sini kalau-kalau ada yang mengalami masalah yang sama!

DarthPablo
sumber