Driver Amazon QLDB per Go — Riferimento al ricettario - Database Amazon Quantum Ledger (Amazon QLDB)

Le traduzioni sono generate tramite traduzione automatica. In caso di conflitto tra il contenuto di una traduzione e la versione originale in Inglese, quest'ultima prevarrà.

Driver Amazon QLDB per Go — Riferimento al ricettario

Questa guida di riferimento mostra i casi d'uso più comuni del driver Amazon QLDB per Go. Fornisce esempi di codice Go che mostrano come utilizzare il driver per eseguire operazioni di creazione, lettura, lettura, lettura, lettura, lettura, lettura, lettura, lettura, lettura, lettura, lettura, lettura, lettura, lettura, lettura, lettura, lettura, lettura, lettura, lettura, lettura Include anche esempi di codice per l'elaborazione dei dati di Amazon Ion. Inoltre, questa guida evidenzia le migliori pratiche per rendere le transazioni idempotenti e implementare i vincoli di unicità.

Nota

Ove applicabile, alcuni casi d'uso presentano esempi di codice diversi per ciascuna versione principale supportata del driver QLDB per Go.

Importazione del driver

Il seguente esempio di codice importa il driver e altriAWS pacchetti richiesti.

3.x
import ( "github.com/amzn/ion-go/ion" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/qldbSession" "github.com/awslabs/amazon-qldb-driver-go/v3/qldbdriver" )
2.x
import ( "github.com/amzn/ion-go/ion" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/qldbsession" "github.com/awslabs/amazon-qldb-driver-go/v2/qldbdriver" )
Nota

Questo esempio importa anche il pacchetto Amazon Ion (amzn/ion-go/ion). È necessario questo pacchetto per elaborare i dati Ion quando si eseguono alcune operazioni sui dati in questo riferimento. Per ulteriori informazioni, consulta Come lavorare con Amazon Ion.

Esempio di creazione del driver

L'esempio di codice seguente crea un'istanza del driver che si connette a un nome di registro specificato in un datoRegione AWS.

3.x
cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { panic(err) } qldbSession := qldbsession.NewFromConfig(cfg, func(options *qldbsession.Options) { options.Region = "us-east-1" }) driver, err := qldbdriver.New( "vehicle-registration", qldbSession, func(options *qldbdriver.DriverOptions) { options.LoggerVerbosity = qldbdriver.LogInfo }) if err != nil { panic(err) } defer driver.Shutdown(context.Background())
2.x
awsSession := session.Must(session.NewSession(aws.NewConfig().WithRegion("us-east-1"))) qldbSession := qldbsession.New(awsSession) driver, err := qldbdriver.New( "vehicle-registration", qldbSession, func(options *qldbdriver.DriverOptions) { options.LoggerVerbosity = qldbdriver.LogInfo }) if err != nil { panic(err) }

Operazioni CRUD

QLDB esegue operazioni di creazione, lettura, lettura, lettura, lettura, aggiornamento ed eliminazione (CRUD) come parte di una transazione.

avvertimento

Come best practice, cerca di rendere le tue transazioni di scrittura rigorosamente idempotenti.

Rendere le transazioni idempotenti

Ti consigliamo di effettuare transazioni di scrittura idempotenti per evitare effetti collaterali imprevisti in caso di nuovi tentativi. Una transazione è idempotente se può essere eseguita più volte e produrre risultati identici ogni volta.

Ad esempio, si consideri una transazione che inserisce un documento in una tabella denominataPerson. La transazione deve innanzitutto verificare se il documento esiste già o meno nella tabella. Senza questo controllo, la tabella potrebbe contenere documenti duplicati.

Supponiamo che QLDB esegua correttamente la transazione sul lato server, ma che il client scada in attesa di una risposta. Se la transazione non è idempotente, lo stesso documento potrebbe essere inserito più di una volta in caso di nuovo tentativo.

Utilizzo degli indici per evitare la scansione completa delle tabelle

Si consiglia inoltre di eseguire istruzioni con una clausolaWHERE predicativa utilizzando un operatore di uguaglianza su un campo indicizzato o un ID di documento, ad esempioWHERE indexedField = 123 oWHERE indexedField IN (456, 789). Senza questa ricerca indicizzata, QLDB deve eseguire una scansione della tabella, che può portare a timeout delle transazioni o conflitti OCC (Optimistic Concurrency Control).

Per ulteriori informazioni su OCC, consultaModello di concorrenza Amazon QLDB.

Transazioni create implicitamente

La funzione QLDBDriver.execute accetta una funzione lambda che riceve un'istanza di Transaction, che è possibile utilizzare per eseguire istruzioni. L'istanza diTransaction avvolge una transazione creata implicitamente.

È possibile eseguire istruzioni all'interno della funzione lambda utilizzando laTransaction.Execute funzione. Il driver esegue implicitamente la transazione quando la funzione lambda ritorna.

Le sezioni seguenti mostrano come eseguire operazioni CRUD di base, specificare una logica di ripetizione personalizzata e implementare vincoli di unicità.

Creazione di tabelle

result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { return txn.Execute("CREATE TABLE Person") })

Creazione di indici

result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { return txn.Execute("CREATE INDEX ON Person(GovId)") })

