Pergunta

Eu estou trabalhando em um aplicativo que é suposto para criar produtos (como o transporte apólices de seguro) quando PayPal notificações instantâneas de pagamento são recebidos. Infelizmente, PayPal, por vezes, envia notificações duplicadas. Além disso, há um outro terceiro que está realizando atualizações de serviços web simultaneamente quando receber atualizações do PayPal também.

Aqui está um diagrama básico das tabelas de banco de dados envolvidos.

// table "package"
// columns packageID, policyID, other data...
// 
// table "insurancepolicy"
// columns policyID, coverageAmount, other data...

Aqui está um diagrama básico do que eu quero fazer:

using (SqlConnection conn = new SqlConnection(...))
{
  sqlTransaction sqlTrans = conn.BeginTransaction(IsolationLevel.RepeatableRead);

  // Calls a stored procedure that checks if the foreign key in the transaction table has a value.
  if (PackageDB.HasInsurancePolicy(packageID, conn))
  { 
    sqlTrans.Commit();
    return false;
  }

  // Insert row in foreign table.
  int policyID = InsurancePolicyDB.Insert(coverageAmount, conn);
  if (policyID <= 0)
  {
    sqlTrans.Rollback();
    return false;
  }

  // Assign foreign key to parent table.  If this fails, roll back everything.
  bool assigned = PackageDB.AssignPolicyID(packageID, policyID, conn);
  if (!assigned)
  {
    sqlTrans.Rollback();
    return false;
  }
}

Se houver dois (ou mais) threads (ou processos ou aplicações) fazendo isso ao mesmo tempo, eu quero que o primeiro segmento para bloquear a linha "pacote" enquanto ele não tem PolicyID, até que a política é criado eo PolicyID é atribuído a mesa o pacote. Em seguida, o bloqueio será liberado após o PolicyID é atribuído a mesa o pacote. É minha esperança que a outra discussão que está chamando este mesmo código fará uma pausa quando se lê a linha pacote para se certificar de que não tem uma PolicyID primeiro. Quando o bloqueio da primeira transação é liberada, é minha esperança que a segunda transação vai ver o PolicyID está lá e, portanto, voltar sem inserir nenhuma linha na tabela de política.

Nota:. Devido ao design de banco de dados CRUD, cada um dos procedimentos armazenados envolvidos quer Read (select), Criar (inserir), ou Atualização

Este é o uso correto do RepeatableRead isolamento da transação?

Graças.

Foi útil?

Solução

Seria mais seguro e mais limpo se insert into Policy apenas acertar alguns restrição de tabela singularidade na tentativa de inserir duplicado. Elevar o nível de isolamento pode diminuir a concorrência e levar a outros problemas desagradáveis ??como impasses.

Outra maneira é sempre inserção de linha política, em seguida, rolá-la de volta se pacote foi anexado a uma política já:

begin tran (read committed)

/* tentatively insert new Policy */
insert Policy

/* attach Package to Policy if it's still free */
update Package
  set Package.policy_id = @policy_id
  where Package.package_id = @package_id and Package.policy_id is null

if @@rowcount > 0
  commit
else
  rollback

Isso funciona melhor quando os conflitos são raros, o que parece ser o seu caso.

Outras dicas

Eu acredito que você está querendo realmente nível de isolamento Serializable. O problema é que dois segmentos podem passar a HasInsurancePolicyCheck (embora eu não tenho nenhuma idéia do que InsurancePolicyDB.Insert faria ou por que ele iria retornar 0)

Você tem muitas outras opções para isso também. Uma delas é usando uma fila de mensagens e processar essas solicitações em série si mesmo. Outra é usar sp_getapplock e bloqueio na chave única para esse pacote . Dessa forma, você não bloquear quaisquer mais linhas ou tabelas que você deve.

Eu concordo com a ideia de "fila de mensagens" na resposta de aaronjensen. Se você está preocupado com múltiplas threads simultâneos tentando atualizar a mesma linha de dados simultaneamente, você deve sim ter as roscas de inserção de seus dados em uma fila de trabalho, que é então processado sequencialmente por um único segmento. Isto reduz significativamente contenção no banco de dados, porque a tabela de destino é atualizado por apenas um segmento em vez de "N", e as operações da fila de trabalho são limitadas a inserções pelos tópicos de mensagens, e uma leitura / atualização dos dados processamento de rosca.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top