Lewati ke konten

Penerapan Terlindungi

GuardedApplier hanya menerapkan MergeResult yang bersih jika database target masih cocok dengan snapshot aktif yang diharapkan. Ini berguna saat rencana penggabungan telah ditinjau sebelumnya dan baris aktif mungkin berubah sebelum penerapan.

Penyiapan umumnya mempromosikan suntingan sandbox ke database aktif. Base adalah database aktif saat sandbox disalin darinya, ours adalah sandbox yang telah diedit, dan theirs adalah database aktif saat ini. Targetnya adalah database aktif, jadi snapshot aktif yang diharapkan adalah theirs, dan operasi yang akan ditulis adalah yang belum ada di database aktif (lihat Memilih operasi).

PHP
use Merql\Connection;
use Merql\Merql;
use Merql\Plan\OperationSelection;
use Merql\Plan\SelectedMergeResultFactory;
use Merql\Snapshot\SnapshotStore;

Merql::init(Connection::sqlite('/data/live.sqlite'));

$plan = Merql::plan('promote-1', 'base', 'ours', 'theirs');

// Skip rows with unresolved conflicts.
$conflicted = [];
foreach ($plan->conflicts as $conflict) {
    $conflicted[$conflict->table . "\0" . $conflict->rowKey] = true;
}

$ids = [];
foreach ($plan->operations as $operation) {
    $missing = $operation->source === 'ours'
        || ($operation->source === 'merged' && $operation->type === 'update');

    if ($missing && !isset($conflicted[$operation->table . "\0" . $operation->rowKey])) {
        $ids[] = $operation->id;
    }
}

$selected = (new SelectedMergeResultFactory())->fromOperationSelection(
    $plan,
    OperationSelection::fromIds($ids),
    SnapshotStore::load('base'),
);

$applied = Merql::applyGuarded($selected, SnapshotStore::load('theirs'));

if ($applied->hasErrors()) {
    foreach ($applied->errors() as $error) {
        echo $error . "\n";
    }
}

Merql::applyGuarded() menulis ke database yang diteruskan ke Merql::init(). Snapshot aktif yang diharapkan harus menggambarkan database yang sama: ambil dari target dan pastikan cukup baru agar prasyaratnya bermakna.

Prasyarat

SQL menambahkan prasyarat baris aktif ke pernyataan yang dihasilkan:

  • pembaruan mengharuskan identitas baris target ada dan nilai kolom non-identitasnya cocok dengan baris aktif yang diharapkan,
  • penghapusan mengharuskan identitas baris target ada dan nilai kolom non-identitasnya cocok dengan baris aktif yang diharapkan,
  • penyisipan mengharuskan identitas target belum ada.

Jika ada pernyataan yang memengaruhi nol baris, merql menganggap rencana sudah kedaluwarsa. Transaksi dibatalkan dan ApplyResult yang dikembalikan berisi kesalahan Optimistic precondition failed for guarded merge statement.

Pada MySQL, pembaruan yang nilainya sudah sama dengan baris aktif juga memengaruhi nol baris, karena MySQL menghitung baris yang berubah, bukan baris yang cocok. Pembaruan seperti itu gagal karena dianggap kedaluwarsa meskipun barisnya cocok (#2). SQLite menghitung baris yang cocok dan menerimanya. Ini dapat terjadi pada pembaruan merged ketika kedua sisi membuat perubahan yang sama pada suatu baris.

Memilih operasi

Hasil penggabungan memuat operasi dari kedua sisi karena hasil itu mengubah base menjadi keadaan gabungan. Jika diterapkan ke target yang sudah memuat theirs, operasi yang berasal dari theirs menggambarkan perubahan yang sudah ada pada target, sehingga prasyaratnya gagal:

Operasi Keadaan pada target yang memuat theirs Pilih
ours insert, update, atau delete Tidak ada Ya
merged update (kedua sisi mengubah baris) Memiliki kolom theirs, tidak memiliki kolom ours Ya
theirs insert Baris sudah ada, sehingga insert yang dijaga memengaruhi nol baris Tidak
merged delete (kedua sisi menghapus baris) Baris sudah tidak ada, sehingga delete yang dijaga memengaruhi nol baris Tidak
theirs update atau delete Sudah diterapkan Tidak

Karena itu, meneruskan seluruh hasil Merql::merge() ke applyGuarded() akan gagal begitu theirs menyisipkan baris atau kedua sisi menghapus baris. Pilih operasi terlebih dahulu, berdasarkan sumber seperti di atas atau dengan ChangeGroupSelection yang dibuat dari UI tinjauan. Jangan sertakan baris yang tercantum di $plan->conflicts: hasil yang dipilih selalu bersih, dan operasi untuk baris yang berkonflik membawa nilai ours pada kolom yang berkonflik (lihat Rencana Penggabungan).

Penerapan Pilihan

Penerapan dengan penjagaan berfungsi pada hasil penggabungan pilihan yang dibuat dari rencana penggabungan, termasuk pilihan yang dibuat berdasarkan kelompok perubahan.

PHP
use Merql\Plan\ChangeGroupSelection;
use Merql\Plan\SelectedMergeResultFactory;
use Merql\Snapshot\SnapshotStore;

$selection = ChangeGroupSelection::fromIds([$plan->changeGroups[0]->id]);
$selected = (new SelectedMergeResultFactory())->fromChangeGroupSelection(
    $plan,
    $selection,
    SnapshotStore::load($plan->baseSnapshot),
);

$applied = Merql::applyGuarded($selected, SnapshotStore::load($plan->theirsSnapshot));

Ini memungkinkan aplikasi host menyiapkan subset kelompok yang telah ditinjau sekaligus memastikan baris aktif tidak berubah sejak rencana dibuat.

Pratinjau SQL

Gunakan SqlGenerator::generateGuarded() untuk memeriksa pernyataan yang dijaga tanpa menjalankannya.

PHP
use Merql\Apply\SqlGenerator;
use Merql\Snapshot\SnapshotStore;

$statements = SqlGenerator::generateGuarded(
    $selected,
    SnapshotStore::load($plan->theirsSnapshot),
);

foreach ($statements as $statement) {
    echo $statement['sql'] . "\n";
}

Pernyataan yang dihasilkan tetap menggunakan parameter. Prasyarat dinyatakan dengan klausa WHERE tambahan untuk update/delete dan pemeriksaan NOT EXISTS untuk insert.

Penerapan dengan penjagaan atau biasa

Merql::apply() menjalankan operasi yang sama tanpa prasyarat. Update dan delete kemudian berhasil terlepas dari apakah baris aktif berubah sejak rencana dibuat, sedangkan insert untuk baris yang sudah ada gagal dengan kesalahan kunci duplikat dari database. Gunakan penerapan biasa untuk target yang Anda ketahui masih memuat base, dan penerapan dengan penjagaan ketika penulis lain mungkin telah mengubah target sejak snapshot dibuat.