Intermedio8 min de lectura

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/SK en tu código (p. ej. CONFIG#GLOBAL) en lugar de plantillar con un id de usuario o pedido.
  • Léelo con GetItem, nunca con Scan. 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:

PKSKattributes
SETTINGS#APPFLAGS#V1signup_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.

noApp / peticiónGetItem PK=SETTINGS#APPSK=FLAGS#V1¿Item encontrado?Usar flags, cachear localmenteRecurrir a valores por defectoseguros

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 Put de 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 singletonItem por entidad
ClaveConstante en duro (SETTINGS#APP)Plantillada con un id (USER#42)
CuántosExactamente unoUno por usuario / pedido / tenant
Lectura típicaGetItem sobre la clave conocidaGetItem o Query por entidad
AlcanceToda la aplicaciónUna sola entidad
Úsalo paraFlags globales, config, versión del sistemaPerfiles, 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 Scan para é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.

Actualizado