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á.
Driver Amazon QLDB para Node.js: referência do livro de receitas
Importante
Aviso de fim do suporte: os clientes existentes poderão usar o Amazon QLDB até o final do suporte em 31/07/2025. Para obter mais detalhes, consulte Migrar um Amazon QLDB Ledger para o Amazon
Este guia de referência mostra casos de uso comuns do driver Amazon QLDB para Node.js. Ele fornece JavaScript exemplos de TypeScript código 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.
Sumário
Importação do driver
O exemplo de código a seguir importa o driver.
var qldb = require('amazon-qldb-driver-nodejs');
var ionjs = require('ion-js');
nota
Este exemplo também importa o pacote Amazon Ion (ion-js
). 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 usando as configurações padrão.
const qldbDriver = new qldb.QldbDriver("vehicle-registration");
Operações de CRUD
O QLDB executa 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 o QLDB confirme com sucesso a transação no lado do servidor, mas o tempo do 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, o QLDB precisa fazer uma varredura de 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 OCC, consulte Modelo de simultaneidade do Amazon QLDB.
Transações criadas implicitamente
O método QldbDriver.executeLambdaTransactionExecutor
envolve uma transação criada implicitamente.
Você pode executar instruções na função do Lambda usando o método de execução
nota
O método execute
suporta valores nos tipos Amazon Ion e nos tipos nativos do Node.js. Se você passar um tipo nativo do Node.js como argumento para execute
, o driver o converterá em um tipo Ion usando o módulo ion-js
(desde que a conversão para o determinado tipo de dados do Node.js seja suportada). Para ver os tipos de dados e as regras de conversão compatíveis, consulte o Ion JavaScript DOM README.
As seções a seguir mostram como executar operações CRUD básicas, especificar a lógica de repetição personalizada e implementar restrições de exclusividade.
Sumário
Criar tabelas
(async function() {
await qldbDriver.executeLambda(async (txn) => {
await txn.execute("CREATE TABLE Person");
});
})();
Criar índices
(async function() {
await qldbDriver.executeLambda(async (txn) => {
await txn.execute("CREATE INDEX ON Person (GovId)");
});
})();
Ler documentos
(async function() {
// Assumes that Person table has documents as follows:
// { "GovId": "TOYENC486FH", "FirstName": "Brent" }
await qldbDriver.executeLambda(async (txn) => {
const results = (await txn.execute("SELECT * FROM Person WHERE GovId = 'TOYENC486FH'")).getResultList();
for (let result of results) {
console.log(result.get('GovId')); // prints [String: 'TOYENC486FH']
console.log(result.get('FirstName')); // prints [String: 'Brent']
}
});
}());
Usar parâmetros de consulta
O exemplo de código a seguir usa um parâmetro de consulta do tipo nativo.
(async function() {
// Assumes that Person table has documents as follows:
// { "GovId": "TOYENC486FH", "FirstName": "Brent" }
await qldbDriver.executeLambda(async (txn) => {
const results = (await txn.execute('SELECT * FROM Person WHERE GovId = ?', 'TOYENC486FH')).getResultList();
for (let result of results) {
console.log(result.get('GovId')); // prints [String: 'TOYENC486FH']
console.log(result.get('FirstName')); // prints [String: 'Brent']
}
});
}());
O exemplo de código a seguir usa um parâmetro de consulta do tipo Ion.
(async function() {
await qldbDriver.executeLambda(async (txn) => {
const govId = ionjs.load("TOYENC486FH");
const results = (await txn.execute('SELECT * FROM Person WHERE GovId = ?', govId)).getResultList();
for (let result of results) {
console.log(result.get('GovId')); // prints [String: 'TOYENC486FH']
console.log(result.get('FirstName')); // prints [String: 'Brent']
}
});
}());
O exemplo de código a seguir usa múltiplos parâmetros de consulta.
(async function() {
await qldbDriver.executeLambda(async (txn) => {
const results = (await txn.execute('SELECT * FROM Person WHERE GovId = ? AND FirstName = ?', 'TOYENC486FH', 'Brent')).getResultList();
for (let result of results) {
console.log(result.get('GovId')); // prints [String: 'TOYENC486FH']
console.log(result.get('FirstName')); // prints [String: 'Brent']
}
});
}());
O exemplo de código a seguir usa uma lista de parâmetros de consulta.
(async function() {
await qldbDriver.executeLambda(async (txn) => {
const govIds = ['TOYENC486FH','LOGANB486CG','LEWISR261LL'];
/*
Assumes that Person table has documents as follows:
{ "GovId": "TOYENC486FH", "FirstName": "Brent" }
{ "GovId": "LOGANB486CG", "FirstName": "Brent" }
{ "GovId": "LEWISR261LL", "FirstName": "Raul" }
*/
const results = (await txn.execute('SELECT * FROM Person WHERE GovId IN (?,?,?)', ...govIds)).getResultList();
for (let result of results) {
console.log(result.get('GovId'));
console.log(result.get('FirstName'));
/*
prints:
[String: 'TOYENC486FH']
[String: 'Brent']
[String: 'LOGANB486CG']
[String: 'Brent']
[String: 'LEWISR261LL']
[String: 'Raul']
*/
}
});
}());
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 em GovId
, as consultas podem ter mais latência e também podem levar a exceções de conflitos de OCC ou a tempos limite de transação.
Inserir documentos
Os exemplos de código a seguir inserem os tipos de dados nativos.
(async function() {
await qldbDriver.executeLambda(async (txn) => {
// Check if doc with GovId:TOYENC486FH exists
// This is critical to make this transaction idempotent
const results = (await txn.execute('SELECT * FROM Person WHERE GovId = ?', 'TOYENC486FH')).getResultList();
// Insert the document after ensuring it doesn't already exist
if (results.length == 0) {
const doc = {
'FirstName': 'Brent',
'GovId': 'TOYENC486FH',
};
await txn.execute('INSERT INTO Person ?', doc);
}
});
}());
Os exemplos de código a seguir inserem os tipos de dados Ion.
(async function() {
await qldbDriver.executeLambda(async (txn) => {
// Check if doc with GovId:TOYENC486FH exists
// This is critical to make this transaction idempotent
const results = (await txn.execute('SELECT * FROM Person WHERE GovId = ?', 'TOYENC486FH')).getResultList();
// Insert the document after ensuring it doesn't already exist
if (results.length == 0) {
const doc = {
'FirstName': 'Brent',
'GovId': 'TOYENC486FH',
};
// Create a sample Ion doc
const ionDoc = ionjs.load(ionjs.dumpBinary(doc));
await txn.execute('INSERT INTO Person ?', ionDoc);
}
});
}());
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 em GovId
, as instruções podem ter mais latência e também podem levar a exceções de conflitos de OCC ou a tempos limite de transação.
Como inserir 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.
(async function() {
await qldbDriver.executeLambda(async (txn) => {
await txn.execute('UPDATE Person SET FirstName = ? WHERE GovId = ?', 'John', 'TOYENC486FH');
});
}());
O exemplo de código a seguir usa os tipos de dados Ion.
(async function() {
await qldbDriver.executeLambda(async (txn) => {
const firstName = ionjs.load("John");
const govId = ionjs.load("TOYENC486FH");
await txn.execute('UPDATE Person SET FirstName = ? WHERE GovId = ?', firstName, govId);
});
}());
nota
Neste exemplo, recomendamos ter um índice no campo GovId
para otimizar o desempenho. Sem um índice em GovId
, as instruções podem ter mais latência e também podem levar a exceções de conflitos de OCC ou a tempos limite de transação.
Como excluir documentos
Os exemplos de código a seguir usam tipos de dados nativos.
(async function() {
await qldbDriver.executeLambda(async (txn) => {
await txn.execute('DELETE FROM Person WHERE GovId = ?', 'TOYENC486FH');
});
}());
O exemplo de código a seguir usa os tipos de dados Ion.
(async function() {
await qldbDriver.executeLambda(async (txn) => {
const govId = ionjs.load("TOYENC486FH");
await txn.execute('DELETE FROM Person WHERE GovId = ?', govId);
});
}());
nota
Neste exemplo, recomendamos ter um índice no campo GovId
para otimizar o desempenho. Sem um índice em GovId
, as instruções podem ter mais latência e também podem levar a exceções de conflitos de OCC ou a tempos limite de transação.
Como executar 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.
async function insureCar(driver: QldbDriver, vin: string): Promise<boolean> {
return await driver.executeLambda(async (txn: TransactionExecutor) => {
const results: dom.Value[] = (await txn.execute(
"SELECT insured FROM Vehicles WHERE vin = ? AND insured = FALSE", vin)).getResultList();
if (results.length > 0) {
await txn.execute(
"UPDATE Vehicles SET insured = TRUE WHERE vin = ?", vin);
return true;
}
return false;
});
};
Lógica de novas tentativas
O método executeLambda
do driver tem um mecanismo de repetição integrado que repete a transação se ocorrer uma exceção que pode ser repetida (como tempos limite ou conflitos de 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 é defaultBackoffFunction10
milissegundos. Você pode definir a configuração de repetição por instância de driver e também por transação usando uma instância de RetryConfig
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.
var qldb = require('amazon-qldb-driver-nodejs');
// Configuring retry limit to 2
const retryConfig = new qldb.RetryConfig(2);
const qldbDriver = new qldb.QldbDriver("test-ledger", undefined, undefined, retryConfig);
// Configuring a custom backoff which increases delay by 1s for each attempt.
const customBackoff = (retryAttempt, error, transactionId) => {
return 1000 * retryAttempt;
};
const retryConfigCustomBackoff = new qldb.RetryConfig(2, customBackoff);
const qldbDriverCustomBackoff = new qldb.QldbDriver("test-ledger", undefined, undefined, retryConfigCustomBackoff);
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 lambda. Essa configuração para executeLambda
substitui a lógica de repetição definida para a instância do driver.
var qldb = require('amazon-qldb-driver-nodejs');
// Configuring retry limit to 2
const retryConfig1 = new qldb.RetryConfig(2);
const qldbDriver = new qldb.QldbDriver("test-ledger", undefined, undefined, retryConfig1);
// Configuring a custom backoff which increases delay by 1s for each attempt.
const customBackoff = (retryAttempt, error, transactionId) => {
return 1000 * retryAttempt;
};
const retryConfig2 = new qldb.RetryConfig(2, customBackoff);
// The config `retryConfig1` will be overridden by `retryConfig2`
(async function() {
await qldbDriver.executeLambda(async (txn) => {
await txn.execute('CREATE TABLE Person');
}, retryConfig2);
}());
Implementação de restrições de exclusividade
O QLDB nã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:
-
Afirme que a tabela não tem documentos existentes com um
GovId
especificado. -
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 conflito de OCC.
O exemplo de código a seguir mostra como implementar essa lógica de restrição de exclusividade.
const govId = 'TOYENC486FH';
const document = {
'FirstName': 'Brent',
'GovId': 'TOYENC486FH',
};
(async function() {
await qldbDriver.executeLambda(async (txn) => {
// Check if doc with GovId = govId exists
const results = (await txn.execute('SELECT * FROM Person WHERE GovId = ?', govId)).getResultList();
// Insert the document after ensuring it doesn't already exist
if (results.length == 0) {
await txn.execute('INSERT INTO Person ?', document);
}
});
})();
nota
Neste exemplo, recomendamos ter um índice no campo GovId
para otimizar o desempenho. Sem um índice em GovId
, as instruções podem ter mais latência e também podem levar a exceções de conflitos de OCC ou a 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.
Sumário
Importar o módulo Ion
var ionjs = require('ion-js');
Criação de tipos de Ion
O exemplo de código a seguir cria um objeto Ion a partir do texto Ion.
const ionText = '{GovId: "TOYENC486FH", FirstName: "Brent"}';
const ionObj = ionjs.load(ionText);
console.log(ionObj.get('GovId')); // prints [String: 'TOYENC486FH']
console.log(ionObj.get('FirstName')); // prints [String: 'Brent']
O exemplo de código a seguir cria um objeto Ion a partir de um dicionário Node.js.
const aDict = {
'GovId': 'TOYENC486FH',
'FirstName': 'Brent'
};
const ionObj = ionjs.load(ionjs.dumpBinary(aDict));
console.log(ionObj.get('GovId')); // prints [String: 'TOYENC486FH']
console.log(ionObj.get('FirstName')); // prints [String: 'Brent']
Obter um despejo de binário Ion
// ionObj is an Ion struct
console.log(ionjs.dumpBinary(ionObj).toString()); // 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 um despejo de texto Ion
// ionObj is an Ion struct
console.log(ionjs.dumpText(ionObj)); // prints {GovId:"TOYENC486FH",FirstName:"Brent"}
Para obter mais informações sobre o Ion, consulte a documentação do Amazon Ion