Zum Inhalt springen
Deutsch

Cassandra-Journal

CassandraJournal speichert Events in einem Cassandra-Cluster. Anders als SQLite (eine Datei pro Node) wird Cassandra über Cluster-Nodes geteilt — jeder Node kann Events für jede persistenceId anhängen, lesen oder abfragen.

import { CassandraJournal, PersistenceExtensionId } from 'actor-ts';
system.extension(PersistenceExtensionId).configure({
journal: new CassandraJournal({
contactPoints: ['cassandra-1:9042', 'cassandra-2:9042'],
keyspace: 'my_app_events',
table: 'events',
}),
});

Cassandra ist die Produktionswahl für Multi-Node-Cluster mit geteilter Persistenz:

  • Sharded Entities, die zwischen Nodes wandernPersistentActors, die auf verschiedenen Nodes gespawnt werden, müssen während des Rebalance die Journals der anderen lesen.
  • Cross-Node-Projektionen — eine Projektion auf Node-A muss Events sehen, die auf Node-B geschrieben werden.
  • High-Throughput-Single-Shard-Szenarien, die die Pro-Maschine- Decke von SQLite überschreiten.

Für Single-Node-Deployments ist SqliteJournal einfacher und billiger — Cassandra hat operative Komplexität (Multi-Node- Cluster, Repair, Tuning), die du nicht brauchst.

interface CassandraJournalOptions {
contactPoints: string[]; // Cluster-Contact-Points
keyspace: string; // Keyspace (extern erstellt)
table?: string; // Name der Events-Tabelle, Default 'events'
tagsTable?: string; // Tag-Index-Tabelle, Default 'events_tags'
consistencyLevel?: ConsistencyLevel; // Default LOCAL_QUORUM
/* ... plus Treiber-Level-Optionen ... */
}
FeldWas
contactPointsInitiale Cassandra-Contact-Nodes. Der Treiber entdeckt den Rest.
keyspaceBereits existierender Keyspace. Das Framework erstellt Tabellen, aber nicht den Keyspace selbst.
tableName der Events-Tabelle. Default events.
tagsTableTag-Index-Tabelle. Default events_tags.
consistencyLevelTreiber-Consistency für Reads/Writes. LOCAL_QUORUM für Produktion.

Das Framework erstellt die beiden Tabellen automatisch bei der ersten Verwendung, mit den Schemas:

CREATE TABLE events (
pid text,
seq bigint,
event blob,
ts bigint,
PRIMARY KEY (pid, seq)
);
CREATE TABLE events_tags (
tag text,
ts bigint,
pid text,
seq bigint,
event_ref blob,
PRIMARY KEY (tag, ts, pid, seq)
);

Die Events-Tabelle ist nach pid geschlüsselt — Recovery für eine persistenceId liest eine Partition. Die Tags-Tabelle ist nach tag geschlüsselt — Projection-Queries treffen eine Partition pro Tag.

Provisioniere den Keyspace mit angemessener Replikation:

CREATE KEYSPACE my_app_events
WITH replication = {
'class': 'NetworkTopologyStrategy',
'datacenter1': 3,
};

NetworkTopologyStrategy mit einem Replikations-Faktor von 3 ist typisch für Produktion. Die Writes des Frameworks gehen via LOCAL_QUORUM, was 2 von 3 Replicas für das Ack braucht.

Cassandra ist schließlich konsistent über Replicas — aber jeder Write ist pro pid linearisiert (über seq als Partitioning-Key). Praktische Garantien:

  • Die Events einer gegebenen pid haben eine strikte Total-Order (sequenceNr).
  • Replays sehen Events in seq-Reihenfolge, unabhängig davon, welche Cassandra-Replica antwortet.
  • Cross-pid-Event-Order in Tag-Queries ist Timestamp-gebunden, aber nicht strikt — Events mit demselben ts können sich verschachteln.

Für die meisten Event-sourced Anwendungen ist das in Ordnung — innerhalb einer einzelnen Entity (pid) ist die Reihenfolge strikt; über Entities hinweg ist die partielle Reihenfolge über Timestamp akzeptabel.

Cassandra unterstützt nativ Multi-Datacenter-Replikation. Konfiguriere Replikation pro DC:

CREATE KEYSPACE my_app_events
WITH replication = {
'class': 'NetworkTopologyStrategy',
'dc1': 3,
'dc2': 3,
};

Das actor-ts-Journal kümmert das nicht — Writes gehen ins lokale DC (über LOCAL_QUORUM), Cross-DC-Replikation ist asynchron und wird von Cassandra gehandhabt.

Approximative Write-Performance (einzelner Cassandra-Cluster):

  • Single-pid-Append — Sub-Millisekunde auf Journal-Ebene. Getrieben von Cassandras Commit-Log + Memtable.
  • Cross-pid-Durchsatz — skaliert linear mit der Cluster-Größe. 10K Events/sec pro Cassandra-Node sind realistisch.
  • Tag-Query — durch die Tag-Partitions-Größe begrenzt. Heiße Tags (jedes Event mit ‘audit’ getaggt) werden zu heißen Partitions; überlege feinkörnigeres Tagging oder Bucketing, wenn du einen Tag siehst, der 100M+ Events trägt.

Cassandra hat seine eigene Backup-Strategie — Snapshots via nodetool snapshot, inkrementelle Backups, plus operatives Tooling (Medusa, Cassandra Backup Tool). Das Journal fügt nichts Besonderes hinzu; behandle es, wie du jeden anderen Cassandra-Keyspace behandeln würdest.

Die CassandraJournal-API-Referenz deckt die vollständigen Optionen ab.