Mattschwarzer Schubladen-Block mit acht identischen Fächern und Brass-Schildern, eines leicht aufgezogen, weiches Tageslicht.
Extension · moselwal/keyvalue-store

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
Das Problem

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

Konfiguration

Der einfachste Weg ist über moselwal/typo3-configautoconfigureCaching() 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

PaketTypZweck
ext-redis >= 6.3Requiredphpredis mit v6-Constructor-API
moselwal/devDevGeteiltes 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.

OperationTYPO3 CoreKeyValueBackend v4.3.0
set()SETEX + SMEMBERS + optionales MULTI/PIPELINE Tag-DiffEinzelnes Lua EVAL — atomar, 1 Roundtrip
flush()KEYS prefix* + DEL — blockiert alle Clients im Event-LoopSCAN + UNLINK-Batches — Server bleibt responsiv
flushByTag() / flushByTags()N× sequenzieller Fan-outEin sUnion + ein gepipelinetes UNLINK
collectGarbage()KEYS identTags:*SCAN-Loop
Verbindungpconnect() only, kein Sentinel/TLSKeyValueConnectionFactory — Sentinel, mTLS, Jitter-Backoff
SerializerPHP-nativ, hartkodiertKonfigurierbar: 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.

OperationCore / v4.0.xv4.3.0Faktor
Bootstrap 11 Caches25,1 ms0,07 ms381×
getAll() 500 Sessions37,2 ms1,5 ms24,6×
renew() (Session Fixation)360 µs161 µs2,2×
Retry-Backoff (2 Failures)162 ms31 ms5,1×
set() 1 Tag353 µs264 µs1,3×
set() 5 Tags421 µs266 µs1,6×
set() 10 Tags421 µs286 µs1,5×
set() 20 Tags582 µs299 µs1,9×
flushByTags(10 Tags)4,1 ms1,2 ms3,3×
flush(10 k Keys)7,4 ms9,5 ms−30 % Wallclock, kein Event-Loop-Block
collectGarbage(5 k Keys)1,4 ms2,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:

WertVerhalten
'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.

Packagist

composer require moselwal/keyvalue-store

Paket auf Packagist →
Packagist

GitHub

Quellcode, Issues und Changelogs. GPL-2.0-or-later.

Auf GitHub ansehen →
GitHub
Nächster Schritt

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.

Cluster-Setup besprechen

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.