
keyvalue-store — Redis/Valkey, production-grade.
Redis/Valkey-Integration für TYPO3 14 mit production-reifem Cache-Backend, Session-Backend und Distributed-Locking-Strategy. Sentinel-Discovery, TLS/mTLS und phpredis 6.3+ inklusive. Das Cache-Backend löst vier Anti-Pattern des TYPO3-Core-RedisBackend auf (KEYS, blockierendes DEL, sequentielles Tag-Flushing, fehlende Sentinel-/TLS-Unterstützung).
- Composer-Paket:
composer require moselwal/keyvalue-store - TYPO3: ^14.0 · PHP: 8.5+ · ext-redis: >= 6.3 · GPL-2.0-or-later
TYPO3-Caching ist Standard — aber wirklich production-ready?
Mit keyvalue-store
- TYPO3-Caching-Framework-Backends für Pages, Hash, RootlinePath, Imagesizes etc.
- Session-Storage in Redis/Valkey mit Replication-Awareness
- Distributed Locking über alle Nodes
- Sentinel-Support out of the box
- TLS und mTLS als Konfigurationsoption
Bisher
- Standard-Redis-Backend ohne Sentinel-Awareness
- Sessions in Datenbank — unnötige Last
- Locking als File-Lock oder DB-Lock — nicht clusterfähig
- TLS/mTLS-Konfiguration als Custom-Bastelei
Vier Bausteine
Sentinel & mTLS
Sentinel-Discovery aktiviert automatisches Failover. mTLS-Konfiguration für verschlüsselte Inter-Service-Kommunikation in Container-Setups.
Distributed Locking
Lua-EVAL + BLPOP-Pattern — atomares SET key NX EX ttl für tryLock(), server-seitiges BLPOP statt Client-Polling für wait(). Cluster-weit korrekt, ohne Polling-Overhead.
Session-Storage
Frontend- und Backend-Sessions in Redis/Valkey — entlastet die Datenbank und macht Multi-Node-Setups nahtlos.
Caching-Backends
Drop-in-Backends für alle TYPO3-Caching-Framework-Caches: Pages, Hash, RootlinePath, Imagesizes — inklusive Tag-basiertem Flushing.
Architektur
Classes/
├── Cache/Backend/
│ └── KeyValueBackend.php (extends TYPO3 Core RedisBackend)
├── Session/Backend/
│ └── KeyValueSessionBackend.php (implements SessionBackendInterface)
├── Locking/
│ └── KeyValueLockingStrategy.php (implements LockingStrategyInterface)
└── Connection/
├── KeyValueConnectionFactory.php
├── SentinelResolver.php
├── TlsContextBuilder.php
└── ValueObject/
Alle drei TYPO3-facing Components routen ihre phpredis-Instantiierung über KeyValueConnectionFactory — Sentinel, TLS, mTLS und der phpredis-6.x-Backoff werden genau einmal konfiguriert.
Voraussetzungen
- PHP 8.5+
- TYPO3 ^14.0 (Composer-Mode)
ext-redis>= 6.3 (phpredis mit v6-Constructor-Config-API)- Redis 4.0+ oder Valkey (
UNLINKwird benötigt)
Konfiguration
Der einfachste Weg ist über moselwal/typo3-config — autoconfigureCaching() verdrahtet alle drei Komponenten konsistent. Manuelle Konfiguration in config/config.php:
Cache-Backend
$GLOBALS['TYPO3_CONF_VARS']['SYS']['caching']['cacheConfigurations']['pages'] = [
'backend' => \Moselwal\KeyValueStore\Cache\Backend\KeyValueBackend::class,
'options' => [
'hostname' => 'valkey.internal',
'port' => 6379,
'database' => 3,
'password' => 'secret',
'persistentConnection' => true,
'defaultLifetime' => 2592000,
'compression' => true,
// TLS/mTLS (optional)
'tls' => true,
'ca_file' => '/run/tls/ca.crt',
'cert_file' => '/run/tls/httpd.crt',
'key_file' => '/run/tls/httpd.key',
// Sentinel (optional)
'sentinel' => true,
'sentinel_host' => 'sentinel.internal',
'sentinel_port' => 26379,
'sentinel_service' => 'mymaster',
],
];
Session-Backend
$GLOBALS['TYPO3_CONF_VARS']['SYS']['session']['BE'] = [
'backend' => \Moselwal\KeyValueStore\Session\Backend\KeyValueSessionBackend::class,
'options' => [
'hostname' => 'valkey.internal',
'database' => 1,
'password' => 'secret',
'persistentConnection' => true,
'persistentId' => 'typo3-session-be',
'prefix' => 'typo3:sess:be:',
'sessionLifetime' => 3600,
],
];
Locking-Strategy
$GLOBALS['TYPO3_CONF_VARS']['SYS']['locking']['strategies'][
\Moselwal\KeyValueStore\Locking\KeyValueLockingStrategy::class
] = [
'hostname' => 'valkey.internal',
'database' => 0,
'password' => 'secret',
'persistentConnection' => true,
'ttl' => 10,
];
Abhängigkeiten
| Paket | Typ | Zweck |
|---|---|---|
ext-redis >= 6.3 | Required | phpredis mit v6-Constructor-API |
moselwal/dev | Dev | Geteiltes QA-Tooling |
Was v4.x anders macht als das TYPO3-Core-Backend
KeyValueBackend erweitert den TYPO3-eigenen RedisBackend und überschreibt vier Operationen, die serverseitig Anti-Patterns erzeugen. Alle anderen Operationen (get, has, remove, findIdentifiersByTag) werden unverändert geerbt — Core pipelines das Tag-Tracking dort bereits korrekt.
| Operation | TYPO3 Core | KeyValueBackend v4.3.0 |
|---|---|---|
set() | SETEX + SMEMBERS + optionales MULTI/PIPELINE Tag-Diff | Einzelnes Lua EVAL — atomar, 1 Roundtrip |
flush() | KEYS prefix* + DEL — blockiert alle Clients im Event-Loop | SCAN + UNLINK-Batches — Server bleibt responsiv |
flushByTag() / flushByTags() | N× sequenzieller Fan-out | Ein sUnion + ein gepipelinetes UNLINK |
collectGarbage() | KEYS identTags:* | SCAN-Loop |
| Verbindung | pconnect() only, kein Sentinel/TLS | KeyValueConnectionFactory — Sentinel, mTLS, Jitter-Backoff |
| Serializer | PHP-nativ, hartkodiert | Konfigurierbar: php / igbinary / none / auto |
Lazy Connect (lazy=true Standard) — TCP/TLS-Handshake wird auf den ersten Befehl verschoben. Bootstrap von 11 Cache-Backends sinkt von ~25 ms auf ~0,07 ms in einem realen mTLS-Valkey-Setup (381×). OPT_SCAN = SCAN_RETRY wird auf jeder Verbindung gesetzt, damit phpredis 6.x leere SCAN-Seiten intern wiederholt statt false zurückzugeben.
Benchmarks (Container gegen Valkey/mTLS, phpredis 6.3.0)
Gemessen container-seitig gegen Valkey mit mTLS. Die Zahlen vergleichen TYPO3 Core / v4.0.x mit v4.3.0.
| Operation | Core / v4.0.x | v4.3.0 | Faktor |
|---|---|---|---|
| Bootstrap 11 Caches | 25,1 ms | 0,07 ms | 381× |
getAll() 500 Sessions | 37,2 ms | 1,5 ms | 24,6× |
renew() (Session Fixation) | 360 µs | 161 µs | 2,2× |
| Retry-Backoff (2 Failures) | 162 ms | 31 ms | 5,1× |
set() 1 Tag | 353 µs | 264 µs | 1,3× |
set() 5 Tags | 421 µs | 266 µs | 1,6× |
set() 10 Tags | 421 µs | 286 µs | 1,5× |
set() 20 Tags | 582 µs | 299 µs | 1,9× |
flushByTags(10 Tags) | 4,1 ms | 1,2 ms | 3,3× |
flush(10 k Keys) | 7,4 ms | 9,5 ms | −30 % Wallclock, kein Event-Loop-Block |
collectGarbage(5 k Keys) | 1,4 ms | 2,8 ms | −50 % Wallclock, kein Event-Loop-Block |
flush() und collectGarbage() sind für den aufrufenden Prozess bewusst wallclock-langsamer. Der Trade-off: KEYS blockiert während der Ausführung alle anderen Clients im Valkey-Instance. Mit SCAN kann der Server zwischen den Batches andere Clients bedienen — in einem Multi-Site- oder Multi-Pod-Setup ist das die wichtigere Eigenschaft. Beide Operationen sind kein Hot-Path (flush() wird per BE/CLI ausgelöst, collectGarbage() läuft im Scheduler-Tick).
Serializer
KeyValueBackend verwendet standardmäßig PHP-native Serialisierung. Über die Option serializer lässt sich das pro Cache-Backend umstellen:
| Wert | Verhalten |
|---|---|
'php'(Standard) | PHP-nativ, BC-sicher, identisch mit v4.2.0 |
'igbinary' | igbinary wenn ext-igbinary geladen, sonst Fallback auf PHP-nativ mit Notice |
'none' | Keine phpredis-seitige Serialisierung — der Aufrufer serialisiert selbst |
'auto' | igbinary wenn verfügbar, sonst php (nicht empfohlen, siehe unten) |
⚠️ Serializer-Wechsel erfordert einen vollständigen Cache-Flush aller betroffenen Cache-Datenbanken. Bestehende Payloads liegen im alten Format und scheitern beim Deserialisieren. Empfohlene Deploy-Sequenz:
# 1. Betroffene Valkey-DBs leeren
valkey-cli -n 3 FLUSHDB # pages
valkey-cli -n 4 FLUSHDB # hash
# ... für jede Cache-DB wiederholen
# 2. Worker neu starten, damit Verbindungen mit neuer Option initialisiert werden
# 3. typo3-config mit dem Serializer-Änderung deployen
Warum auto kein sinnvoller Standard ist: Ein Image-Update, das ext-igbinary neu einführt, würde das On-Disk-Format aller Caches auf auto stillschweigend wechseln. Der nächste Lese-Zugriff auf einen alten PHP-serialisierten Payload würde scheitern. Mit explizitem Wert ('php' oder 'igbinary') ist der Vertrag beobachtbar.
Wann igbinary sich lohnt: Nur bei Caches mit tief verschachtelten Arrays/Objekten (z. B. Extbase ClassSchema, Fluid Template Reflection). Bei String-Content-Caches (gerenderte Seiten, große Text-Blobs) oder flachen Key/Value-Caches (hash, imagesizes) dominiert der igbinary-Encoder-Overhead den marginalen Payload-Größen-Vorteil — Standard behalten.
Der Session-Backend (KeyValueSessionBackend) verwendet intern JSON für Debuggability über valkey-cli — diese Option hat dort keine Wirkung.
Cluster-Setup oder Migration vom Standard-Backend?
Für Migration vom Standard-Redis-Backend, Sentinel-Setup oder mTLS-Konfiguration in Production unterstützen wir gerne als Service.
Oder direkt schreiben: kontakt@moselwal.de
Setzen wir ein bei …
Dieses Paket trägt die Cache- und Session-Schicht in TYPO3 Kubernetes — ohne zentralen Cache-Store läuft kein Multi-Pod-Setup sauber. In der betreuten Variante läuft es als Teil unseres AI-Ready CMS as a Service.