Items singleton en DynamoDB
Un item singleton es una única fila con una clave fija y codificada en duro que guarda estado para toda tu aplicación — no un registro por usuario o por pedido, sino un registro, punto. Feature flags, un blob de configuración, un kill-switch global: el tipo de cosa que una app relacional guardaría en una tabla de ajustes de una sola fila.
Viniendo de SQL, recurrirías a una tabla config con id = 1 y un SELECT * FROM config. En DynamoDB haces lo mismo con una clave de partición codificada en
duro — y como siempre conoces esa clave, la lees con un GetItem, no con una
Queryni unScan.
¿Qué es un item singleton en DynamoDB?
Un item singleton es una única fila de DynamoDB almacenada bajo una clave fija y codificada en duro que guarda estado global para toda tu aplicación — feature flags, un blob de configuración, una versión de sistema — en lugar de un registro por usuario o pedido. Como siempre conoces la clave, la lees con un GetItem y la actualizas con expresiones de más expresiones de condición.
- Un singleton es un item con una clave constante. Codificas en duro la
PK/SKen tu código (p. ej.CONFIG#GLOBAL) en lugar de plantillar con un id de usuario o pedido. - Léelo con
GetItem, nunca conScan. Siempre conoces la clave completa, así que una lectura puntual es una RCU consistente y predecible — sin filtro, sin recorrer la tabla. - Es una hot key por definición. Cada petición puede tocar la misma partición, así que cachéala y mantén el item pequeño; no la conviertas en un cuello de botella de escritura.
- Múdala de forma segura con expresiones de update + condición, no con read-modify-write en tu app — ahí es donde vive la condición de carrera de actualización perdida.
Reconoce el patrón
Tienes estado global cuando los datos no están acotados a ninguna entidad concreta. Algunas señales:
- Un flag que es el mismo para todos (
signup_enabled = false). - Un blob de parámetros ajustables que tu app lee al arrancar (límites de tasa, cuotas por defecto).
- Un contador o número de versión para todo el sistema, no por fila.
Cualquier cosa acotada a un usuario, tenant o pedido no es un singleton — eso es un item ordinario con clave por el id de esa entidad. El singleton es el trozo global sobrante que no tiene otro sitio donde vivir.
Dale una clave constante
Todo el patrón depende de una decisión: la clave es un literal, no una plantilla. Para un item global de feature flags en una tabla única sobrecargada, elige un prefijo fijo y un valor fijo:
| PK | SK | attributes |
|---|---|---|
| SETTINGS#APP | FLAGS#V1 | signup_enabled, maintenance_mode, ai_search_enabled |
PK = "SETTINGS#APP" y SK = "FLAGS#V1" están horneadas en el código. No hay id de
usuario, no hay id de tenant — la aplicación pide exactamente este item cada vez.
Esa predictibilidad es el punto: una clave conocida es un GetItem, y un GetItem
es la lectura más barata y consistente que ofrece DynamoDB.
El sufijo V1 es deliberado. Si el esquema del flag cambia de forma más adelante,
escribes un item FLAGS#V2 y conmutas los lectores, en lugar de mutar el vivo en
sitio. Versionar la clave del singleton te compra una costura de migración limpia.
Léelo con GetItem
Como la clave es totalmente conocida, nunca haces Query ni Scan para un
singleton. Un Scan lee toda la tabla y filtra en cliente — el clásico
footgun del Scan — y es un exceso absurdo para obtener una
fila que puedes direccionar directamente.
Un GetItem contra SETTINGS#APP / FLAGS#V1 devuelve los flags en una sola
lectura fuerte o eventualmente consistente. AWS factura un GetItem de un item ≤ 4 KB
como 0,5 RCU eventualmente consistente o 1 RCU fuertemente consistente
(docs de capacidad de lectura/escritura de AWS).
Mantén el singleton pequeño y ese coste se queda plano para siempre.
El camino de lectura es simplemente: la app arranca o llega una petición, haces
GetItem de la clave fija, cacheas el resultado. Aquí está el flujo.
La clave fija convierte una búsqueda global en una lectura puntual con un camino por defecto integrado.
Fíjate en la rama no: un singleton ausente nunca debería tumbarte. Por defecto al
valor seguro (feature off, mantenimiento on) para que un hueco en el primer
deploy o una clave mala falle cerrado, no abierto.
Actualízalo sin una condición de carrera
La trampa es actualizar un singleton con read-modify-write en tu app: haces
GetItem de los flags, cambias uno en memoria, luego haces PutItem de todo de
vuelta. Dos escritores concurrentes leen ambos el item viejo y el segundo Put
aplasta el cambio del primero. Actualización perdida.
Dos features de DynamoDB matan la condición de carrera sin bloqueo del lado de la app:
- Las expresiones de update mutan un atributo del lado servidor, dejando el
resto intacto. No hace falta volver a hacer
Putde todo el item. - Las expresiones de condición hacen que la escritura tenga éxito solo si el
item aún tiene el aspecto que esperas, así que una escritura obsoleta se rechaza
con
ConditionalCheckFailedException(docs de expresiones de condición de AWS).
Para cambiar un flag, apunta solo a ese atributo con un SET y protégelo con un
incremento de versión para que los escritores concurrentes no se pisen:
# UpdateItem
Key PK=SETTINGS#APP SK=FLAGS#V1
UpdateExpression SET signup_enabled = :on, schema_version = :next
ConditionExpression schema_version = :current
Si dos escritores compiten, la comprobación schema_version = :current del segundo
falla y reintenta contra el valor fresco. Puedes montar los nombres, los valores y
esta forma exacta de expresión en el
constructor de expresiones de DynamoDB antes de
cablearlo en el código. Para una mirada más profunda a los operadores, consulta la
guía de idiomas de expresiones de update.
Cuidado con la hot key
Un singleton es, por construcción, una hot key — cada parte de tu app puede leer la misma partición. Eso está bien para lecturas si cacheas, pero es el único riesgo real del patrón.
- Cachea agresivamente. Lee los flags una vez por proceso (o cada N segundos), no en cada petición. El valor del singleton es lo más barato de memoizar.
- No lo conviertas en un punto caliente de escritura. Un flag que un admin cambia unas pocas veces al día no es nada. Un singleton que incrementas en cada petición es un cuello de botella de throughput de partición — eso es un problema de contador, no un singleton.
- Mantenlo pequeño. El coste de lectura escala con el tamaño del item en bloques de 4 KB. Un blob de config hinchado hace cada arranque más caro de lo necesario.
Si genuinamente necesitas un contador global de alta escritura, el singleton es la forma equivocada — fragméntalo a lo largo de N items y suma en la lectura. Ese es un patrón diferente.
Singleton vs item por entidad
La línea es simplemente a qué está acotado el dato.
| Item singleton | Item por entidad | |
|---|---|---|
| Clave | Constante en duro (SETTINGS#APP) | Plantillada con un id (USER#42) |
| Cuántos | Exactamente uno | Uno por usuario / pedido / tenant |
| Lectura típica | GetItem sobre la clave conocida | GetItem o Query por entidad |
| Alcance | Toda la aplicación | Una sola entidad |
| Úsalo para | Flags globales, config, versión del sistema | Perfiles, pedidos, cualquier cosa por id |
Si te encuentras queriendo dos singletons del mismo tipo, no tienes un singleton — tienes un item por entidad y la entidad es lo que olvidaste usar como clave (config por tenant, digamos).
Trampas y próximos pasos
- No hagas
Scanpara él. Conoces la clave; direcciónala directamente. - No hagas read-modify-write. Usa expresiones de update + condición.
- No dejes que desaparezca en silencio. Por defecto al valor seguro en un fallo de caché.
- No lo sobrecargues con escrituras de alta frecuencia. Eso es trabajo de contador fragmentado.
El singleton vive cómodamente dentro de un diseño de tabla única — es solo una colección de items más con una clave fija junto a tus filas de entidad.
Prueba DynoTable para explorar tu tabla, encontrar la fila singleton por su clave fija y editar flags a mano mientras construyes el camino de escritura.