Skip to content

Winter Boot Node-Local In-Memory KV and Queue Stores

Winter Boot ships two lightweight stores — a key-value store and a queue store — that run as child processes next to your server workers. Every worker on the host talks to them over a local TCP socket, so they can share state with no external infrastructure. Both are optional and off by default, and both require the WinterWebSwooleApplication runner.


The KV store is a fast key-value store whose keys are grouped by a domain string. Any bean can use it by autowiring KvTemplate, and the SharedKvCache cache backend is built on it.

Add the following block to your application.yml. The store will not start unless port is a positive integer.

application.yml
winter:
kv:
port: 7880 # local TCP port the KV server listens on
address: # optional bind address; defaults to 127.0.0.1
token: # optional shared secret; a random one is generated per start

Once the store is enabled, the framework registers a KvTemplate bean automatically. Inject it with #[Autowired]:

src/Service/SessionCacheService.php
use dev\winterframework\io\kv\KvTemplate;
use dev\winterframework\stereotype\Autowired;
use dev\winterframework\stereotype\Service;
#[Service]
class SessionCacheService {
#[Autowired]
private KvTemplate $kvTemplate;
public function storeSession(string $userId, array $data): void {
$this->kvTemplate->put('sessions', $userId, $data, ttl: 3600);
}
public function loadSession(string $userId): mixed {
return $this->kvTemplate->get('sessions', $userId);
}
}
Method Signature Description
put put(string $domain, string $key, mixed $data, int $ttl = 0): bool Store a value. $ttl is in seconds; 0 means no expiry.
putIfNot putIfNot(string $domain, string $key, mixed $data, int $ttl = 0): bool Store only if the key does not already exist.
get get(string $domain, string $key): mixed Retrieve a value, or null if absent.
has has(string $domain, string $key): bool Check whether a key exists.
del del(string $domain, string $key): bool Delete a single key.
delAll delAll(string $domain): bool Delete every key in a domain.
incr incr(string $domain, string $key, int|float|null $incVal = null): int|float Atomically increment a numeric value.
decr decr(string $domain, string $key, int|float|null $decVal = null): int|float Atomically decrement a numeric value.
getSet getSet(string $domain, string $key, mixed $value): mixed Set a new value and return the old one.
getSetIfNot getSetIfNot(string $domain, string $key, mixed $data, int $ttl = 0): mixed Set a new value only if the key does not exist; returns the current value.
append append(string $domain, string $key, string $append): int Append a string to an existing value; returns the new length.
strLen strLen(string $domain, string $key): int Return the byte length of the stored string value.
keys keys(string $domain, string $key): array Return matching keys (supports glob patterns).
getAll getAll(string $domain): array Return all key→value pairs in a domain.
stats stats(): array Return store statistics (connections, memory, etc.).
ping ping(): int Health-check the store; returns latency in microseconds.

To use the KV store as a cache backend for #[Cacheable], register a CacheManager bean that wraps a SharedKvCache — see Caching.


The queue store is a lightweight FIFO queue, addressed by queue name. Use it through QueueSharedTemplate to pass messages between workers on the same host. (#[Async] does not use it — async calls have their own shared-memory queue.)

application.yml
winter:
queue:
port: 7881 # local TCP port the queue server listens on
address: # optional bind address; defaults to 127.0.0.1
token: # optional shared secret; a random one is generated per start
src/Service/NotificationDispatcher.php
use dev\winterframework\io\queue\QueueSharedTemplate;
use dev\winterframework\stereotype\Autowired;
use dev\winterframework\stereotype\Service;
#[Service]
class NotificationDispatcher {
#[Autowired]
private QueueSharedTemplate $queue;
public function dispatch(string $userId, string $message): void {
$this->queue->enqueue('notifications', [
'userId' => $userId,
'message' => $message,
]);
}
public function processNext(): mixed {
return $this->queue->dequeue('notifications');
}
}
Method Signature Description
enqueue enqueue(string $queue, mixed $data): bool Add an item to the tail of the named queue.
dequeue dequeue(string $queue): mixed Remove and return the item at the head of the queue, or null if empty.
size size(string $queue): int Return the number of items currently in the queue.
delete delete(string $queue): bool Remove the entire named queue.
stats stats(): array Return store statistics.
ping ping(): int Health-check the store; returns latency in microseconds.

A typical application.yml that enables both stores side-by-side:

application.yml
winter:
kv:
port: 7880
address: 127.0.0.1
queue:
port: 7881
address: 127.0.0.1

Both stores start as child processes before the HTTP workers, so they are ready when your beans initialize. You can verify connectivity by calling $kvTemplate->ping() or $queue->ping() inside an #[OnApplicationReady] listener.