From: Hannes Laimer <h.laimer@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [PATCH cluster 00/12] pmxcfs: add a change notification socket
Date: Thu, 22 Oct 2026 16:21:53 +0200 [thread overview]
Message-ID: <20261022142205.930536-1-h.laimer@proxmox.com> (raw)
pmxcfs gets a Unix stream socket at /run/pve-cluster/pmxcfs.sock. It
pushes one JSON line per change to /etc/pve to every client whose path
template matches the changed path. A client subscribes with named
templates such as nodes/{node}/qemu-server/{vmid}.conf. An event
carries the type of the change, the path, the rename target and the
values the placeholders captured. So a client reads the one file that
changed. Today a daemon that cares about /etc/pve compares the version
vector on every loop pass and re-reads what usually has not changed. A
connection admitted by its group id never sees private paths, as on
the IPC and FUSE side.
The socket is served by a Rust thread linked into the C daemon as a
static library with a small C ABI. The daemon never waits on a client.
A client that falls too far behind gets a resync event in place of
what it missed, and one that stopped reading is cut off. A node that
takes its whole state from the cluster sends a resync as well. Nothing
is kept across connections, so every subscription starts with a resync
and the client reads everything once. A panic in the Rust code never
takes pmxcfs down, at worst it switches the notifier off.
On top of the socket, pve-cluster ships three Perl modules. A client
handles the connection and its reconnects. A hook registry takes hooks
registered like API methods, with a path template whose placeholders
become the parameters of a run. And a runner executes those runs in
children. It keeps one run per hook and parameter set in flight and
collapses later events for the same set into a single rerun. The
series ships no hook and nothing that runs the runner.
Example, hook registration:
```
package PVE::Example::Hooks;
use base qw(PVE::Cluster::Hooks);
__PACKAGE__->register_hook({
name => 'guest-config',
path => 'nodes/{node}/qemu-server/{vmid}.conf',
code => sub {
my ($param, $event) = @_;
if ($event->{type} eq 'resync') {
# reread every guest config
return;
}
# reread guest $param->{vmid} on $param->{node}
},
});
1;
```
Example, in a child of the daemon:
```
use PVE::Example::Hooks;
use PVE::Cluster::HookRunner;
my $pid = fork() // die "fork failed: $!\n";
if (!$pid) {
my $ok = eval { PVE::Cluster::HookRunner->new()->run(); 1 };
warn "hook runner failed: $@" if !$ok;
POSIX::_exit($ok ? 0 : 1);
}
```
Example, in the daemon's own loop:
```
use PVE::Example::Hooks;
use PVE::Cluster;
use PVE::Cluster::Hooks;
use PVE::Cluster::Watch;
my $watch = PVE::Cluster::Watch->new();
$watch->subscribe(PVE::Cluster::Hooks->subscription()->%*);
# handle events for ten seconds. Events that arrive while the daemon
# does other work wait in the socket until the next pass. If the
# socket is full for 30s the server drops the connection, the next
# wait reconnects and starts with a resync.
my $deadline = time() + 10;
while ((my $left = $deadline - time()) > 0) {
my @events = $watch->wait($left);
# so hooks reading through the cluster cache see the change
PVE::Cluster::cfs_update() if @events;
for my $event (@events) {
for my $run (PVE::Cluster::Hooks->dispatch($event)->@*) {
$run->{hook}->{code}->($run->{param}, $run->{event});
}
}
}
```
Changes since the RFC, thanks @Wolfgang!
* resuming is gone, every subscribe is answered with a resync, so
there are no sequence numbers, no ring and no saved positions
* the server runs on tokio with one task per connection and one
bounded channel
* the protocol is parsed by serde alone
* the ffi takes the event enum directly and lost the version argument,
the memdb hook lost it as well
* the build goes through cargo's dependency file with the build mode
from d/rules
* the runner moved to pve-cluster and the pve-manager part is gone.
The series adds the machinery only, the examples above show how to
use it
pve-cluster:
Hannes Laimer (12):
buildsys: add rust workspace under src/rust
rust: notify: add path templates with placeholder capture
rust: notify: add the wire protocol
rust: notify: add subscription matching
rust: notify: add the socket server
rust: ffi: add C ABI staticlib for pmxcfs
pmxcfs: memdb: add change notification hook
buildsys: link pmxcfs against the rust notify staticlib
pmxcfs: notify: emit change events over the notification socket
cfs: add perl client for the change notification socket
cfs: add hook registry for change notification consumers
cfs: add runner executing cluster change hooks in children
.gitignore | 2 +
Makefile | 1 +
debian/control | 22 +-
debian/pve-cluster.install | 3 +
debian/rules | 17 +
src/Makefile | 2 +-
src/PVE/Cluster/HookRunner.pm | 268 ++++++++++
src/PVE/Cluster/Hooks.pm | 135 +++++
src/PVE/Cluster/Makefile | 2 +-
src/PVE/Cluster/Watch.pm | 325 ++++++++++++
src/defines.mk | 11 +
src/pmxcfs/Makefile | 20 +-
src/pmxcfs/cfs-utils.h | 4 +
src/pmxcfs/database.c | 2 +
src/pmxcfs/memdb.c | 22 +
src/pmxcfs/memdb.h | 8 +
src/pmxcfs/pmxcfs.c | 24 +
src/rust/.cargo/config.toml | 8 +
src/rust/Cargo.toml | 24 +
src/rust/Makefile | 20 +
src/rust/pmxcfs-ffi/Cargo.toml | 17 +
src/rust/pmxcfs-ffi/include/pmxcfs-notify.h | 24 +
src/rust/pmxcfs-ffi/src/lib.rs | 306 +++++++++++
src/rust/pmxcfs-notify/Cargo.toml | 17 +
src/rust/pmxcfs-notify/src/conn.rs | 334 ++++++++++++
src/rust/pmxcfs-notify/src/lib.rs | 32 ++
src/rust/pmxcfs-notify/src/protocol.rs | 189 +++++++
src/rust/pmxcfs-notify/src/server.rs | 542 ++++++++++++++++++++
src/rust/pmxcfs-notify/src/template.rs | 285 ++++++++++
src/rust/rustfmt.toml | 2 +
src/test/Makefile | 14 +-
src/test/hook_runner_test.pl | 302 +++++++++++
src/test/hooks_test.pl | 219 ++++++++
src/test/watch_client_test.pl | 229 +++++++++
34 files changed, 3426 insertions(+), 6 deletions(-)
create mode 100644 src/PVE/Cluster/HookRunner.pm
create mode 100644 src/PVE/Cluster/Hooks.pm
create mode 100644 src/PVE/Cluster/Watch.pm
create mode 100644 src/defines.mk
create mode 100644 src/rust/.cargo/config.toml
create mode 100644 src/rust/Cargo.toml
create mode 100644 src/rust/Makefile
create mode 100644 src/rust/pmxcfs-ffi/Cargo.toml
create mode 100644 src/rust/pmxcfs-ffi/include/pmxcfs-notify.h
create mode 100644 src/rust/pmxcfs-ffi/src/lib.rs
create mode 100644 src/rust/pmxcfs-notify/Cargo.toml
create mode 100644 src/rust/pmxcfs-notify/src/conn.rs
create mode 100644 src/rust/pmxcfs-notify/src/lib.rs
create mode 100644 src/rust/pmxcfs-notify/src/protocol.rs
create mode 100644 src/rust/pmxcfs-notify/src/server.rs
create mode 100644 src/rust/pmxcfs-notify/src/template.rs
create mode 100644 src/rust/rustfmt.toml
create mode 100644 src/test/hook_runner_test.pl
create mode 100644 src/test/hooks_test.pl
create mode 100644 src/test/watch_client_test.pl
Summary over all repositories:
34 files changed, 3426 insertions(+), 6 deletions(-)
--
Generated by murpp 0.12.0
next reply other threads:[~2026-10-22 14:22 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-22 14:21 Hannes Laimer [this message]
2026-10-22 14:21 ` [PATCH pve-cluster 01/12] buildsys: add rust workspace under src/rust Hannes Laimer
2026-10-22 14:21 ` [PATCH pve-cluster 02/12] rust: notify: add path templates with placeholder capture Hannes Laimer
2026-10-22 14:21 ` [PATCH pve-cluster 03/12] rust: notify: add the wire protocol Hannes Laimer
2026-10-22 14:21 ` [PATCH pve-cluster 04/12] rust: notify: add subscription matching Hannes Laimer
2026-10-22 14:21 ` [PATCH pve-cluster 05/12] rust: notify: add the socket server Hannes Laimer
2026-10-22 14:21 ` [PATCH pve-cluster 06/12] rust: ffi: add C ABI staticlib for pmxcfs Hannes Laimer
2026-10-22 14:22 ` [PATCH pve-cluster 07/12] pmxcfs: memdb: add change notification hook Hannes Laimer
2026-10-22 14:22 ` [PATCH pve-cluster 08/12] buildsys: link pmxcfs against the rust notify staticlib Hannes Laimer
2026-10-22 14:22 ` [PATCH pve-cluster 09/12] pmxcfs: notify: emit change events over the notification socket Hannes Laimer
2026-10-22 14:22 ` [PATCH pve-cluster 10/12] cfs: add perl client for the change " Hannes Laimer
2026-10-22 14:22 ` [PATCH pve-cluster 11/12] cfs: add hook registry for change notification consumers Hannes Laimer
2026-10-22 14:22 ` [PATCH pve-cluster 12/12] cfs: add runner executing cluster change hooks in children Hannes Laimer
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261022142205.930536-1-h.laimer@proxmox.com \
--to=h.laimer@proxmox.com \
--cc=pve-devel@lists.proxmox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.