Intermedio8 min di lettura

Item singleton in DynamoDB

Un item singleton è una singola riga con una chiave fissa e hardcoded che contiene stato per l'intera applicazione — non un record per utente o per ordine, ma un record, punto. Feature flag, un blob di config, un kill-switch globale: il tipo di cosa che un'app relazionale terrebbe in una tabella di impostazioni a una riga.

Venendo da SQL, ricorreresti a una tabella config con id = 1 e un SELECT * FROM config. In DynamoDB fai la stessa cosa con una partition key hardcoded — e poiché conosci sempre quella chiave, la leggi con un GetItem, non con una Queryo unoScan.

Cos'è un item singleton in DynamoDB?

Un item singleton è una singola riga DynamoDB memorizzata sotto una chiave fissa e hardcoded che contiene stato globale per l'intera applicazione — feature flag, un blob di config, una versione di sistema — anziché un record per utente o per ordine. Poiché conosci sempre la chiave, la leggi con un GetItem e la aggiorni con più condition expression.

  • Un singleton è un item con una chiave costante. Hardcodi la PK/SK nel tuo codice (es. CONFIG#GLOBAL) invece di interpolare l'id di un utente o un ordine.
  • Leggilo con GetItem, mai con Scan. Conosci sempre la chiave completa, quindi una lettura puntuale è una RCU coerente e prevedibile — niente filtro, niente scansione della tabella.
  • È una hot key per definizione. Ogni richiesta può toccare la stessa partizione, quindi mettila in cache e mantieni l'item piccolo; non farne un collo di bottiglia in scrittura.
  • Modificalo in sicurezza con update + condition expression, non con read-modify-write nella tua app — è lì che vive la race del lost update.

Riconosci il pattern

Hai stato globale quando i dati non sono associati a nessuna singola entità. Alcuni segnali:

  • Un flag uguale per tutti (signup_enabled = false).
  • Un blob di parametri che la tua app legge all'avvio (rate limit, quote di default).
  • Un contatore o numero di versione per l'intero sistema, non per riga.

Tutto ciò che è associato a un utente, tenant o ordine non è un singleton — quello è un item ordinario con chiave basata sull'id di quell'entità. Il singleton è la fetta globale residua che non ha altro posto dove vivere.

Dagli una chiave costante

L'intero pattern si regge su una decisione: la chiave è un letterale, non un template. Per un item di feature flag globali in una single table sovraccaricata, scegli un prefisso fisso e un valore fisso:

PKSKattributes
SETTINGS#APPFLAGS#V1signup_enabled, maintenance_mode, ai_search_enabled

PK = "SETTINGS#APP" e SK = "FLAGS#V1" sono incorporati nel codice. Non c'è id utente, né id tenant — l'applicazione chiede esattamente questo item ogni volta. Quella prevedibilità è il punto: una chiave nota è un GetItem, e un GetItem è la lettura più economica e coerente che DynamoDB offra.

Il suffisso V1 è deliberato. Se lo schema del flag cambia forma in seguito, scrivi un item FLAGS#V2 e sposti i reader su di esso, invece di mutare quello live sul posto. Versionare la chiave del singleton ti compra una giuntura di migrazione pulita.

Leggilo con GetItem

Poiché la chiave è completamente nota, non usi mai QueryScan per un singleton. Uno Scan legge l'intera tabella e filtra lato client — il classico footgun dello Scan — ed è un eccesso assurdo per recuperare una riga che puoi indirizzare direttamente.

Un GetItem contro SETTINGS#APP / FLAGS#V1 restituisce i flag in una singola lettura fortemente o eventualmente coerente. AWS fattura un GetItem di un item ≤ 4 KB come 0,5 RCU eventualmente coerente o 1 RCU fortemente coerente (documentazione AWS sulla capacità di lettura/scrittura). Mantieni il singleton piccolo e quel costo resta piatto per sempre.

Il percorso di lettura è semplicemente: l'app si avvia o arriva una richiesta, fai GetItem sulla chiave fissa, metti in cache il risultato. Ecco il flusso.

noApp / richiestaGetItem PK=SETTINGS#APPSK=FLAGS#V1Item trovato?Usa i flag, metti in cachelocalmenteRicadi su default sicuri

La chiave fissa trasforma una ricerca globale in una lettura puntuale con un percorso di default integrato.

Nota il ramo no: un singleton mancante non dovrebbe mai mandarti in crash. Imposta come default il valore sicuro (feature off, manutenzione on) così che una mancanza al primo deploy o una chiave sbagliata falliscano in modo chiuso, non aperto.

Aggiornalo senza una race

La trappola è aggiornare un singleton con read-modify-write nella tua app: fai GetItem sui flag, ne ribalti uno in memoria, poi rimetti l'intera cosa con un PutItem. Due writer concorrenti leggono entrambi il vecchio item e il secondo Put calpesta la modifica del primo. Lost update.

Due funzionalità di DynamoDB uccidono la race senza locking lato app:

  • Le update expression mutano un attributo lato server, lasciando il resto intatto. Non serve rifare il Put dell'intero item.
  • Le condition expression fanno riuscire la scrittura solo se l'item appare ancora come ti aspetti, così una scrittura stantia viene rifiutata con ConditionalCheckFailedException (documentazione AWS sulle condition expression).

Per ribaltare un flag, mira solo a quell'attributo con un SET e proteggilo con un bump di versione così che i writer concorrenti non possano calpestarsi a vicenda:

# UpdateItem
Key                  PK=SETTINGS#APP  SK=FLAGS#V1
UpdateExpression     SET signup_enabled = :on, schema_version = :next
ConditionExpression  schema_version = :current

Se due writer corrono, il controllo schema_version = :current del secondo fallisce ed esso riprova contro il valore fresco. Puoi impalcare i nomi, i valori e questa esatta forma di espressione nell'Expression Builder per DynamoDB prima di cablarla nel codice. Per uno sguardo più approfondito agli operatori, vedi la guida sugli idiomi delle update expression.

Attento alla hot key

Un singleton è, per costruzione, una hot key — ogni parte della tua app può leggere la stessa partizione. Va bene per le letture se metti in cache, ma è l'unico rischio reale del pattern.

  • Metti in cache in modo aggressivo. Leggi i flag una volta per processo (o ogni N secondi), non a ogni richiesta. Il valore del singleton è la cosa più economica da memoizzare.
  • Non renderlo un hot spot in scrittura. Un flag commutato da un admin qualche volta al giorno non è nulla. Un singleton che incrementi a ogni richiesta è un collo di bottiglia di throughput di partizione — quello è un problema da contatore, non da singleton.
  • Mantienilo piccolo. Il costo di lettura scala con la dimensione dell'item a blocchi di 4 KB. Un blob di config gonfio rende ogni avvio più costoso del necessario.

Se davvero ti serve un contatore globale ad alta scrittura, il singleton è la forma sbagliata — fallo a shard su N item e somma in lettura. Quello è un pattern diverso.

Singleton vs item per-entità

La linea è semplicemente a cosa sono associati i dati.

Item singletonItem per-entità
ChiaveCostante hardcoded (SETTINGS#APP)Template con un id (USER#42)
QuantiEsattamente unoUno per utente / ordine / tenant
Lettura tipicaGetItem sulla chiave notaGetItem o Query per entità
AmbitoIntera applicazioneUna singola entità
Usalo perFlag globali, config, versione di sistemaProfili, ordini, qualsiasi cosa per-id

Se ti ritrovi a volere due singleton dello stesso tipo, non hai un singleton — hai un item per-entità e l'entità è la cosa che hai dimenticato di mettere in chiave (config per-tenant, ad esempio).

Insidie e prossimi passi

  • Non fare Scan per esso. Conosci la chiave; indirizzala direttamente.
  • Non fare read-modify-write. Usa update + condition expression.
  • Non lasciarlo sparire silenziosamente. Imposta come default il valore sicuro in caso di cache miss.
  • Non sovraccaricarlo con scritture ad alta frequenza. Quello è un lavoro da contatore a shard.

Il singleton vive comodamente dentro un single-table design — è solo un'altra item collection con una chiave fissa accanto alle tue righe di entità.

Prova DynoTable per sfogliare la tua tabella, trovare la riga singleton dalla sua chiave fissa e modificare i flag a mano mentre costruisci il percorso di scrittura.

Aggiornato