Lettura di documenti

var decodedResult map[string]interface{} // Assumes that Person table has documents as follows: // { "GovId": "TOYENC486FH", "FirstName": "Brent" } _, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { result, err := txn.Execute("SELECT * FROM Person WHERE GovId = 'TOYENC486FH'") if err != nil { return nil, err } for result.Next(txn) { ionBinary := result.GetCurrentData() err = ion.Unmarshal(ionBinary, &decodedResult) if err != nil { return nil, err } fmt.Println(decodedResult) // prints map[GovId: TOYENC486FH FirstName:Brent] } if result.Err() != nil { return nil, result.Err() } return nil, nil }) if err != nil { panic(err) }

Utilizzo dei parametri della query

L'esempio di codice seguente utilizza un parametro di interrogazione di tipo nativo.

result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { return txn.Execute("SELECT * FROM Person WHERE GovId = ?", "TOYENC486FH") }) if err != nil { panic(err) }

Il seguente esempio di codice utilizza più parametri di interrogazione.

result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { return txn.Execute("SELECT * FROM Person WHERE GovId = ? AND FirstName = ?", "TOYENC486FH", "Brent") }) if err != nil { panic(err) }

L'esempio di codice seguente utilizza un elenco di parametri di interrogazione.

govIDs := []string{}{"TOYENC486FH", "ROEE1", "YH844"} result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { return txn.Execute("SELECT * FROM Person WHERE GovId IN (?,?,?)", govIDs...) }) if err != nil { panic(err) }
Nota

Quando si esegue una query senza una ricerca indicizzata, viene richiamata una scansione completa della tabella. In questo esempio, consigliamo di avere un indice sulGovId campo per ottimizzare le prestazioni. Senza un indice attivoGovId, le query possono avere una maggiore latenza e possono anche portare a eccezioni di conflitto OCC o a timeout delle transazioni.

Inserimento di documenti

Il seguente esempio di codice inserisce tipi di dati nativi.

_, err = driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { // Check if a document with a GovId of TOYENC486FH exists // This is critical to make this transaction idempotent result, err := txn.Execute("SELECT * FROM Person WHERE GovId = ?", "TOYENC486FH") if err != nil { return nil, err } // Check if there are any results if result.Next(txn) { // Document already exists, no need to insert } else { person := map[string]interface{}{ "GovId": "TOYENC486FH", "FirstName": "Brent", } _, err = txn.Execute("INSERT INTO Person ?", person) if err != nil { return nil, err } } return nil, nil })

Questa transazione inserisce un documento nellaPerson tabella. Prima dell'inserimento, controlla innanzitutto se il documento esiste già nella tabella. Questo controllo rende la transazione di natura idempotente. Anche se esegui questa transazione più volte, non causerà effetti collaterali indesiderati.

Nota

In questo esempio, consigliamo di avere un indice sulGovId campo per ottimizzare le prestazioni. Senza un indice attivoGovId, le dichiarazioni possono avere una maggiore latenza e possono anche portare a eccezioni di conflitto OCC o a timeout delle transazioni.

Inserimento di più documenti in un'unica dichiarazione

Per inserire più documenti utilizzando una singolaINSERT istruzione, è possibile passare un elenco di parametri di tipo all'istruzione come segue.

// people is a list txn.Execute("INSERT INTO People ?", people)

Non si racchiude la variabile placeholder (?) tra parentesi a doppio angolo (<<...>>) quando si passa un elenco. Nelle istruzioni PartiQL, le parentesi a doppio angolo indicano una raccolta non ordinata nota come borsa.

Aggiornamento dei documenti

Il seguente esempio di codice utilizza tipi di dati nativi.

result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { return txn.Execute("UPDATE Person SET FirstName = ? WHERE GovId = ?", "John", "TOYENC486FH") })
Nota

