DynamoDB TransactWriteItems en Node.js (AWS SDK v3)
Un TransactWriteItemsCommand réussi ne te dit presque rien : pas d'éléments, pas d'attributs, une réponse vide. Tout ce dont tu as besoin est sur l'exception, donc en SDK v3 le bloc catch ci-dessous est la vraie surface d'API, et il vaut la peine de savoir exactement ce qui y atterrit. (Pour savoir si une transaction est le bon appel tout court, voir les transactions DynamoDB.)
Code
import {DynamoDBClient, TransactWriteItemsCommand} from '@aws-sdk/client-dynamodb';
const client = new DynamoDBClient({region: 'us-east-1'});
// Move one award between two songs — atomically. If the first song has no
// award to give, NEITHER update happens.
const command = new TransactWriteItemsCommand({
TransactItems: [
{
Update: {
TableName: 'Music',
Key: {Artist: {S: 'Arturo Sandoval'}, SongTitle: {S: 'Cubano Chant'}},
UpdateExpression: 'SET #upd0 = #upd0 - :one',
ConditionExpression: '#upd0 >= :one',
ExpressionAttributeNames: {'#upd0': 'Awards'},
ExpressionAttributeValues: {':one': {N: '1'}}
}
},
{
Update: {
TableName: 'Music',
Key: {Artist: {S: 'Arturo Sandoval'}, SongTitle: {S: 'A Mis Abuelos'}},
UpdateExpression: 'SET #upd0 = if_not_exists(#upd0, :zero) + :one',
ExpressionAttributeNames: {'#upd0': 'Awards'},
ExpressionAttributeValues: {':one': {N: '1'}, ':zero': {N: '0'}}
}
}
]
});
try {
await client.send(command);
console.log('Transaction committed');
} catch (err) {
if (err.name === 'TransactionCanceledException') {
// One reason per action, in TransactItems order. 'None' means that action
// was fine — some OTHER action sank the transaction.
const codes = (err.CancellationReasons ?? []).map((r) => r.Code);
console.log('Transaction canceled:', codes); // e.g. ['ConditionalCheckFailed', 'None']
} else {
throw err;
}
}Explication
TransactItems— un tableau ordonné d'actionsPut,Update,DeleteetConditionCheck. L'ordre n'est pas l'ordre d'exécution (la transaction est atomique), mais c'est l'ordre dans lequel les raisons d'échec reviennent, ce qui est la seule raison de s'en soucier. Les plafonds sont traités plus bas.- Ce que v3 lève réellement. Les propriétés propres de l'objet attrapé sont
$fault,$retryable,$metadata,name,CancellationReasons,messageet__type. Il n'y a pas deerr.code;err.nameest la chaîne sur laquelle brancher, eterr.$metadataportehttpStatusCode: 400plusattempts: 1, ce qui te permet de vérifier que le SDK n'a pas discrètement réessayé l'annulation à ta place. CancellationReasonsest positionnel et lacunaire. Pour la transaction ci-dessus, il arrive sous la forme[{"Code":"ConditionalCheckFailed","Message":"The conditional request failed"},{"Code":"None"}]. L'entréeNonen'a aucune propriétéMessage, doncerr.CancellationReasons.map((r) => r.Message.trim())lève une exception dans ton gestionnaire d'erreurs, précisément sur les actions qui avaient réussi.ReturnValuesOnConditionCheckFailure: 'ALL_OLD'ajoute unItemà la raison de cette action, devantCodeetMessage, en JSON DynamoDB brut. Les attributs de l'élément perdant reviennent gratuitement ; l'alternative est unGetItemde suivi alors que tu as déjà perdu la course.- Le test sur
err.namea un trou, et il vaut la peine de savoir lequel. Vise deux actions sur le même élément et DynamoDB répondValidationExceptionavec le messageTransaction request cannot include multiple operations on one item, et aucunCancellationReasons, parce que rien n'a été tenté. La brancheelse { throw err }ci-dessus le relance. C'est un comportement correct, pas un bug, mais ça veut dire que les erreurs structurelles n'atteignent jamais ta journalisation des annulations. - v3 envoie déjà un
ClientRequestToken, même si tu l'omets. Capturer le corps sérialisé montre un UUID neuf sur le fil, et deux appelssend()du même objet commande sont partis avec deux jetons différents. Le jeton protège donc un appel en vol, pas ta propre boucle de reprise : attrape, renvoie, et te voilà avec un nouveau jeton et aucune idempotence. Fournis le tien si une reprise peut franchir une frontière de processus. Réutilise-le avec un paramètre modifié et tu obtiensIdempotentParameterMismatchau lieu d'une double application silencieuse. - Un seul autre code mérite son chemin de code.
TransactionConflictsignifie qu'une transaction concurrente détenait l'un de tes éléments : une reprise avec backoff est la bonne réponse, là où elle ne l'est jamais pourConditionalCheckFailed. Les autres sont décodés sur la page TransactionCanceledException. - Coût — chaque élément d'une transaction est écrit deux fois en dessous (préparation, puis validation), donc prévois environ 2× la capacité d'écriture d'une écriture simple. Une écriture conditionnelle sur un seul élément te donne l'atomicité sur cet élément pour la moitié.
Quelle limite tu atteins en premier
Le plafond de 100 actions et celui de 4 Mo sont indépendants, et c'est celui des octets qui surprend : cent incréments de compteur, ce n'est rien, alors qu'une douzaine d'éléments volumineux peuvent épuiser l'agrégat à eux seuls. Mesure un élément représentatif avec le calculateur de taille d'élément DynamoDB avant de décider combien d'actions regrouper. Pour lire les éléments qu'une action va toucher pendant que tu écris encore la condition, télécharge DynoTable.
Exemples liés
- DynamoDB TransactWriteItems en Python — la même transaction avec boto3.
- DynamoDB TransactWriteItems avec l'AWS CLI — la même transaction depuis le shell.
- Écriture conditionnelle DynamoDB en Node.js — l'atomicité sur un seul élément sans le coût ×2.
- Les transactions DynamoDB — isolation, idempotence, et quand les transactions en valent la peine.
- DynamoDB TransactionCanceledException — chaque code de raison d'annulation, décodé.
- "Too many actions in a TransactWriteItems call" — les limites de 100 actions et de 4 Mo par transaction.
- "Transaction request cannot include multiple operations on one item" — une action par élément, par transaction.
Références
- TransactWriteItems — Amazon DynamoDB API Reference
- Amazon DynamoDB transactions: how it works — Amazon DynamoDB Developer Guide
- DynamoDB read and write operations (capacity unit consumption) — Amazon DynamoDB Developer Guide
Dernière vérification le 2026-07-28 par rapport à la documentation officielle AWS liée ci-dessus.