Pular para o conteúdo

Aplicação protegida

GuardedApplier aplica um MergeResult sem conflitos somente se o banco de dados de destino ainda corresponder ao snapshot ativo esperado. Isso é útil quando um plano de mesclagem foi revisado anteriormente e as linhas ativas podem ter mudado antes da aplicação.

A configuração típica promove as edições de um ambiente de testes para um banco de dados ativo. A base é o banco ativo no momento em que o ambiente de testes foi copiado, ours é o ambiente de testes editado e theirs é o banco ativo no momento. O destino é o banco ativo, portanto o snapshot ativo esperado é theirs, e as operações a gravar são aquelas que o banco ativo ainda não tem (consulte Escolha das operações).

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() grava no banco de dados passado para Merql::init(). O snapshot ativo esperado deve descrever esse mesmo banco de dados: obtenha-o do destino e capture-o recentemente o bastante para que as pré-condições tenham validade.

Pré-condições

A SQL protegida adiciona pré-condições de linhas ativas às instruções geradas:

  • as atualizações exigem que a identidade da linha de destino exista e que os valores das colunas que não são de identidade correspondam à linha ativa esperada,
  • as exclusões exigem que a identidade da linha de destino exista e que os valores das colunas que não são de identidade correspondam à linha ativa esperada,
  • as inserções exigem que a identidade de destino ainda não exista.

Se alguma instrução afetar zero linhas, merql considera o plano desatualizado. A transação é revertida e o ApplyResult retornado contém o erro Optimistic precondition failed for guarded merge statement.

No MySQL, uma atualização cujos valores já são iguais aos da linha ativa também afeta zero linhas, porque MySQL conta as linhas alteradas, e não as correspondidas. Essa atualização falha como desatualizada, embora a linha corresponda (#2). SQLite conta as linhas correspondidas e a aceita. Isso pode acontecer com uma atualização merged quando ambos os lados fizeram a mesma alteração em uma linha.

Escolha das operações

Um resultado de mesclagem contém as operações de ambos os lados, pois transforma a base no estado mesclado. Aplicadas a um destino que já contém theirs, as operações que vieram de theirs descrevem alterações que o destino já tem, e suas pré-condições falham:

Operação Estado em um destino que contém theirs Selecionar
ours inserção, atualização ou exclusão Ausente Sim
merged atualização (ambos os lados alteraram a linha) Contém as colunas de theirs, faltam as de ours Sim
theirs inserção A linha existe, portanto a inserção protegida afeta zero linhas Não
merged exclusão (ambos os lados excluíram a linha) A linha foi removida, portanto a exclusão protegida afeta zero linhas Não
theirs atualização ou exclusão Já aplicada Não

Passar o resultado completo de Merql::merge() para applyGuarded(), portanto, falha assim que theirs inseriu uma linha ou ambos os lados excluíram uma. Selecione primeiro as operações, por origem, como acima, ou com um ChangeGroupSelection criado a partir de uma revisão UI. Exclua as linhas listadas em $plan->conflicts: um resultado selecionado está sempre sem conflitos, e a operação de uma linha em conflito carrega o valor de ours na coluna conflitante (consulte Planos de mesclagem).

Aplicação selecionada

A aplicação protegida funciona com resultados de mesclagem selecionados produzidos por um plano de mesclagem, incluindo seleções feitas por grupo de alterações.

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

Isso permite que um aplicativo host prepare um subconjunto dos grupos revisados e ainda confirme que as linhas ativas não divergiram desde a criação do plano.

Pré-visualização de SQL

Use SqlGenerator::generateGuarded() para inspecionar as instruções protegidas sem executá-las.

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";
}

As instruções geradas continuam parametrizadas. As pré-condições são expressas com cláusulas WHERE adicionais para atualizações/exclusões e verificações NOT EXISTS para inserções.

Aplicação protegida ou simples

Merql::apply() executa as mesmas operações sem pré-condições. Assim, atualizações e exclusões são concluídas independentemente de a linha ativa ter mudado desde a criação do plano, e uma inserção de uma linha existente falha com o erro de chave duplicada do banco de dados. Use a aplicação simples para um destino que você sabe que ainda contém a base, e a aplicação protegida quando outros processos possam ter alterado o destino desde que ele foi capturado.