Condition Expressions en DynamoDB: la guía completa (con ejemplos)
Una expresión de condición es un predicado que DynamoDB evalúa sobre el elemento existente
antes de confirmar tu escritura. Si el predicado es falso, la escritura se
rechaza y nada cambia. Es lo más parecido que tiene DynamoDB a una
cláusula WHERE en una escritura — y la única forma segura de imponer un invariante.
¿Cómo funcionan las expresiones de condición de DynamoDB?
Una expresión de condición es un predicado que DynamoDB evalúa del lado del servidor contra el elemento actual antes de confirmar una escritura. Si es verdadero, la escritura continúa; si es falso, la escritura se rechaza con ConditionalCheckFailedException y nada cambia. Fusiona la comprobación y la mutación en una sola operación atómica, de modo que los llamadores concurrentes no pueden competir con una lectura obsoleta.
- Es una guarda, no un filtro.
ConditionExpressionse ejecuta del lado del servidor sobre el elemento actual; un resultado falso hace fallar la escritura conConditionalCheckFailedException. - Reemplaza al leer-luego-escribir. Sin un viaje de ida y vuelta
SELECTy luegoUPDATE— la comprobación y la mutación son una sola operación atómica, de modo que dos llamadores no pueden competir. - Es gratis rechazar, no gratis ejecutar. Una escritura condicional fallida aún consume capacidad de escritura. Una escritura rechazada factura WCU por el tamaño del elemento existente contra el que se comprobó (mínimo 1) — un crear-si-ausente fallido cuesta 1 WCU.
Viniendo de SQL, leerías la fila, la comprobarías en el código de la aplicación y luego actualizarías. En DynamoDB ese hueco entre la lectura y la escritura es un error de corrupción de datos esperando a un llamador concurrente. La expresión de condición cierra el hueco.
Dónde se aplican
Adjuntas una ConditionExpression a PutItem, UpdateItem, DeleteItem y a
cada acción dentro de TransactWriteItems. No forma parte de Query ni Scan
— esos usan FilterExpression, que es algo diferente en la ruta de lectura.
Esa distinción confunde a la gente, así que sé preciso:
ConditionExpression | FilterExpression | |
|---|---|---|
| Ruta | Escrituras (Put/Update/Delete) | Lecturas (Query/Scan) |
| Efecto en caso de fallo | Rechaza toda la escritura | Descarta el elemento de los resultados |
| Ve | El elemento actual, previo a la escritura | Cada elemento candidato, tras la lectura |
| Costo | La escritura fallida aún factura | Los elementos filtrados aún se facturan por la lectura |
Ambos se ejecutan del lado del servidor. La diferencia está en lo que hace "falso": una condición aborta una mutación; un filtro solo oculta una fila que ya pagaste por leer. (AWS: Expresiones de condición)
Las funciones que realmente usarás
El lenguaje de condiciones es pequeño. Las herramientas de trabajo:
attribute_exists(path)/attribute_not_exists(path)— ¿este existe en el elemento? El modismo clásico para "crear solo si está ausente" / "actualizar solo si está presente".- Comparadores —
=,<>,<,<=,>,>=— contra un valor u otro atributo. attribute_type,begins_with,contains,size— comprobaciones de tipo y de cadena/conjunto.BETWEEN … AND …,IN (…)— rango y pertenencia.AND,OR,NOT, paréntesis — para combinar lo anterior.
attribute_not_exists sobre la es la forma canónica de hacer que
PutItem se comporte como una inserción que no sobrescribirá un elemento existente — DynamoDB
no tiene una operación de "inserción" separada, así que la condición es la semántica de inserción.
(AWS: Referencia de operadores de comparación y funciones)
Un ejemplo trabajado: proteger un libro mayor contra el descubierto
Toma un libro mayor bancario. Cada cuenta es un elemento:
PK = "ACCT#a7f3"
SK = "BALANCE"
clearedCents = 50000
holdCents = 0El invariante: un débito nunca debe empujar el saldo disponible por debajo de cero, y nunca debes debitar una cuenta que no existe. Dos reglas, ambas exigibles en la propia escritura.
La forma incorrecta (la trampa)
GetItem ACCT#a7f3 / BALANCE → clearedCents = 50000
if (50000 >= 30000) ... ← app-side check
UpdateItem SET clearedCents = 20000
Entre el GetItem y el UpdateItem, un segundo débito puede leer el mismo
50000, pasar su propia comprobación y escribir también. Ambos tienen éxito; la cuenta se pone
en negativo. Esto es una carrera de leer-modificar-escribir, y ninguna cantidad de validación del lado de la aplicación
lo arregla — la comprobación y la escritura son operaciones separadas.
La forma correcta
Fusiona la comprobación en la escritura. Debita 30000 céntimos, condicionado a que la cuenta exista y tenga suficiente:
UpdateItem ACCT#a7f3 / BALANCE
SET clearedCents = clearedCents - :amt
ConditionExpression:
attribute_exists(PK) AND clearedCents >= :amtcon :amt = 30000. Si el saldo es demasiado bajo, o el elemento nunca se creó,
DynamoDB rechaza la escritura con
ConditionalCheckFailedException
y el saldo queda intacto. El débito concurrente o bien ve el saldo original y se
comprueba contra él, o ve el actualizado — nunca una lectura obsoleta sobre la que actuó.
Puedes construir y copiar la expresión exacta — nombres, valores y todo — con el
Generador de expresiones de DynamoDB en lugar de
ensamblar a mano el mapa ExpressionAttributeValues.
Pruébalo aquí mismo — este generador viene preconfigurado con un PutItem protegido
(attribute_not_exists) para que puedas leer la ConditionExpression generada:
Inspeccionar la guarda en DynoTable
Cuando una escritura condicional falla, quieres ver el estado real del elemento, no adivinarlo.
Abre el elemento de la cuenta y lee clearedCents directamente.

