QLDBDriver da Amazon para Go — Referência do livro de receitas - Banco de dados Amazon Quantum Ledger (AmazonQLDB)

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á.

QLDBDriver da Amazon para Go — Referência do livro de receitas

Importante

Aviso de fim do suporte: os clientes existentes poderão usar a Amazon QLDB até o final do suporte em 31/07/2025. Para obter mais detalhes, consulte Migrar um Amazon QLDB Ledger para o Amazon Aurora Postgre. SQL

Este guia de referência mostra casos de uso comuns do QLDB driver Amazon para Go. Ele fornece exemplos de código Go que demonstram como usar o driver para executar operações básicas de criação, leitura, atualização e exclusão (CRUD). Também inclui exemplos de código para processamento de dados do Amazon Ion. Além disso, este guia destaca as práticas recomendadas para tornar as transações idempotentes e implantar restrições de exclusividade.

nota

Quando aplicável, alguns casos de uso têm exemplos de código diferentes para cada versão principal compatível do QLDB driver para Go.

Importação do driver

O exemplo de código a seguir importa o driver e outros AWS pacotes necessários.

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

Este exemplo também importa o pacote Amazon Ion (amzn/ion-go/ion). Você precisa desse pacote para processar dados de íons ao executar algumas operações de dados nesta referência. Para saber mais, consulte Como trabalhar com o Amazon Ion.

Instanciação do driver

O exemplo de código a seguir cria uma instância do driver que se conecta a um nome de ledger especificado em um Região da AWS especificado.

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) }

CRUDoperações

QLDBexecuta operações de criação, leitura, atualização e exclusão (CRUD) como parte de uma transação.

Atenção

Como prática recomendada, torne suas transações de gravação estritamente idempotentes.

Tornar as transações idempotentes

Recomendamos que você torne as transações de gravação idempotentes para evitar efeitos colaterais inesperados no caso de novas tentativas. Uma transação é idempotente se puder ser executada várias vezes e produzir resultados idênticos a cada vez.

Por exemplo, considere uma transação que insere um documento em uma tabela chamada Person. A transação deve primeiro verificar se o documento já existe ou não na tabela. Sem essa verificação, a tabela pode acabar com documentos duplicados.

Suponha que a transação seja confirmada QLDB com sucesso no lado do servidor, mas o cliente expire enquanto espera por uma resposta. Se a transação não for idempotente, o mesmo documento poderá ser inserido mais de uma vez no caso de uma nova tentativa.

Usar índices para evitar varreduras completas da tabela

Também recomendamos executar instruções com uma cláusula de predicado WHERE usando um operador de igualdade em um campo indexado ou em um ID de documento, por exemplo, WHERE indexedField = 123 ou WHERE indexedField IN (456, 789). Sem essa pesquisa indexada, é QLDB necessário fazer uma varredura da tabela, o que pode levar a tempos limite de transação ou conflitos otimistas de controle de simultaneidade (). OCC

Para obter mais informações sobre o OCC, consulte Modelo de QLDB concorrência da Amazon.

Transações criadas implicitamente

A função QLDBDriver.Execute aceita uma função lambda que recebe uma instância de Transaction, que você pode usar para executar instruções. A instância de Transaction envolve uma transação criada implicitamente.

Você pode executar instruções na função do Lambda usando a função Transaction.Execute. O driver confirma implicitamente a transação quando a função do Lambda retorna.

As seções a seguir mostram como executar CRUD operações básicas, especificar a lógica de repetição personalizada e implementar restrições de exclusividade.

Criar tabelas

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

Criar índices

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

Ler documentos

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) }

Usar parâmetros de consulta

O exemplo de código a seguir usa um parâmetro de consulta do 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) }

O exemplo de código a seguir usa múltiplos parâmetros de consulta.

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) }

O exemplo de código a seguir usa uma lista de parâmetros de consulta.

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 você executa uma consulta sem uma pesquisa indexada, ela invoca uma verificação completa da tabela. Neste exemplo, recomendamos ter um índice no campo GovId para otimizar o desempenho. Sem um índice ativadoGovId, as consultas podem ter mais latência e também podem levar a exceções de OCC conflito ou tempos limite de transação.

Inserir documentos

Os exemplos de código a seguir inserem os tipos de dados nativos.

