LavinMQ::Clustering::Server
Inherits LavinMQ::Clustering::Replicator < LavinMQ::Clustering::FileIndex < Reference < Object
When a follower connects: It sends a static header (wrong header disconnects the client) It sends its password (servers closes the connection if the password is wrong) Server sends a list of files in its data directory and the sha1 hash of those files Client requests files that is missing or has mismatching checksums of In the meantime the server queues up changes (all publishes/consumes are paused) When client doesn't request more files starts to stream changes Server sends "appends", which include the file path and the bytes to be appended It also sends which files should be deleted or has been rewritten (such as json files) The follower sends back/acknowledges how many bytes it has received
Constants
Constructors
Instance methods
Replicate length bytes from path starting at pos. The bytes are
read from the registered MFile's mmap — zero syscalls. Callers must
have registered the path as an MFile first; for regular Files use the
append(path, bytes) overload instead.
Replicate an in-memory byte buffer. Caller owns the buffer. See
append_value for offset.
Replicate a small in-memory integer (e.g. Schema::VERSION, ack
position). The follower writes it directly to its LZ4 stream — no
heap allocation. offset is where the value is written on the leader,
used to skip bytes a just-joined follower already got via full_sync.
When caps is given (the last full_sync of a joining follower), each
file's hash is computed over only the first caps[path] bytes — the
cut the follower will be marked synced at. This excludes writes whose
replication dispatch hasn't happened yet (local write at
message_store.cr:339 races full_sync because it doesn't hold @lock), so
they are delivered incrementally instead of duplicated. Files absent
from caps are capped at 0 (delivered entirely via the stream).
Commit the current ISR to the coordinator, retrying until it succeeds. Called before any durable operation is acknowledged when a synced follower has disconnected — by each_follower after dispatching a replicated change, and by the Persister before sending publish confirms: the acknowledgment may only go out once the follower's removal from the ISR is durable, otherwise a leader crash right after the acknowledgment could elect that follower even though it lacks the acknowledged data. Operations must stall rather than be acknowledged against a stale ISR — if the coordinator stays unreachable the leader's lease eventually expires and the process exits.
True when the ISR last written to the coordinator may be stale (a follower connected or disconnected since). Checked by the Persister before sending publish confirms.
Replace an mmap-backed file's entire content on all followers — used after an in-place compaction (e.g. a stream's consumer_offsets file). Unlike replace_file(path) the MFile stays registered, so the zero-syscall append(path, pos, length) overload keeps working for later writes, and the bytes are read from the mmap capped at mfile.size (File.size would be the sparse ftruncate capacity, not the data length).
Block until every in-sync follower has acked everything replicated so far, so a durable operation may be acknowledged to a client: once this returns, every node etcd lists as a failover candidate has the operation durably on disk. Wait for all followers (no short-circuit) — wait_for_confirm blocks until the follower acks or disconnects. A follower that disconnected (wait_for_confirm == false, or it dropped earlier and left the ISR dirty) may lack data that's about to be acknowledged, so its removal must be committed to the coordinator before this returns (see flush_isr).
Block until every in-sync follower has acked everything replicated so far, then commit any pending ISR change. Called after a durable operation has been dispatched and locally fsynced, before it is acknowledged to a client (publish confirms via the Persister, definition changes via the DefinitionsStore).
Yields the file (or nil if missing) along with its real data size in
bytes. For MFile-backed sparse files the size comes from mfile.size,
not from File.size which would be the capacity. cap (see
files_with_hash) limits the yielded size for a capped full_sync.