Condition expressions de DynamoDB
Una condition expression 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 DynamoDB tiene a una cláusula WHERE sobre una
escritura — y la única forma segura de imponer un invariante.
¿Cómo funcionan las condition expressions de DynamoDB?
Una condition expression es un predicado que DynamoDB evalúa del lado del servidor sobre 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. Pliega la comprobación y la mutación en una sola operación atómica, de modo que los llamantes concurrentes no pueden competir contra una lectura obsoleta.
- Es una guarda, no un filtro. La
ConditionExpressionse ejecuta del lado del servidor sobre el elemento actual; un resultado falso hace fallar la escritura conConditionalCheckFailedException. - Reemplaza al leer-luego-escribir. Sin viaje de ida y vuelta
SELECTy luegoUPDATE— la comprobación y la mutación son una operación atómica, así que dos llamantes no pueden competir. - Rechazar es gratis, ejecutar no. Una escritura condicional fallida aún consume capacidad de escritura. La garantía cuesta lo mismo que la escritura que bloquea.
Viniendo de SQL, leerías la fila, la comprobarías en el código de la app, luego actualizarías. En DynamoDB esa brecha entre lectura y escritura es un bug de corrupción de datos esperando a un llamante concurrente. La condition expression cierra la brecha.
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 de Scan — esos usan
FilterExpression, que es algo distinto 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 al fallar | Rechaza toda la escritura | Descarta el elemento de los resultados |
| Ve | El elemento actual, pre-escritura | Cada elemento candidato, post-lectura |
| Coste | La escritura fallida aún factura | Los elementos filtrados aún se facturan en la lectura |
Ambos se ejecutan del lado del servidor. La diferencia es lo que hace "falso": una condición aborta una mutación; un filtro solo oculta una fila que ya pagaste por leer. (AWS: Condition Expressions)
Las funciones que realmente usarás
El lenguaje de condiciones es pequeño. Los caballos de batalla:
attribute_exists(path)/attribute_not_exists(path)— ¿existe este atributo en el elemento? El idioma 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 partition key es la forma canónica de hacer que PutItem
se comporte como una inserción que no pisará un elemento existente — DynamoDB no tiene una
operación "insert" separada, así que la condición es la semántica de inserción.
(AWS: Comparison Operator and Function Reference)
Un ejemplo desarrollado: 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 cargo nunca debe empujar el saldo disponible por debajo de cero, y nunca debes cargar a una cuenta que no existe. Dos reglas, ambas imponibles en la propia escritura.
La forma equivocada (el tiro al pie)
GetItem ACCT#a7f3 / BALANCE → clearedCents = 50000
if (50000 >= 30000) ... ← app-side check
UpdateItem SET clearedCents = 20000
Entre el GetItem y el UpdateItem, un segundo cargo puede leer el mismo 50000, pasar
su propia comprobación y escribir también. Ambos tienen éxito; la cuenta se vuelve
negativa. Esta es una carrera de lectura-modificación-escritura, y ninguna cantidad de
validación del lado de la app la arregla — la comprobación y la escritura son operaciones
separadas.
La forma correcta
Pliega la comprobación dentro de la escritura. Carga 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
cargo concurrente o bien ve el saldo original y se comprueba contra él, o bien 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
constructor de expresiones de DynamoDB en lugar de
ensamblar a mano el mapa ExpressionAttributeValues.
Inspeccionar la guarda en DynoTable
Cuando una escritura condicional falla, quieres ver el estado real del elemento, no
adivinarlo. Saca a la vista 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. Exponlo como un resultado de dominio,
no como un hipo 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: Working with Items)- Distinguir las dos razones del fallo.
attribute_exists(PK) AND clearedCents >= :amtcolapsa "sin cuenta" y "sin fondos" en una sola excepción. Si los llamantes necesitan diferenciarlas, divide en dos escrituras o inspecciona el elemento devuelto.
El bloqueo optimista es el mismo truco
El patrón del número de versión es solo una condition expression 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
relees y reintentas. Así es como DynamoDB hace el control de concurrencia sin bloqueos —
afirma lo que viste, falla si se movió. (AWS: Optimistic Locking with Version
Number)
Trampas y próximos pasos
- Nombres que chocan con palabras reservadas.
status,size,namey ~570 más están reservados. Ponles un alias conExpressionAttributeNames(#s = status) o tu expresión fallará silenciosamente al analizarse. - 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 por esos rechazos. Seguro barato, pero no gratis.
Para modelar las claves contra las que se ejecutan estas guardas, consulta diseño de tabla única y Query vs Scan. Cuando estés listo para emitir escrituras condicionales contra datos reales, descarga DynoTable y ejecútalas contra tus propias tablas.


