Fortgeschritten7 Min. Lesezeit

Singleton-Items in DynamoDB

Ein Singleton-Item ist eine einzelne Row mit festem, hardgecodetem Key, die State für deine ganze Application hält — nicht ein Record pro User oder Order, sondern ein Record, Punkt. Feature Flags, ein Config-Blob, ein globaler Kill- Switch: das, was eine relationale App in einer One-Row-Settings-Tabelle halten würde.

Aus SQL würdest du nach einer config-Tabelle mit id = 1 und SELECT * FROM config greifen. In DynamoDB machst du dasselbe mit einem hardgecodeten Partition Key — und weil du diesen Key immer kennst, liest du ihn mit GetItem, nicht mit Query oder Scan.

Was ist ein Singleton-Item in DynamoDB?

Ein Singleton-Item ist eine einzelne DynamoDB-Row unter einem festen, hardgecodeten Key, die globalen State für deine ganze Application hält — Feature Flags, ein Config-Blob, eine systemweite Version — statt ein Record pro User oder Order. Weil du den Key immer kennst, liest du ihn mit GetItem und updatest ihn mit plus Condition Expressions.

  • Ein Singleton ist ein Item mit konstantem Key. Du hardcodest PK/SK in deinem Code (z. B. CONFIG#GLOBAL), statt eine User- oder Order-Id zu templatieren.
  • Lies ihn mit GetItem, nie Scan. Du kennst immer den vollen Key, also hat ein Point Read flache, vorhersagbare Kosten (höchstens 1 RCU für ein kleines Item) — kein Filter, kein Table Walk.
  • Er ist per Definition ein . Jeder Request kann dieselbe Partition berühren, also cache ihn und halte das Item klein; mach ihn nicht zum Write-Bottleneck.
  • Mutiere ihn sicher mit Update + Condition Expressions, nicht Read-Modify- Write in deiner App — dort lebt die Lost-Update-Race.

Das Pattern erkennen

Du hast globalen State, wenn die Daten nicht auf eine Entity scoped sind. Ein paar Tells:

  • Ein Flag, das für alle gleich ist (signup_enabled = false).
  • Ein Blob Tunables, den deine App beim Boot liest (Rate Limits, Default Quotas).
  • Ein Counter oder eine Version für das ganze System, nicht per Row.

Alles, was auf User, Tenant oder Order scoped ist, ist kein Singleton — das ist ein gewöhnliches Item, keyed by der Id dieser Entity. Der Singleton ist der übrig gebliebene globale Slice, der nirgendwo sonst leben kann.

Gib ihm einen konstanten Key

Das ganze Pattern hängt an einer Entscheidung. Der Key ist ein Literal, kein Template. Für ein globales Feature-Flags-Item in einer überladenen Single Table wähle ein fixes Prefix und einen fixen Wert:

PKSKattributes
SETTINGS#APPFLAGS#V1signup_enabled, maintenance_mode, ai_search_enabled

PK = "SETTINGS#APP" und SK = "FLAGS#V1" sind in den Code gebacken. Keine User- Id, keine Tenant-Id — die Application fragt jedes Mal genau dieses Item. Diese Vorhersagbarkeit ist der Punkt: ein bekannter Key ist ein GetItem, und ein GetItem ist der günstigste, konsistenteste Read, den DynamoDB bietet.

Das V1-Suffix ist Absicht. Wenn sich die Flag-Schema-Form später ändert, schreibst du ein FLAGS#V2-Item und flipst Reader um, statt das Live-eine in Place zu mutieren. Den Singleton-Key zu versionieren kauft dir eine saubere Migrationsnaht.

Lies ihn mit GetItem

Weil der Key vollständig bekannt ist, Queryst und Scanst du nie nach einem Singleton. Ein Scan liest die ganze Tabelle und filtert clientseitig — der klassische Scan-Fallstrick — und ist absurder Overkill, um eine Row zu holen, die du direkt addressieren kannst.

Ein GetItem gegen SETTINGS#APP / FLAGS#V1 liefert die Flags in einem einzelnen stark oder eventuell konsistenten Read. On-Demand in us-east-1 rechnet AWS ein GetItem eines Items ≤ 4 KB als 0,5 RCU eventually- consistent oder 1 RCU strongly-consistent ab (AWS read/write capacity docs). Halte den Singleton klein und diese Kosten bleiben forever flach — den Config- Blob in einen zweiten 4-KB-Block aufzublähen verdoppelt den abgerechneten Read.

Auf dem Read-Pfad, wenn die App bootet oder ein Request landet, GetItemst du den festen Key und cachest das Result. Der Flow:

janeinApp / RequestGetItem PK=SETTINGS#APPSK=FLAGS#V1Item gefunden?Flags nutzen, lokal cachenAuf sichere Defaults fallen

Der feste Key verwandelt einen globalen Lookup in einen Point Read mit eingebautem Default-Pfad.

Beachtee den nein-Branch: ein fehlender Singleton sollte dich nie crashen. Default zum sicheren Wert (Feature aus, Maintenance an), damit eine First- Deploy-Lücke oder ein schlechter Key closed failt, nicht open.

Update ohne Race

Die Falle ist, einen Singleton mit Read-Modify-Write in deiner App zu updaten: du GetItemst die Flags, flipst eine im Memory, dann PutItemst das Ganze zurück. Zwei concurrent Writer lesen beide das alte Item und der zweite Put clobbert die Änderung des ersten. Lost Update.

Zwei DynamoDB-Features töten die Race ohne App-seitiges Locking:

  • mutieren ein Attribut serverseitig und lassen den Rest unberührt. Kein Re-Put des ganzen Items nötig.
  • lassen den Write nur succeeden, wenn das Item noch so aussieht, wie du erwartest, sodass ein stale Write mit ConditionalCheckFailedException rejected wird (AWS condition expression docs).

Um ein Flag zu flippen, targete genau dieses Attribut mit einem SET und schütze es mit einem Version-Bump, sodass concurrent Writer einander nicht trampeln:

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

Wenn zwei Writer racen, scheitert der schema_version = :current-Check des zweiten und er retryt gegen den frischen Wert. Du kannst Names, Values und genau diese Expression-Form im DynamoDB Expression Builder scaffolden, bevor du sie in Code verdrahtest. Für einen tieferen Blick auf die Operatoren siehe den Update-Expression-Idioms-Guide.

Achte auf den Hot Key

Ein Singleton ist by Construction ein Hot Key — jeder Teil deiner App kann dieselbe Partition lesen. Das ist fine für Reads, wenn du cachest, aber es ist das eine echte Risiko des Patterns.

  • Cache aggressiv. Lies die Flags einmal pro Process (oder pro N Sekunden), nicht bei jedem Request. Der Wert des Singletons ist das günstigste zum Memoizen.
  • Mach ihn nicht zum Write-Hotspot. Ein Flag, das ein Admin ein paar Mal am Tag toggled, ist nichts. Ein Singleton, den du bei jedem Request inkrementierst, ist ein Partition-Throughput-Bottleneck — das ist ein Counter-Problem, kein Singleton.
  • Halte ihn klein. Read-Kosten skalieren mit Item-Größe in 4-KB-Blöcken. Ein aufgeblähter Config-Blob macht jeden Boot teurer als nötig.

Brauchst du wirklich einen High-Write-Global-Counter, ist der Singleton die falsche Form — shard ihn über N Items und summiere on Read. Das ist ein anderes Pattern.

Singleton vs Per-Entity-Item

Die Linie ist schlicht worauf die Daten scoped sind.

Singleton-ItemPer-Entity-Item
KeyHardgecodete Konstante (SETTINGS#APP)Mit Id templatisiert (USER#42)
Wie vieleGenau einsEins pro User / Order / Tenant
Typischer ReadGetItem auf dem bekannten KeyGetItem oder Query per Entity
ScopeGanze ApplicationEine einzelne Entity
Nutzen fürGlobale Flags, Config, System-VersionProfiles, Orders, alles Per-Id

Wenn du zwei Singletons derselben Art willst, hast du keinen Singleton — du hast ein Per-Entity-Item und die Entity ist das, wonach du vergessen hast zu keyen (Per-Tenant-Config, etwa).

Fallstricke und nächste Schritte

  • Scan nicht danach. Du kennst den Key; addressiere ihn direkt.
  • Read-Modify-Write ihn nicht. Nutze Update + Condition Expressions.
  • Lass ihn nicht still fehlen. Default zum sicheren Wert bei Cache Miss.
  • Überlade ihn nicht mit High-Frequency-Writes. Das ist ein Sharded-Counter- Job.

Der Singleton lebt komfortabel in einem Single-Table Design — er ist nur eine weitere Item Collection mit festem Key neben deinen Entity-Rows.

Probier DynoTable, um deine Tabelle zu browsen, die Singleton-Row per festem Key zu finden und Flags von Hand zu editieren, während du den Write-Pfad baust.

Aktualisiert