In questo esempio, consigliamo di avere un indice sulGovId campo per ottimizzare le prestazioni. Senza un indice attivoGovId, le dichiarazioni possono avere una maggiore latenza e possono anche portare a eccezioni di conflitto OCC o a timeout delle transazioni.

Eliminazione di documenti

Il seguente esempio di codice utilizza tipi di dati nativi.

result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { return txn.Execute("DELETE FROM Person WHERE GovId = ?", "TOYENC486FH") })
Nota

In questo esempio, consigliamo di avere un indice sulGovId campo per ottimizzare le prestazioni. Senza un indice attivoGovId, le dichiarazioni possono avere una maggiore latenza e possono anche portare a eccezioni di conflitto OCC o a timeout delle transazioni.

Esecuzione di più rendiconti in una transazione

// This code snippet is intentionally trivial. In reality you wouldn't do this because you'd // set your UPDATE to filter on vin and insured, and check if you updated something or not. func InsureCar(driver *qldbdriver.QLDBDriver, vin string) (bool, error) { insured, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { result, err := txn.Execute( "SELECT insured FROM Vehicles WHERE vin = ? AND insured = FALSE", vin) if err != nil { return false, err } hasNext := result.Next(txn) if !hasNext && result.Err() != nil { return false, result.Err() } if hasNext { _, err = txn.Execute( "UPDATE Vehicles SET insured = TRUE WHERE vin = ?", vin) if err != nil { return false, err } return true, nil } return false, nil }) if err != nil { panic(err) } return insured.(bool), err }

Logica di ripetizione dei tentativi

LaExecute funzione del driver dispone di un meccanismo di ripetizione integrato che riprova la transazione se si verifica un'eccezione ripetibile (ad esempio timeout o conflitti OCC). Il numero massimo di tentativi di ripetizione e la strategia di backoff sono configurabili.

Il limite di tentativi predefinito è4 e la strategia di backoff predefinita è ExponentialBackoffStrategybasata su10 millisecondi. È possibile impostare la politica di ripetizione per istanza del driver e anche per transazione utilizzando un'istanza di RetryPolicy.

L'esempio di codice seguente specifica la logica dei tentativi con un limite di tentativi personalizzato e una strategia di backoff personalizzata per un'istanza del driver.

