Repository navigation
perfcached 0.6.0
A multithreaded, self-clustering cache for Linux, with its own encrypted protocols and a Redis-compatible door. New here? Start with the README.
This page covers 0.6.0. 0.5.7.1, 0.5.8 and 0.5.9 have pages of their own without packages: coming from 0.5.6.6, the last release with packages, read those too. The ChangeLog has the detail.
A rolling restart. From 0.4.5, 0.6.0 joins a fleet one node at a time; use SET IFEQ/IFNE and DELEX once every node runs 0.5.0 or later. A config that uses [mail] (0.5.9) is refused by an older release: to roll back, put the old config back with the old binary.
io_uring by default
- The workers receive into provided buffers, with one multishot receive per connection, and send their replies with the turn's single
io_uring_enter(): noread()and nowrite()a request. - At start the daemon tries io_uring for real and falls back to epoll where the kernel (before 6.0),
kernel.io_uring_disabledor a container's seccomp profile refuses it. The reason is logged and shown in/stats(io.backend), andperfcached -Csays so beforehand.[daemon] io = epollforces epoll. - On 16-vCPU VMs with multiqueue NICs, against epoll: 41% more RESP and 63% more native requests a second one at a time, 12% more pipelined, and a third less CPU a request.
- On epoll, a short read ends a wakeup's reads: one
read()a request instead of two. - PRODUCTION.md section 7: one NIC queue per CPU on the nodes and the clients, irqbalance, and the daemon unpinned.
A fast writer with a slow reader is throttled, not closed
- At 4 MB of replies unread, a client's further requests wait and its socket is not read, until the backlog drains below 1 MB. One that reads nothing for
output_stall_s(default 30) meanwhile is closed as a slow consumer. - The log says
throttling client <ip:port>the first time, and the disconnect line counts the throttles./stats(clients.output) and/metricscount them. A slow consumer's close now says so; it used to read "peer closed".
Busy polling, opt-in
busy_poll_usandbusy_poll_prefer, per worker (kernel 6.9+), off by default. Its effect on perfcached is not measured yet: measure it on your own NIC before you keep it.
Stats
/stats ioand/metricscount the system calls against the work they moved: waits, reads and writes, io_uring enters, completions, receives and sends, and UDP datagrams a call.perfcached_io_worker_ops_totalhas each worker's I/O.
Packages
Debian 12 and 13, Ubuntu 20.04 and 24.04, amd64:
perfcached: the clustered daemon, every cluster mode.perfcached-standalone: the single-node edition, built from the same tree without the cluster code. A[cluster]section in its config is refused rather than ignored.libperfd0: the client library,libperfd.so.0.libperfd-dev: its header, static archive,libperfd.solink andlibperfd.pc. A program built against it links the shared library by default.
The two daemon packages install the same files and replace each other. They do not need libperfd0: install it, or libperfd-dev, for your own programs.
sudo apt install ./perfcached_0.6.0-1+deb13_amd64.deb
sudo apt install ./libperfd0_0.6.0-1+deb13_amd64.deb ./libperfd-dev_0.6.0-1+deb13_amd64.deb # to build against libperfd
Each distribution's packages were built on that distribution: Debian 13 (gcc 14.2), Debian 12 (gcc 12.2, in a container), Ubuntu 20.04 (gcc 9.4) and Ubuntu 24.04 (gcc 13.3). Every build ran noisetest, the selftest and a deliberately broken-lock build that must fail. Each set was then checked in a clean container of its distribution:
- the version and revision
perfcached -Vreports, and the systemd unit; - the daemon starting and answering ping, set and get over its encrypted native door from
perfcli, on epoll under the container's default seccomp profile and on io_uring without it; - a program built against
libperfd-devthrough pkg-config, linkinglibperfd.so.0; - the shipped config passing
perfcached -C, a[cluster]section refused by the standalone edition, and the two editions replacing each other; - an upgrade over the published 0.5.6.6
perfcachedpackage.
Alpine 3.24 (musl), x86_64, both editions, as tarballs of the tree's make install layout: binaries in usr/bin, libperfd (shared and static) with perfd.h and libperfd.pc, the example config, and the docs. No systemd unit or OpenRC service is shipped; run the daemon under your own supervisor.
apk add libsodium libcurl
tar -xzf perfcached-0.6.0-alpine3.24-x86_64.tar.gz
cp -a perfcached-0.6.0-alpine3.24-x86_64/usr perfcached-0.6.0-alpine3.24-x86_64/etc /
Built with gcc 15.2 in a clean Alpine container behind the same three gates, then checked in a second clean container: -V and the musl interpreter, the round trip on epoll and on io_uring, the libperfd program, the shipped config and the [cluster] refusal.
libperfd for Alpine: libperfd and libperfd-dev as .apk, signed with the perfcached release key. Trust the key once (its public half is attached here and in contrib/alpine/keys), then install:
cp yury@kfi.net.au-6aca2bb6.rsa.pub /etc/apk/keys/
apk add ./libperfd-0.6.0-r0.apk ./libperfd-dev-0.6.0-r0.apk
Checked in a clean container that trusts only that key: both install, pkg-config reports 0.6.0, a program links the shared or the static library and pings a 0.6.0 daemon, and without the key apk refuses them.
SHA256SUMS covers every file on this page.
Known
- In a container under Docker's or podman's default seccomp profile, io_uring is refused and the daemon runs on epoll.
perfcached -Cand the log say which. - Busy polling's effect is not measured yet.
- No lists, sets, sorted sets or streams yet. Keys hold strings, counters, JSON documents or hashes (a hash is one value, under 58,000 bytes).
discovery = unicast(clouds, Kubernetes) is tested in lab network namespaces that drop IP fragments and block UDP, not yet in a real cloud deployment.spreadmode has not yet carried production traffic.- In
eagermode a delete made on one side of a network partition can be undone when the partition heals.