Crea aggiornamenti atomici tra codici concatenati 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 successo o che non hanno 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 sui dati è seguita da una fase in cui viene effettivamente eseguito il commit di ogni operazione sui 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 dal seguente endpoint API REST:
  • restproxy/api/v2/atomicTransactions
L'endpoint API REST prepara le transazioni in base a quanto definito dal codice concatenato e quindi utilizza le funzioni di 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 messe in attesa e i valori vengono bloccati per impedire ad altre transazioni di modificare i valori messi in attesa.
  • Se la fase di preparazione ha esito positivo per ogni transazione blockchain, le transazioni vengono approvate e sottoposte a commit utilizzando il 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, viene eseguito il rollback di tutte le altre transazioni in cui la fase di preparazione è riuscita, di nuovo utilizzando il codice concatenato integrato. Le modifiche inserite nell'area intermedia vengono rimosse e i valori bloccati in precedenza vengono sbloccati. Il risultato della transazione atomica è Failure.
Poiché le transazioni atomiche funzionano bloccando le chiavi, è possibile che venga visualizzato un errore Two_Phase_Commit_Lock se un'altra transazione tenta di modificare una chiave bloccata da una transazione atomica attualmente attiva preparata. Ciò può verificarsi in uno dei due scenari seguenti:
  • Una transazione atomica è ancora in fase preparata 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, provare a rieseguire la seconda transazione. Questo è analogo al modo in cui le applicazioni gestiscono errori di lettura fantasma o 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 bloccate da transazioni di cui non è stato eseguito il commit o il rollback. In questo caso, utilizzare il seguente endpoint API REST 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: esplorazione delle transazioni atomiche mediante campioni

Si consideri l'esempio riportato di seguito, 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. L'esempio Marmi consente di creare marmi e scambiarli tra i proprietari. È possibile utilizzare singole transazioni (non atomiche) per acquistare un marmo scambiando fondi nel codice a catena Balance Transfer e cambiando la proprietà del marmo nel codice a catena 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 dell'API REST che supportano le transazioni atomiche. Lo scambio di fondi e il trasferimento di proprietà del marmo devono avere successo o fallire entrambi. Se una delle due transazioni rileva un errore, non viene eseguito il commit di alcuna transazione. Per esplorare questo scenario, effettuare le operazioni riportate di seguito.

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

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 nel conto sono presenti meno di 50 monete a, non viene prelevato alcun denaro dal conto e nessun marmo viene trasferito a Tom.

Si è verificato un problema noto con l'esempio Marmi e il valore predefinito del campo Proprietario marmo. Per ulteriori informazioni, vedere: Problemi noti per Oracle Blockchain Platform.