Lee el rechazo, no reintentes a ciegas
ConditionalCheckFailedException no es un error transitorio — reintentar la misma
escritura no cambia nada. Significa que se disparó una regla de negocio: fondos insuficientes,
creación duplicada, versión obsoleta. Preséntalo como un resultado de dominio, no como un fallo
de infraestructura.
Dos cosas hacen depurables los fallos:
ReturnValuesOnConditionCheckFailure: ALL_OLD— DynamoDB devuelve el elemento actual junto con el fallo, para que puedas mostrar "el saldo era 20000, pediste 30000" sin una segunda lectura. (AWS: Trabajar con elementos)- Distinguir las dos razones de fallo.
attribute_exists(PK) AND clearedCents >= :amtcolapsa "no hay cuenta" y "no hay fondos" en una sola excepción. Si los llamadores necesitan diferenciarlos, divídelo en dos escrituras o inspecciona el elemento devuelto.
El bloqueo optimista es el mismo truco
El patrón de número de versión es solo una expresión de condición con otro
sombrero. Almacena un atributo version; cada escritura afirma la versión que leíste y
la incrementa:
UpdateItem ACCT#a7f3 / BALANCE
SET clearedCents = :new, version = :next
ConditionExpression: version = :seenSi otro escritor se movió primero, version = :seen es falso, la escritura se rechaza,
y vuelves a leer y reintentas. Así hace DynamoDB el control de concurrencia sin
bloqueos — afirma lo que viste, falla si se movió. (AWS: Bloqueo optimista con
número de versión)
El área de preparación de DynoTable ejecuta este patrón por ti — una
edición concurrente aparece como un conflicto que resolver, no como una escritura
perdida.
Escollos y próximos pasos
- Nombres que colisionan con palabras reservadas.
status,size,namey unas ~570 más están reservadas. Aliasízalas conExpressionAttributeNames(#s = status) o la solicitud se rechaza con una ValidationException ('Attribute name is a reserved keyword'). El comprobador de palabras reservadas toma tus nombres de atributo y te devuelve el mapa de alias listo para pegar. - Una condición no puede referenciar otro elemento. Solo ve el elemento que se está
escribiendo. Los invariantes entre elementos necesitan
TransactWriteItemscon unaConditionExpressionpor acción, o unConditionCheckcontra un elemento centinela. - Las escrituras fallidas aún cuestan WCU. Una guarda que rechaza el 90% de las veces aún factura esos rechazos. Un seguro barato, pero no gratis.
Para modelar las claves contra las que se ejecutan estas guardas, consulta diseño de tabla única y Query frente a Scan. Cuando estés listo para emitir escrituras condicionales contra datos reales, descarga DynoTable y ejecútalas contra tus propias tablas.


