class

LavinMQ::Clustering::Follower

Inherits Reference < Object

Constants

ACK_TIMEOUT = 3.seconds

Max time a connected follower may go without acking outstanding data before it's disconnected from the replica set. Mirrors the @socket.write_timeout used for the flush path so a slow but still-connected follower can't stall publish confirms indefinitely.

Log = LavinMQ::Log.for("clustering.follower")
SYNC_WRITE_TIMEOUT = 60.seconds

Write timeout used during full_sync. A syncing follower isn't in the ISR yet, so its slowness can't stall publish confirms, and the bulk transfer legitimately blocks the leader's writes while the follower persists the files it receives. The aggressive ACK_TIMEOUT is for the steady-state streaming phase; using it during full_sync wrongly drops a merely-slow follower. Still bounded so a genuinely wedged follower can't hold the sync lock forever.

Constructors

new(socket : TCPSocket, data_dir : String, file_index : FileIndex)
Source

Instance methods

ack_loop(ack_timeout : Time::Span = ACK_TIMEOUT)
Source
acked_bytes
Source
already_synced(path : String, offset : Int64, length : Int64) : Int64

How many leading bytes of the append [offset, offset + length) this follower already received via full_sync (0 = none, length = all). The baseline cut is a live file size, so it may land inside a record that a writer had written locally but not yet dispatched when the cut was taken; such a straddling append is healed by the caller sending only the tail the snapshot didn't cover (the follower appends raw bytes, it doesn't care about record boundaries).

Appends to a given path arrive at monotonically increasing offsets, so the first one ending at or past the cut means the follower has caught up with that file and the entry can be dropped. When the last entry goes the whole baseline is reassigned to a fresh empty hash, releasing the capacity a large join held and turning later checks into a plain miss.

Source
append(path : String, bytes : Bytes) : Int64
Source
append(path : String, value : UInt32 | Int32) : Int64
Source
capture_synced_baseline(sizes : Hash(String, Int64)) : Nil

Record, at the moment of becoming synced, how many bytes of each file full_sync just delivered. Keyed by the same (data-dir-relative) path the replication appends use.

Source
close
Source
dead?

True once ack_loop has ended (follower disconnected or timed out) or the follower was closed without ack_loop ever running. Server#update_isr excludes dead followers: a publish-confirm waiter unblocked by the @ack_notify close can flush an ISR without this follower even though it hasn't been removed from @followers yet.

Source
delete(path) : Int64
Source
forget_baseline(path : String) : Nil
Source
full_sync(caps : Hash(String, Int64) | Nil = nil) : Nil

caps (last sync of a joining follower) limits each file to the byte count recorded as that follower's synced baseline, so the snapshot and the baseline agree and in-flight writes aren't duplicated.

Source
lag_in_bytes
Source
mark_synced!
Source
negotiate!(password) : Nil
Source
protocol_version
Source
remote_address
Source
replace(path : String, bytes : Bytes) : Int64

Replace a file's whole content from an in-memory slice (the leader's mmap, read capped at mfile.size). A positive length on the wire marks a replace; the follower streams it to <path>.tmp then renames atomically. An empty slice writes a 0 length, which the follower applies as a delete (an emptied file carries no data to restore anyway).

Source
replace(path) : Int64
Source
request_flush

Ask flush_loop to push buffered bytes to the follower. Never blocks and never touches the socket, so it's safe to call from any execution context (see flush_loop).

Source
request_fsync(paths : Enumerable(String)) : Nil

Ask the follower to fsync paths (relative to the data dir) before it acks past this point in the stream. The bytes are counted as sent right away, so a later wait_for_confirm covers them, and they're written before anything sent after them (see #write_pending_fsyncs). Only buffers, never touches the socket, so it's safe to call from the isolated publish confirm loop. Version 1 followers sync before every ack and don't understand fsync requests.

Source
request_syncfs

Ask the follower to sync its whole data dir, an fsync request without a path

Source
sent_bytes
Source
state
Source
synced?
Source
syncing?
Source
to_json(json : JSON::Builder)
Source
wait_for_confirm

Block until the follower has acked at least the bytes already sent at call time. Requests a flush first so the pending bytes reach the follower without waiting for the ack_loop's 100ms flush timeout. Returns true if the bytes were acked, false if the follower disconnected. A follower that stops acking is disconnected by ack_loop, which closes @ack_notify and unblocks us here.

Source

Nested types