Effettua aggiornamenti atomici tra codici a catena e canali

È possibile utilizzare le transazioni atomiche per completare più transazioni tra canali e codici concatenati in modo atomico.

Una transazione atomica è una serie indivisibile di operazioni di dati che hanno tutti successo o nessuna ha successo.

Le transazioni atomiche possono essere utili in situazioni complesse in cui più codici concatenati vengono distribuiti su canali separati. Puoi utilizzare le transazioni atomiche per mantenere la coerenza dei dati durante l'esecuzione di più transazioni blockchain, anche se si verifica un errore di rete o di sistema. Oracle Blockchain Platform supporta le transazioni atomiche utilizzando il protocollo di commit a due fasi, in cui una fase iniziale in cui viene preparata ogni operazione di dati è seguita da una fase in cui viene effettivamente eseguito il commit di ogni operazione di dati.

Le transazioni atomiche funzionano a livello di applicazione. In genere non è necessario modificare la logica del codice concatenato esistente per supportare le transazioni atomiche. Poiché uno o più argomenti aggiuntivi vengono aggiunti dal framework delle transazioni atomiche, assicurarsi che qualsiasi codice concatenato esistente non esegua controlli rigorosi sul numero di argomenti passati nel metodo del codice concatenato. Le transazioni atomiche sono supportate dall'endpoint API REST seguente:
  • restproxy/api/v2/atomicTransactions
L'endpoint dell'API REST prepara le transazioni come definito dal codice concatenato, quindi utilizza le funzioni del codice concatenato integrate per eseguire il commit di tutte le transazioni o per eseguire il rollback di tutte le transazioni in caso di errori durante la fase di preparazione. Per ulteriori informazioni sugli endpoint REST da utilizzare per implementare le transazioni atomiche, vedere Endpoint REST delle transazioni atomiche.

Ogni transazione atomica è composta da due o più transazioni blockchain. Il risultato (il valore returnCode) della transazione atomica è Success o Failure. In una transazione atomica, ogni transazione blockchain richiesta viene suddivisa in due operazioni distinte: una fase di preparazione e quindi una fase di commit o di rollback.

  • Nella fase di preparazione, ogni transazione viene girata come al solito, ma invece di essere finalizzata, le modifiche vengono posizionate nell'area intermedia e i valori vengono bloccati per evitare che altre transazioni modifichino i valori nell'area intermedia.
  • Se la fase di preparazione riesce per ogni transazione blockchain, le transazioni vengono approvate e impegnate utilizzando un codice concatenato integrato. I valori precedentemente bloccati vengono sbloccati e il risultato della transazione atomica è Success.
  • Se la fase di preparazione non riesce per qualsiasi transazione blockchain, allora tutte le altre transazioni in cui la fase di preparazione è riuscita vengono sottoposte a rollback, ancora una volta utilizzando il codice concatenato integrato. Le modifiche posizionate nell'area intermedia vengono rimosse e i valori bloccati in precedenza vengono sbloccati. Il risultato della transazione atomica è Failure.
Poiché le transazioni atomiche funzionano mediante chiavi di blocco, è possibile che venga visualizzato un errore Two_Phase_Commit_Lock se una transazione diversa tenta di modificare una chiave bloccata da una transazione atomica attualmente attiva preparata. Ciò può verificarsi in uno dei seguenti due scenari:
  • Una transazione atomica è ancora in fase di preparazione e una transazione diversa tenta di modificare una chiave bloccata dalla transazione preparata. In questo caso, il sistema funziona come progettato. Se si verifica questo errore, riprovare la seconda transazione. Questo è analogo al modo in cui le applicazioni gestiscono gli errori di lettura fantasma o gli errori MVCC (Multi-version Concurrency Control).
  • Il valore GlobalStatus restituito dalla transazione atomica è HeuristicOutcome. In questo caso, un'operazione di transazione atomica è stata annullata perché una delle operazioni di commit non è riuscita. Si tratta di un evento raro e potrebbe essere necessario risolverlo manualmente. Un effetto collaterale di un risultato euristico è che alcune chiavi potrebbero essere lasciate bloccate da transazioni di cui non è stato eseguito il commit o il rollback. In questo caso, utilizzare l'endpoint API REST seguente per sbloccare la transazione atomica:
    • restproxy/api/v2/atomicTransactions/{globalTransactionId}
    Per ulteriori informazioni sull'endpoint REST da utilizzare per sbloccare le transazioni atomiche, vedere Sblocca transazione atomica.

Scenario: esplora transazioni atomiche mediante campioni

Si consideri l'esempio seguente, che utilizza due dei codici concatenati di esempio inclusi in Oracle Blockchain Platform, Balance Transfer e Marbles. Il campione Trasferimento saldo rappresenta due parti con la possibilità di trasferire fondi tra saldi conto. Il campione Marmi consente di creare marmi e scambiarli tra proprietari. È possibile utilizzare transazioni individuali (non atomiche) per acquistare un marmo scambiando fondi nel codice concatenato di Balance Transfer e cambiando la proprietà del marmo nel codice concatenato Marmi. Tuttavia, se si verifica un errore con una di queste transazioni, il libro mastro potrebbe essere lasciato in uno stato incoerente: o i fondi sono stati trasferiti ma non il marmo o il marmo viene trasferito ma non pagato.

In questo scenario, è possibile utilizzare il codice concatenato esistente con gli endpoint API REST che supportano le transazioni atomiche. Lo scambio di fondi e il trasferimento di proprietà del marmo devono avere successo o entrambi fallire. Se una transazione rileva un errore, non viene eseguito il commit della transazione. Per esplorare questo scenario, effettuare le operazioni riportate di seguito.

  1. Installare i campioni di Balance Transfer e Marbles su canali diversi. Per ulteriori informazioni sull'installazione degli esempi, vedere Esplora Oracle Blockchain Platform Using Samples.
  2. Nel campione Marmi, invocare l'azione Create a new marble per creare una serie di marmi per vari proprietari di marmo.
  3. Utilizzare l'endpoint REST Invoke Atomic Transaction per completare le transazioni atomiche che richiamano sia i Marmi che gli esempi di trasferimento saldo.

Ad esempio, la transazione seguente trasferisce un marmo denominato marble1 a Tom e invia 50 monete dal conto a al conto b.

{
 "transactions": [
   {"chaincode":"obcs-marbles","args":["transferMarble", "marble1", "tom"],"timeout":0, "channel":"goods"},
   {"chaincode":"obcs-example02","args":["invoke", "a", "b", "50"],"timeout":0, "channel":"wallet"}
 ],
 "isolationLevel": "serializable",
 "prepareTimeout": 10000,
 "sync": true
}

Nella transazione precedente, se entrambe le transazioni hanno esito positivo nella fase di preparazione, entrambe le transazioni vengono impegnate nel libro contabile. Se si verifica un errore in una delle due transazioni, non viene eseguito il commit della transazione durante la seconda fase. Viene invece eseguito il rollback di entrambe le transazioni. Ad esempio, se sono presenti meno di 50 monete in conto a, non viene prelevato alcun denaro dal conto e non viene trasferito alcun marmo a Tom.