import ( "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/qldbsession" "github.com/awslabs/amazon-qldb-driver-go/v2/qldbdriver" ) func main() { awsSession := session.Must(session.NewSession(aws.NewConfig().WithRegion("us-east-1"))) qldbSession := qldbsession.New(awsSession) // Configuring retry limit to 2 retryPolicy := qldbdriver.RetryPolicy{MaxRetryLimit: 2} driver, err := qldbdriver.New("test-ledger", qldbSession, func(options *qldbdriver.DriverOptions) { options.RetryPolicy = retryPolicy }) if err != nil { panic(err) } // Configuring an exponential backoff strategy with base of 20 milliseconds retryPolicy = qldbdriver.RetryPolicy{ MaxRetryLimit: 2, Backoff: qldbdriver.ExponentialBackoffStrategy{SleepBase: 20, SleepCap: 4000, }} driver, err = qldbdriver.New("test-ledger", qldbSession, func(options *qldbdriver.DriverOptions) { options.RetryPolicy = retryPolicy }) if err != nil { panic(err) } }

L'esempio di codice seguente specifica la logica dei tentativi con un limite di tentativi personalizzato e una strategia di backoff personalizzata per una particolare funzione anonima. LaSetRetryPolicy funzione sostituisce la politica di ripetizione impostata per l'istanza del driver.

import ( "context" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/qldbsession" "github.com/awslabs/amazon-qldb-driver-go/v2/qldbdriver" ) func main() { awsSession := session.Must(session.NewSession(aws.NewConfig().WithRegion("us-east-1"))) qldbSession := qldbsession.New(awsSession) // Configuring retry limit to 2 retryPolicy1 := qldbdriver.RetryPolicy{MaxRetryLimit: 2} driver, err := qldbdriver.New("test-ledger", qldbSession, func(options *qldbdriver.DriverOptions) { options.RetryPolicy = retryPolicy1 }) if err != nil { panic(err) } // Configuring an exponential backoff strategy with base of 20 milliseconds retryPolicy2 := qldbdriver.RetryPolicy{ MaxRetryLimit: 2, Backoff: qldbdriver.ExponentialBackoffStrategy{SleepBase: 20, SleepCap: 4000, }} // Overrides the retry policy set by the driver instance driver.SetRetryPolicy(retryPolicy2) driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { return txn.Execute("CREATE TABLE Person") }) }

Implementazione dei vincoli di unicità

QLDB non supporta indici univoci, ma puoi implementare questo comportamento nella tua applicazione.

Si supponga di voler implementare un vincolo di unicità sulGovId campo dellaPerson tabella. Per eseguire questa operazione, è possibile scrivere una transazione che esegue le seguenti operazioni di creazione, lettura di una transazione che esegue le seguenti operazioni di creazione

  1. Asserisci che la tabella non ha documenti esistenti con un valore specificatoGovId.

  2. Inserisci il documento se l'asserzione è valida.

Se una transazione concorrente supera contemporaneamente l'asserzione, solo una delle transazioni verrà confermata con successo. L'altra transazione avrà esito negativo con un'eccezione di conflitto OCC.

L'esempio di codice seguente mostra come implementare questa logica di vincolo di unicità.

govID := "TOYENC486FH" document := map[string]interface{}{ "GovId": "TOYENC486FH", "FirstName": "Brent", } result, err := driver.Execute(context.Background(), func(txn qldbdriver.Transaction) (interface{}, error) { // Check if doc with GovId = govID exists result, err := txn.Execute("SELECT * FROM Person WHERE GovId = ?", govID) if err != nil { return nil, err } // Check if there are any results if result.Next(txn) { // Document already exists, no need to insert return nil, nil } return txn.Execute("INSERT INTO Person ?", document) }) if err != nil { panic(err) }
Nota

In questo esempio, consigliamo di avere un indice sulGovId campo per ottimizzare le prestazioni. Senza un indice attivoGovId, le dichiarazioni possono avere una maggiore latenza e possono anche portare a eccezioni di conflitto OCC o a timeout delle transazioni.

Come lavorare con Amazon Ion

Le sezioni seguenti mostrano come utilizzare i dati Amazon Ion per elaborare i dati Ion.

Importazione del modulo Ion

import "github.com/amzn/ion-go/ion"

Creazione di tipi di Ion

La libreria Ion per Go attualmente non supporta il Document Object Model (DOM), quindi non è possibile creare tipi di dati Ion. Ma puoi distinguere e deselezionare tra i tipi nativi di Go e i binari Ion quando lavori con QLDB.

Come ottenere Ion Binary

aDict := map[string]interface{}{ "GovId": "TOYENC486FH", "FirstName": "Brent", } ionBytes, err := ion.MarshalBinary(aDict) if err != nil { panic(err) } fmt.Println(ionBytes) // prints [224 1 0 234 238 151 129 131 222 147 135 190 144 133 71 111 118 73 100 137 70 105 114 115 116 78 97 109 101 222 148 138 139 84 79 89 69 78 67 52 56 54 70 72 139 133 66 114 101 110 116]

Come ottenere testo Ion

aDict := map[string]interface{}{ "GovId": "TOYENC486FH", "FirstName": "Brent", } ionBytes, err := ion.MarshalText(aDict) if err != nil { panic(err) } fmt.Println(string(ionBytes)) // prints {FirstName:"Brent",GovId:"TOYENC486FH"}

Per ulteriori informazioni su Ion, consulta la documentazione di Amazon Ion su GitHub. Per altri esempi di codice sull'utilizzo di Ion in QLDB, vedereUtilizzo dei tipi di dati Amazon Ion in Amazon QLDB.