Exemplos de semântica de transação do Neptune - Amazon Neptune

As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.

Exemplos de semântica de transação do Neptune

Os exemplos a seguir ilustram diferentes casos de uso para a semântica de transação no Amazon Neptune.

Exemplo 1: Inserir uma propriedade somente se ela não existir

Suponha que você queira garantir que uma propriedade seja definida apenas uma vez. Por exemplo, suponha que várias consultas estejam tentando atribuir uma pontuação de crédito a uma pessoa simultaneamente. Você só quer que uma instância da propriedade seja inserida, e as outras consultas falhem porque a propriedade já foi definida.

# GREMLIN: g.V('person1').hasLabel('Person').coalesce(has('creditScore'), property('creditScore', 'AAA+')) # SPARQL: INSERT { :person1 :creditScore "AAA+" .} WHERE { :person1 rdf:type :Person . FILTER NOT EXISTS { :person1 :creditScore ?o .} }

A etapa property() do Gremlin insere uma propriedade com a chave e o valor fornecidos. A etapa coalesce() executa o primeiro argumento na primeira etapa e, se falhar, executa a segunda etapa:

Antes de inserir o valor da propriedade creditScore para um determinado vértice person1, uma transação deve tentar ler o valor de creditScore possivelmente inexistente de person1. Essa tentativa de leitura bloqueia o intervalo SP para S=person1 e P=creditScore no índice SPOG em que o valor de creditScore existe ou será gravado.

Usar esse bloqueio de intervalo impede que qualquer transação simultânea insira um valor de creditScore simultaneamente. Quando há várias transações paralelas, no máximo uma delas pode atualizar o valor de cada vez. Isso exclui a anomalia de mais de uma propriedade creditScore ser criada.

Exemplo 2: Afirmar que um valor de propriedade é globalmente exclusivo

Suponha que você queira inserir uma pessoa com um número de seguro social como uma chave primária. Você quer que sua consulta de mutação garanta que, em nível global, ninguém mais no banco de dados tenha o mesmo número de seguro social:

# GREMLIN: g.V().has('ssn', 123456789).fold() .coalesce(__.unfold(), __.addV('Person').property('name', 'John Doe').property('ssn', 123456789')) # SPARQL: INSERT { :person1 rdf:type :Person . :person1 :name "John Doe" . :person1 :ssn 123456789 .} WHERE { FILTER NOT EXISTS { ?person :ssn 123456789 } }

Este exemplo é semelhante ao anterior. A principal diferença é que o bloqueio do intervalo é feito no índice POGS em vez de no índice SPOG.

A transação que executa a consulta deve ler o padrão, ?person :ssn 123456789, no qual as posições P e O estão vinculadas. O bloqueio do intervalo é feito no índice POGS para P=ssn e O=123456789.

  • Se o padrão existir, nenhuma ação será executada.

  • Se não existir, o bloqueio impedirá que qualquer transação simultânea insira esse número de seguro social também

Exemplo 3: Alterar uma propriedade se outra propriedade tiver um valor especificado

Suponha que vários eventos em um jogo movam uma pessoa do nível um para o nível dois e atribuam a ela uma nova propriedade level2Score definida como zero. Você precisa ter certeza de que várias instâncias simultâneas dessa transação não possam criar várias instâncias da propriedade de pontuação de nível dois. As consultas em Gremlin e SPARQL podem ter a seguinte aparência.

# GREMLIN: g.V('person1').hasLabel('Person').has('level', 1) .property('level2Score', 0) .property(Cardinality.single, 'level', 2) # SPARQL: DELETE { :person1 :level 1 .} INSERT { :person1 :level2Score 0 . :person1 :level 2 .} WHERE { :person1 rdf:type :Person . :person1 :level 1 .}

No Gremlin, quando Cardinality.single é especificado, a etapa property() adiciona uma nova propriedade ou substitui um valor de propriedade existente pelo novo valor especificado.

Qualquer atualização em um valor de propriedade, como aumentar o level de 1 para 2, é implementada como uma exclusão do registro atual e a inserção de um novo registro com o novo valor de propriedade. Nesse caso, o registro com o nível de número 1 é excluído e um registro com o nível de número 2 é reinserido.

Para que a transação possa adicionar level2Score e atualizar o level de 1 para 2, ela deve primeiro validar se o valor de level atualmente é igual a 1. Ao fazer isso, ele usa um bloqueio de intervalo no prefixo SPO para S=person1, P=level e O=1 no índice SPOG. Esse bloqueio impede que transações simultâneas excluam o triplo da versão 1 e, como resultado, nenhuma atualização simultânea conflitante pode ocorrer.

Exemplo 4: Substituir uma propriedade existente

Alguns eventos podem atualizar a pontuação de crédito de uma pessoa para um novo valor (aqui BBB). Mas você quer ter certeza de que eventos simultâneos desse tipo não possam criar várias propriedades de pontuação de crédito para uma pessoa.

# GREMLIN: g.V('person1').hasLabel('Person') .sideEffect(properties('creditScore').drop()) .property('creditScore', 'BBB') # SPARQL: DELETE { :person1 :creditScore ?o .} INSERT { :person1 :creditScore "BBB" .} WHERE { :person1 rdf:type :Person . :person1 :creditScore ?o .}

Esse caso é semelhante ao exemplo 3, exceto que, em vez de bloquear o prefixo SPO, o Neptune bloqueia o prefixo SP apenas com S=person1 e P=creditScore. Isso evita que transações simultâneas insiram ou excluam triplos com a propriedade creditScore para o assunto person1.

Exemplo 5: Evitar propriedades ou bordas pendentes

A atualização em uma entidade não deve deixar um elemento pendente, ou seja, uma propriedade ou borda associada a uma entidade que não é tipada. Isso é apenas um problema emSPARQL, porque o Gremlin tem restrições embutidas para evitar elementos pendentes.

# SPARQL: tx1: INSERT { :person1 :age 23 } WHERE { :person1 rdf:type :Person } tx2: DELETE { :person1 ?p ?o }

A consulta INSERT deve ler e bloquear o prefixo SPO com S=person1, P=rdf:type e O=Person no índice SPOG. O bloqueio impede que a consulta DELETE seja bem-sucedida em paralelo.

Na corrida entre a tentativa da consulta DELETE excluir o registro :person1 rdf:type :Person e a consulta INSERT ler o registro e criar um bloqueio de intervalo em seu SPO no índice SPOG, os seguintes resultados são possíveis:

  • Se a consulta INSERT for confirmada antes da consulta DELETE ler e excluir todos os registros de :person1, :person1 será removido completamente do banco de dados, incluindo o registro recém-inserido.

  • Se a consulta DELETE for confirmada antes da consulta INSERT tentar ler o registro :person1 rdf:type :Person, a leitura observará a alteração confirmada. Ou seja, ela não encontra nenhum registro :person1 rdf:type :Person e, portanto, torna-se um no-op.

  • Se a INSERT consulta for lida antes da DELETE consulta, a :person1 rdf:type :Person tripla será bloqueada e a DELETE consulta será bloqueada até que a INSERT consulta seja confirmada, como no primeiro caso anterior.

  • Se a DELETE ler antes da consulta INSERT, e a consulta INSERT tentar ler e bloquear o prefixo SPO do registro, será detectado um conflito. Isso ocorre porque o triplo foi marcado para remoção, e a consulta INSERT falha.

Em todas essas diferentes sequências possíveis de eventos, nenhuma borda pendente é criada.