_, 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 })

Essa transação insere um documento na tabela Person. Antes de inserir, ele primeiro verifica se o documento já existe na tabela. Essa verificação torna a transação idempotente por natureza. Mesmo que você execute essa transação várias vezes, ela não causará efeitos colaterais indesejados.

nota

Neste exemplo, recomendamos ter um índice no campo GovId para otimizar o desempenho. Sem um índice ativadoGovId, as declarações podem ter mais latência e também podem levar a exceções de OCC conflito ou tempos limite de transação.

Inserindo vários documentos em uma instrução

Para inserir vários documentos usando uma única instrução INSERT, você pode passar um parâmetro do tipo lista para a instrução da seguinte maneira.

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

Você não coloca o marcador variável (?) entre colchetes angulares duplos (<<...>>) ao passar uma lista. Nas instruções manuais do PartiQL, colchetes angulares duplos denotam uma coleção não ordenada conhecida como bolsa.

Como atualizar documentos

Os exemplos de código a seguir usam tipos de dados nativos.

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

Neste exemplo, recomendamos ter um índice no campo GovId para otimizar o desempenho. Sem um índice ativadoGovId, as declarações podem ter mais latência e também podem levar a exceções de OCC conflito ou tempos limite de transação.

Como excluir documentos

Os exemplos de código a seguir usam tipos de dados nativos.

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

Neste exemplo, recomendamos ter um índice no campo GovId para otimizar o desempenho. Sem um índice ativadoGovId, as declarações podem ter mais latência e também podem levar a exceções de OCC conflito ou tempos limite de transação.

Executando várias instruções em uma transação

// 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 }

Lógica de novas tentativas

A Execute função do driver tem um mecanismo de repetição integrado que repete a transação se ocorrer uma exceção que possa ser repetida (como tempos limite ou conflitos). OCC O número máximo de tentativas de repetição e a estratégia de recuo são configuráveis.

O limite padrão de repetição é4, e a estratégia de recuo padrão é ExponentialBackoffStrategycom uma base de 10 milissegundos. Você pode definir a política de repetição por instância de driver e também por transação usando uma instância de RetryPolicy.

O exemplo de código a seguir especifica a lógica de repetição com um limite de repetição personalizado e uma estratégia de recuo personalizada para uma instância do 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) } }

O exemplo de código a seguir especifica a lógica de repetição com um limite de repetição personalizado e uma estratégia de recuo personalizada para uma instância anônima particular. A função SetRetryPolicy substitui a política de repetição definida para a instância do 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") }) }

Implementação de restrições de exclusividade

QLDBnão oferece suporte a índices exclusivos, mas você pode implementar esse comportamento em seu aplicativo.

Suponha que você queira implementar uma restrição de exclusividade no campo GovId da tabela Person. Para fazer isso, você pode escrever uma transação que faça o seguinte:

  1. Afirme que a tabela não tem documentos existentes com um GovId especificado.

  2. Insira o documento se a afirmação for aprovada.

Se uma transação concorrente passar simultaneamente pela declaração, somente uma das transações será confirmada com sucesso. A outra transação falhará com uma exceção de OCC conflito.

O exemplo de código a seguir mostra como implementar essa lógica de restrição de exclusividade.

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

Neste exemplo, recomendamos ter um índice no campo GovId para otimizar o desempenho. Sem um índice ativadoGovId, as declarações podem ter mais latência e também podem levar a exceções de OCC conflito ou tempos limite de transação.

Como trabalhar com o Amazon Ion

As seções a seguir mostram como usar o módulo Amazon Ion para processar dados do Ion.

Importar o módulo Ion

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

Criação de tipos de Ion

Atualmente, a biblioteca Ion para Go não oferece suporte ao Document Object Model (DOM), portanto, você não pode criar tipos de dados Ion. Mas você pode organizar e desorganizar entre os tipos nativos de Go e o binário de íons ao trabalhar com. QLDB

Obter binário Ion

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]

Obter texto 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"}

Para obter mais informações sobre o Ion, consulte a documentação do Amazon Ion em GitHub. Para obter mais exemplos de código de como trabalhar com o Ion inQLDB, consulteTrabalhando com os tipos de dados Amazon Ion na Amazon QLDB.