PHPackages                             moselwal/keyvalue-store - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Caching](/categories/caching)
4. /
5. moselwal/keyvalue-store

ActiveTypo3-cms-extension[Caching](/categories/caching)

moselwal/keyvalue-store
=======================

KeyValue Store integration for TYPO3 (Redis/Valkey) with PHPRedis &gt;= 6.3, Sentinel and TLS/mTLS.

v4.4.1(1mo ago)050MITPHPPHP ^8.5

Since Jun 6Pushed 1mo agoCompare

[ Source](https://github.com/Moselwal-Digitalagentur/keyvalue-store)[ Packagist](https://packagist.org/packages/moselwal/keyvalue-store)[ Docs](https://moselwal.de)[ RSS](/packages/moselwal-keyvalue-store/feed)WikiDiscussions main Synced 1w ago

READMEChangelogDependencies (5)Versions (4)Used By (0)

KeyValue Store — TYPO3 Extension
================================

[](#keyvalue-store--typo3-extension)

[![TYPO3 14](https://camo.githubusercontent.com/8f02675f3749751be8f38b62ba260ffb937ee6fad6c4b7ab3a1e704af48fea2a/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5459504f332d31342e782d6f72616e67652e737667)](https://get.typo3.org/)[![PHP 8.5+](https://camo.githubusercontent.com/2788132aa1e54031a6c94edcbf8688566d3e18cb5492cd1766836f74a24b27b5/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5048502d382e352532422d626c75652e737667)](https://php.net/)[![phpredis 6.3+](https://camo.githubusercontent.com/73de1fc3aea5d81e2c4d1444a556ecfd8bb33057f0ce5887f32b58a7e7811c82/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f70687072656469732d362e332532422d7265642e737667)](https://github.com/phpredis/phpredis)[![License: GPL-2.0-or-later](https://camo.githubusercontent.com/d865d3f061b5bdb93940bfc940d9e431166c9a28b20e625622a9d77854d1e3c7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d47504c2d2d322e302d2d6f722d2d6c617465722d677265656e2e737667)](https://www.gnu.org/licenses/gpl-2.0.html)

Redis/Valkey integration for TYPO3 14: cache backend, session backend, distributed locking. Production-ready Sentinel discovery, TLS, and mTLS. Built-in override pack against TYPO3 Core's legacy Redis anti-patterns (`KEYS`, blocking `DEL`).

Components
----------

[](#components)

ComponentClassDrop-in forCache backend`KeyValueBackend``TYPO3\CMS\Core\Cache\Backend\RedisBackend`Session backend`KeyValueSessionBackend``TYPO3\CMS\Core\Session\Backend\RedisSessionBackend`Locking strategy`KeyValueLockingStrategy``LockingStrategyInterface` (registered automatically)Installation
------------

[](#installation)

```
composer require moselwal/keyvalue-store
```text

### Requirements

- PHP 8.5+
- TYPO3 14.x
- `ext-redis` >= 6.3 (phpredis with the v6 constructor-config API,
  Sentinel resolver, TLS context, and decorrelated-jitter backoff)
- Redis 4.0+ or Valkey (UNLINK is required for the v4.2.0 override pack)

## Configuration

The cleanest path is via [`moselwal/typo3-config`](../typo3-config) —
its `autoconfigureCaching()`, session-binding, and locking helpers wire
all three components consistently. Manual configuration is supported
and documented below.

### Cache backend

```php
$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',
        'peer_name'            => 'valkey.internal',
        'verify_peer'          => true,
        'verify_peer_name'     => true,

        // Sentinel (optional)
        'sentinel'             => true,
        'sentinel_host'        => 'sentinel.internal',
        'sentinel_port'        => 26379,
        'sentinel_service'     => 'mymaster',

        // phpredis 6.x backoff (optional)
        'backoff' => [
            'algorithm' => \Redis::BACKOFF_ALGORITHM_DECORRELATED_JITTER,
            'base'      => 500,
            'cap'       => 750,
        ],
    ],
];
```

### Session backend

[](#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,
        // TLS / Sentinel options as above
    ],
];
```text

### Locking strategy

The locking strategy registers via TYPO3's `LockFactory` configuration:

```php
$GLOBALS['TYPO3_CONF_VARS']['SYS']['locking']['strategies'][
    \Moselwal\KeyValueStore\Locking\KeyValueLockingStrategy::class
] = [
    'hostname'             => 'valkey.internal',
    'database'             => 0,
    'password'             => 'secret',
    'persistentConnection' => true,
    'ttl'                  => 10,
    // TLS / Sentinel options as above
];
```

What v4.x does differently from TYPO3 Core's `RedisBackend`
-----------------------------------------------------------

[](#what-v4x-does-differently-from-typo3-cores-redisbackend)

The `KeyValueBackend` extends `RedisBackend` and overrides four operations to drop server-side anti-patterns. The other operations (`get`, `set`, `has`, `remove`, `findIdentifiersByTag`) are inherited verbatim — Core already pipelines tag tracking there, so there is nothing to add.

OperationTYPO3 Core`KeyValueBackend` (v4.3.0)`set()`SETEX + SMEMBERS + (optional MULTI/PIPELINE) tag diffSingle Lua `EVAL` (atomic, 1 roundtrip)`flush()``KEYS prefix*` + `DEL` (event-loop block for all clients)`SCAN` + `UNLINK` batches (server stays responsive)`flushByTag()` / `flushByTags()`N× sequential `flushByTag()` fan-outOne `sUnion` + one pipelined `UNLINK``collectGarbage()``KEYS identTags:*``SCAN`-loopConnection`pconnect()` only, no Sentinel/TLS`KeyValueConnectionFactory` (Sentinel resolver, mTLS, backoff)Serializerhard-coded PHP-nativeconfigurable: `php` / `igbinary` / `none` / `auto`Additionally:

- `lazy=true` is the default — the TCP/TLS handshake is deferred to the first command. Bootstrap of 11 cache backends drops from ~25 ms to ~0.07 ms on a real mTLS Valkey setup.
- `OPT_SCAN = SCAN_RETRY` is set on every connection so phpredis retries empty SCAN pages internally instead of returning `false`to the caller (a well-known phpredis 6.x footgun).

### Bench (real, container-side against Valkey/mTLS, phpredis 6.3.0)

[](#bench-real-container-side-against-valkeymtls-phpredis-630)

OperationCore / v4.0.xv4.3.0ΔBootstrap 11 caches25.1 ms0.07 ms**381×**`getAll()` 500 sessions37.2 ms1.5 ms**24.6×**`renew()` (session fixation)360 µs161 µs2.2×Retry-Backoff (2 failures)162 ms31 ms**5.1×**`set()` 1 tag353 µs264 µs1.3×`set()` 5 tags421 µs266 µs**1.6×**`set()` 10 tags421 µs286 µs1.5×`set()` 20 tags582 µs299 µs**1.9×**`flushByTags(10 tags)`4.1 ms1.2 ms**3.3×**`flush(10 k keys)`7.4 ms9.5 ms−30 % wallclock, no event-loop block`collectGarbage(5 k keys)`1.4 ms2.8 ms−50 % wallclock, no event-loop blockThe `flush()` and `collectGarbage()` overrides are intentionally slower wallclock-wise for the caller. The trade-off is server-side fairness: while `KEYS` runs, every other client in the Valkey instance is blocked. With `SCAN`, the server can interleave other clients between batches — for a multi-site, multi-pod setup that is the more important property. Neither path is a hot-path operation (`flush()` is BE/CLI-triggered; `collectGarbage()` runs in the scheduler tick).

Session backend internals
-------------------------

[](#session-backend-internals)

`KeyValueSessionBackend` is a from-scratch implementation of TYPO3's `SessionBackendInterface`. Notable choices:

- **JSON serialisation** for session payloads (not PHP-native) so records remain debuggable with `valkey-cli`
- **WATCH / MULTI / EXEC** on `update()` with bounded retries — optimistic concurrency, two-tab login is safe
- **Lua EVAL for `renew()`** — atomic GET/SETEX/DEL so concurrent updates cannot be lost during the rename
- **`getAll()` via SCAN + MGET** instead of SCAN + N× GET — 24.6× faster on 500 sessions

Locking strategy internals
--------------------------

[](#locking-strategy-internals)

`KeyValueLockingStrategy` uses the Lua-EVAL + BLPOP pattern recommended by the Redis docs:

- `tryLock()` is a single atomic `SET key value NX EX ttl`
- `wait()` blocks via server-side `BLPOP` on a signal list — no client-side polling
- `unlockAndSignal()` runs a Lua script that atomically verifies ownership (GET), releases (DEL), and wakes one waiter (RPUSH + EXPIRE)

Architecture
------------

[](#architecture)

```
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/
        ├── ConnectionParams.php
        └── Endpoint.php

```

All three TYPO3-facing components route their phpredis instantiation through `KeyValueConnectionFactory` so Sentinel, TLS, mTLS, and the phpredis 6.x backoff config are configured in exactly one place.

Development
-----------

[](#development)

Tooling lives in [`moselwal/dev`](../dev). Common commands:

```
composer install
vendor/bin/phpunit -c Tests/phpunit.xml --testsuite=Unit
vendor/bin/phpunit -c Tests/phpunit.xml --testsuite=Functional   # needs Redis
vendor/bin/phpstan analyse Classes --level=8
vendor/bin/php-cs-fixer fix Classes Tests --config=vendor/moselwal/dev/.php-cs-fixer.dist.php
```text

Tests gated with `#[RequiresPhpExtension('redis')]` skip locally when
ext-redis is missing and run in CI / dev containers where it is
installed.

## Dependencies

| Package | Type | Purpose |
|---|---|---|
| `php` ^8.5 | Required | Language baseline |
| `typo3/cms-core` ^14.0 | Required | TYPO3 core |
| `ext-redis` >= 6.3 | Required | phpredis with v6 constructor API |
| `moselwal/dev` ^5.2 | Dev | Shared QA tooling (phpstan, cs-fixer, etc.) |

## Related

- [`moselwal/typo3-config`](../typo3-config) — Fluent TYPO3 config API
  with `autoconfigureCaching()` that wires this package
- [`moselwal/cluster-file-backend`](../cluster-file-backend) — Pod-local
  cache backend that uses this package's `KeyValueBackend` as its
  metadata storage layer

## Serializer

`KeyValueBackend` defaults to PHP-native serialization (`SERIALIZER_PHP`).
Operators can opt into other phpredis serializers via the `serializer`
option:

```php
'options' => [
    'serializer' => 'php' | 'igbinary' | 'none' | 'auto',
    // …
],
```

ValueBehaviour`'php'` *(default)*PHP-native, BC-safe, identical to v4.2.0`'igbinary'`igbinary when `ext-igbinary` is loaded; falls back to PHP-native with a notice otherwise`'none'`No phpredis-layer serialization; the caller serializes`'auto'`igbinary if loaded, php otherwise (**not the default**, see below)**⚠️ Switching the serializer requires a full cache flush of all affected cache databases.** Existing payloads stay in the previous format and will fail to deserialize on read. Recommended deploy sequence:

```
# 1. Flush each affected Valkey DB
valkey-cli -n 3 FLUSHDB    # pages
valkey-cli -n 4 FLUSHDB    # hash
# … repeat for each cache DB

# 2. Restart workers so connections re-initialise with the new option

# 3. Deploy the new typo3-config with the serializer change
```text

**When `auto` is the wrong default**: an image update that ships
`ext-igbinary` would silently switch the on-disk format for any cache
that uses `auto` — and on the next read of an old PHP-serialised
payload, the cache would throw. Pinning the value explicitly (`'php'`
or `'igbinary'`) makes the contract observable.

**When `igbinary` is worth it**: only for caches storing deeply nested
arrays/objects (e.g. extbase ClassSchema, fluid template reflection).
For string-content caches (rendered pages, large text blobs) or flat
key/value caches (`hash`, `imagesizes`) the igbinary encoder overhead
dominates the marginal payload-size win — keep the default. See
[CHANGELOG.md](CHANGELOG.md) for measured numbers.

The session backend (`KeyValueSessionBackend`) uses JSON internally
for debuggability via `valkey-cli` — this option has no effect there.

## Changelog

See [CHANGELOG.md](CHANGELOG.md). Notable releases:

- **v4.3.0** — Lua-EVAL `set()` (1.3×–1.9× faster); `serializer` opt-in;
  KeyValueLockingStrategy audit (configurable `maxAcquireAttempts`,
  lazy connect, TTL-cache in `wait()`, log-level differentiation)
- **v4.2.0** — TYPO3 Core override pack (`flush`, `flushByTag`,
  `collectGarbage`); critical fix for `getAll()` SCAN cursor
- **v4.1.0** — Lazy-connect, MGET-based `getAll()`, atomic `renew()`,
  decorrelated-jitter backoff
- **v4.0.0** — PHP 8.5 baseline, TYPO3 14-only, phpredis 6.3 required

## License

GPL-2.0-or-later — see [LICENSE](LICENSE) for details.
```

###  Health Score

43

—

FairBetter than 89% of packages

Maintenance90

Actively maintained with recent releases

Popularity11

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

3

Last Release

47d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/7e0a48f51d9fddd2b5104df8997e3281935968f4895d7c0392e719ecb916ccea?d=identicon)[o.hartwig](/maintainers/o.hartwig)

---

Top Contributors

[![ohartwig](https://avatars.githubusercontent.com/u/4158815?v=4)](https://github.com/ohartwig "ohartwig (49 commits)")

---

Tags

rediscachesessiontypo3lockkeyvaluevalkey

### Embed Badge

![Health badge](/badges/moselwal-keyvalue-store/health.svg)

```
[![Health](https://phpackages.com/badges/moselwal-keyvalue-store/health.svg)](https://phpackages.com/packages/moselwal-keyvalue-store)
```

###  Alternatives

[byerikas/cache-tags

Allows for Redis/Valkey cache flushing multiple tagged items by a single tag.

1420.4k](/packages/byerikas-cache-tags)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
