Singleton-Items in DynamoDB
Ein Singleton-Item ist eine einzelne Zeile mit einem festen, hartkodierten Key, die Zustand für deine ganze Anwendung hält — nicht ein Datensatz pro Nutzer oder pro Bestellung, sondern ein Datensatz, Punkt. Feature-Flags, ein Config-Blob, ein globaler Kill-Switch: die Art von Sache, die eine relationale App in einer Einzeilen-Settings-Tabelle halten würde.
Von SQL kommend würdest du zu einer config-Tabelle mit id = 1 und einem
SELECT * FROM configgreifen. In DynamoDB machst du dasselbe mit einem hartkodierten
Partition Key — und weil du diesen Key immer kennst, liest du ihn mit einemGetItem, nicht einer Queryoder einemScan.
Was ist ein Singleton-Item in DynamoDB?
Ein Singleton-Item ist eine einzelne DynamoDB-Zeile, die unter einem festen, hartkodierten Key gespeichert ist und globalen Zustand für deine gesamte Anwendung hält — Feature-Flags, einen Config-Blob, eine systemweite Version — statt eines Datensatzes pro Nutzer oder Bestellung. Weil du den Key immer kennst, liest du es mit einem GetItem und aktualisierst es mit - plus Condition-Expressions.
- Ein Singleton ist ein Item mit konstantem Key. Du hartkodierst den
PK/SKin deinem Code (z. B.CONFIG#GLOBAL), statt eine Nutzer- oder Bestell-ID einzusetzen. - Lies es mit
GetItem, nie mitScan. Du kennst immer den vollen Key, also ist ein Point Read eine konsistente, vorhersehbare RCU — kein Filter, kein Tabellendurchlauf. - Es ist per Definition ein Hot Key. Jeder Request kann dieselbe Partition berühren, also cache es und halte das Item klein; mach es nicht zum Schreib-Bottleneck.
- Mutiere es sicher mit Update- + Condition-Expressions, nicht mit Read-Modify-Write in deiner App — dort lauert der Lost-Update-Race.
Das Muster erkennen
Du hast globalen Zustand, wenn die Daten an keine einzelne Entität gebunden sind. Ein paar Anzeichen:
- Ein Flag, das für alle gleich ist (
signup_enabled = false). - Ein Blob von Stellschrauben, die deine App beim Booten liest (Rate Limits, Standardkontingente).
- Ein Zähler oder eine Versionsnummer für das ganze System, nicht pro Zeile.
Alles, was an einen Nutzer, Mandanten oder eine Bestellung gebunden ist, ist kein Singleton — das ist ein gewöhnliches Item, das durch die ID dieser Entität verschlüsselt ist. Das Singleton ist die übrige globale Scheibe, die nirgendwo sonst hinpasst.
Gib ihm einen konstanten Key
Das ganze Muster 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 festes Präfix und einen festen Wert:
| PK | SK | attributes |
|---|---|---|
| SETTINGS#APP | FLAGS#V1 | signup_enabled, maintenance_mode, ai_search_enabled |
PK = "SETTINGS#APP" und SK = "FLAGS#V1" sind im Code festgebacken. Da gibt es
keine Nutzer-ID, keine Mandanten-ID — die Anwendung fragt jedes Mal nach genau
diesem Item. Diese Vorhersehbarkeit ist der Sinn: Ein bekannter Key ist ein
GetItem, und ein GetItem ist der günstigste, konsistenteste Read, den
DynamoDB bietet.
Das V1-Suffix ist gewollt. Wenn sich das Flag-Schema später in der Form ändert,
schreibst du ein FLAGS#V2-Item und schaltest die Reader um, statt das Live-Item
an Ort und Stelle zu mutieren. Den Singleton-Key zu versionieren verschafft dir
eine saubere Migrations-Naht.
Lies es mit GetItem
Weil der Key vollständig bekannt ist, machst du für ein Singleton nie eine Query
und nie einen Scan. Ein Scan liest die ganze Tabelle und filtert
client-seitig — das klassische Scan-Footgun — und es ist
absurder Overkill, um eine Zeile zu holen, die du direkt adressieren kannst.
Ein GetItem gegen SETTINGS#APP / FLAGS#V1 gibt die Flags in einem einzelnen
stark oder letztendlich konsistenten Read zurück. AWS berechnet ein GetItem
eines Items ≤ 4 KB als 0,5 RCU letztendlich konsistent oder 1 RCU
stark konsistent
(AWS Read/Write-Capacity-Doku).
Halte das Singleton klein und diese Kosten bleiben für immer flach.
Der Read-Pfad ist einfach: App bootet oder ein Request landet, du machst ein
GetItem auf den festen Key, du cachest das Ergebnis. Hier ist der Ablauf.
Der feste Key verwandelt einen globalen Lookup in einen Point Read mit eingebautem Default-Pfad.
Beachte den nein-Zweig: Ein fehlendes Singleton sollte dich nie zum Absturz
bringen. Falle auf den sicheren Wert zurück (Feature aus, Maintenance an),
damit eine Lücke beim ersten Deploy oder ein schlechter Key fail-closed scheitert,
nicht fail-open.
Aktualisiere es ohne Race
Die Falle ist, ein Singleton mit Read-Modify-Write in deiner App zu aktualisieren:
Du machst ein GetItem auf die Flags, kippst eines im Speicher, dann ein
PutItem des ganzen Dings zurück. Zwei nebenläufige Writer lesen beide das alte
Item und das zweite Put überschreibt die Änderung des ersten. Lost Update.
Zwei DynamoDB-Features töten den Race ohne app-seitiges Locking:
- Update-Expressions mutieren ein Attribut server-seitig und lassen den Rest
unberührt. Kein Bedarf, das ganze Item erneut zu
Put-en. - Condition-Expressions lassen den Write nur gelingen, wenn das Item noch so
aussieht, wie du es erwartest, sodass ein veralteter Write mit
ConditionalCheckFailedExceptionabgewiesen wird (AWS Condition-Expression-Doku).
Um ein Flag zu kippen, ziele nur auf dieses Attribut mit einem SET und sichere
es mit einem Versions-Bump ab, damit nebenläufige Writer sich nicht gegenseitig
zertreten:
# UpdateItem
Key PK=SETTINGS#APP SK=FLAGS#V1
UpdateExpression SET signup_enabled = :on, schema_version = :next
ConditionExpression schema_version = :current
Wenn zwei Writer um die Wette laufen, scheitert der schema_version = :current-
Check des zweiten und er versucht es erneut gegen den frischen Wert. Du kannst die
Names, Values und genau diese Expression-Form im
DynamoDB Expression Builder gerüstartig
aufbauen, bevor du es in Code verdrahtest. Für einen tieferen Blick auf die
Operatoren siehe den Guide zu den
Update-Expression-Idiomen.
Achte auf den Hot Key
Ein Singleton ist konstruktionsbedingt ein Hot Key — jeder Teil deiner App kann dieselbe Partition lesen. Für Reads ist das in Ordnung, wenn du cachest, aber es ist das eine echte Risiko des Musters.
- Cache aggressiv. Lies die Flags einmal pro Prozess (oder pro N Sekunden), nicht bei jedem Request. Der Wert des Singletons ist das Günstigste zum Memoisieren.
- Mach es nicht zum Schreib-Hotspot. Ein Flag, das ein Admin ein paar Mal pro Tag umschaltet, ist nichts. Ein Singleton, das du bei jedem Request inkrementierst, ist ein Partition-Throughput-Bottleneck — das ist ein Zähler-Problem, kein Singleton.
- Halte es klein. Read-Kosten skalieren mit der Item-Größe in 4-KB-Blöcken. Ein aufgeblähter Config-Blob macht jeden Boot teurer, als er sein müsste.
Wenn du wirklich einen globalen Zähler mit hoher Schreibrate brauchst, ist das Singleton die falsche Form — sharde ihn über N Items und summiere beim Read. Das ist ein anderes Muster.
Singleton vs. Per-Entity-Item
Die Linie ist einfach, woran die Daten gebunden sind.
| Singleton-Item | Per-Entity-Item | |
|---|---|---|
| Key | Hartkodierte Konstante (SETTINGS#APP) | Mit ID templated (USER#42) |
| Wie viele | Genau eines | Eines pro Nutzer / Bestellung / Mandant |
| Typischer Read | GetItem auf den bekannten Key | GetItem oder Query nach Entität |
| Scope | Ganze Anwendung | Eine einzelne Entität |
| Verwenden für | Globale Flags, Config, Systemversion | Profile, Bestellungen, alles pro ID |
Wenn du dich dabei ertappst, zwei Singletons derselben Art zu wollen, hast du kein Singleton — du hast ein Per-Entity-Item und die Entität ist das, nach dem zu verschlüsseln du vergessen hast (etwa Per-Mandant-Config).
Fallstricke und nächste Schritte
- Mach keinen
Scandafür. Du kennst den Key; adressiere ihn direkt. - Mach kein Read-Modify-Write damit. Verwende Update- + Condition-Expressions.
- Lass es nicht still fehlen. Falle bei einem Cache-Miss auf den sicheren Wert zurück.
- Überlade es nicht mit hochfrequenten Writes. Das ist ein Sharded-Counter-Job.
Das Singleton lebt bequem in einem Single-Table-Design — es ist einfach eine weitere Item-Collection mit festem Key neben deinen Entitätszeilen.
Probiere DynoTable aus, um deine Tabelle zu durchsuchen, die Singleton-Zeile an ihrem festen Key zu finden und Flags von Hand zu bearbeiten, während du den Schreibpfad baust.