Scrittura condizionale DynamoDB in Node.js (AWS SDK v3)
La parte interessante di una scrittura condizionale in AWS SDK v3 non è la ConditionExpression, che funziona allo stesso modo ovunque ed è trattata in espressioni di condizione DynamoDB. È il percorso di fallimento: v3 ti consegna l'Item che ha vinto sull'errore lanciato, se l'hai chiesto, e non ti dà nulla se non l'hai fatto.
Codice
import {DynamoDBClient, UpdateItemCommand} from '@aws-sdk/client-dynamodb';
const client = new DynamoDBClient({region: 'us-east-1'});
// Update the item only if nobody changed it since we read version 7.
const command = new UpdateItemCommand({
TableName: 'Music',
Key: {
Artist: {S: 'Arturo Sandoval'},
SongTitle: {S: 'Cubano Chant'}
},
UpdateExpression: 'SET #upd0 = :updValue0, #version = :newVersion',
ConditionExpression: 'attribute_exists(#cond0) AND #version = :expectedVersion',
ExpressionAttributeNames: {
'#upd0': 'Genre',
'#version': 'Version',
'#cond0': 'Artist'
},
ExpressionAttributeValues: {
':updValue0': {S: 'Latin Jazz'},
':expectedVersion': {N: '7'},
':newVersion': {N: '8'}
},
ReturnValuesOnConditionCheckFailure: 'ALL_OLD'
});
try {
await client.send(command);
console.log('Updated to version 8');
} catch (err) {
if (err.name === 'ConditionalCheckFailedException') {
// With ReturnValuesOnConditionCheckFailure: 'ALL_OLD', the current item
// rides back on the exception — no extra read to see what beat you.
console.log('Lost the race — item is now:', err.Item);
} else {
throw err;
}
}Spiegazione
- La verifica fallita è un errore lanciato, non un campo di stato. v3 rifiuta la promise, quindi il percorso della scrittura e quello della corsa persa sono rami diversi.
err.name === 'ConditionalCheckFailedException'è il discriminante; tutto il resto va rilanciato, ed è a questo che serve l'elsenel blocco. Inghiotti l'interocatche avrai silenziosamente trasformato un throttling in un no-op. ReturnValuesOnConditionCheckFailureè l'unico modo per vedere chi ti ha battuto. Senza di esso l'errore porta il messaggio e nient'altro, e ti ritrovi con unaGetItemdi cui non avevi bisogno. Il riferimento dell'API fissa i suoi valori validi aALL_OLD | NONEe conferma che non consuma capacità di lettura.err.Itemè una mappaAttributeValuegrezza, della stessa forma dellaKeyche hai inviato, non JavaScript semplice. Passala perunmarshalldi@aws-sdk/util-dynamodbprima di confrontareVersioncon un numero, altrimenti starai confrontando con{N: '9'}.- La scrittura fallita viene comunque fatturata. La Developer Guide è esplicita: una condizione che si valuta a false consuma comunque capacità di scrittura, dimensionata sul maggiore tra il vecchio e il nuovo Item. Un loop di retry su una chiave hot è una voce reale in bolletta, quindi metti un tetto ai tentativi.
- Ogni nome nel blocco ha un alias (
#version→Version,#cond0→Artist) perché l'Expression Builder che l'ha generato mette alias incondizionatamente. Qui è più pesante del necessario e non è mai sbagliato: è il compromesso che fa.
Leggere la copia del perdente sull'eccezione
Imposta il Version memorizzato a 9 ed esegui il blocco, che si aspetta 7. DynamoDB Local 3.3.0 lancia, e l'errore intercettato porta con sé:
err.name ConditionalCheckFailedException
err.message The conditional request failed
err.$metadata.httpStatusCode 400
err.Item {
Artist: { S: 'Arturo Sandoval' },
Year: { N: '1994' },
Version: { N: '9' },
SongTitle: { S: 'Cubano Chant' },
AlbumTitle: { S: 'Danzon' }
}Quel Version: 9 è tutto il punto. Il retry può tornare dritto dentro l'update con :expectedVersion impostato a 9, senza nessuna lettura in più e senza una finestra in cui un terzo scrittore si infili tra la tua GetItem e il tuo retry.
Elimina ReturnValuesOnConditionCheckFailure dallo stesso comando e riesegui. Stesso name, stesso message, stesso 400, e err.Item è undefined. Nulla ti avverte: il parametro è opzionale, la sua assenza non è un errore, e il codice che legge err.Item comincia semplicemente a loggare undefined in produzione.
Nota anche che un 400 qui non significa richiesta malformata. ValidationException e ConditionalCheckFailedException condividono il codice di stato, e solo uno dei due è un bug: ecco perché il ramo è su err.name e mai sullo stato.
Per vedere una condizione riuscire e fallire sui tuoi dati, con l'espressione scritta per te invece che digitata, scarica DynoTable.
Esempi correlati
- Scrittura condizionale DynamoDB in Python — lo stesso lock ottimistico con boto3.
- Scrittura condizionale DynamoDB con la AWS CLI — lo stesso lock ottimistico dalla shell.
- DynamoDB PutItem in Node.js — il put solo-creazione con
attribute_not_exists. - Espressioni di condizione DynamoDB — ogni funzione, con i pattern.
- Imporre l'unicità su più attributi — condizioni + transazioni insieme.
- DynamoDB ConditionalCheckFailedException — quando la verifica fallita è attesa, e come gestirla a poco prezzo.
Riferimenti
- UpdateItem — Amazon DynamoDB API Reference
- Condition expressions — Amazon DynamoDB Developer Guide
- DynamoDB read and write operations (capacity unit consumption) — Amazon DynamoDB Developer Guide
Ultima verifica 2026-07-28 rispetto alla documentazione ufficiale AWS collegata sopra.