Skip to main content

ReplicatedEnvironment

Struct ReplicatedEnvironment 

Source
pub struct ReplicatedEnvironment { /* private fields */ }
Expand description

A replicated database environment.

This is the entry point for replication. It wraps a standard Environment and adds replication capabilities including master election, replica streaming, and commit acknowledgments.

High Availability (HA) provides a replicated, embedded database management system which provides fast, reliable, and scalable data management. HA enables replication of an environment across a Replication Group. A ReplicatedEnvironment is a single node in the replication group.

ReplicatedEnvironment wraps a standard Environment. All database operations are executed in the same fashion in both replicated and non-replicated applications. A ReplicatedEnvironment must be transactional. All replicated databases created in the replicated environment must be transactional as well.

A ReplicatedEnvironment joins its replication group when it is created. When new() returns, the node will have established contact with the other members of the group and will be ready to service operations.

Replicated environments can be created with node type Electable or Secondary. Electable nodes can be masters or replicas, and participate in both master elections and commit durability decisions. Secondary nodes can only be replicas, not masters, and do not participate in either elections or durability decisions.

§Example

use noxu_rep::{ReplicatedEnvironment, RepConfig};

let config = RepConfig::builder("my_group", "node1", "localhost")
    .node_port(5001)
    .build();
let rep_env = ReplicatedEnvironment::new(config).unwrap();

Implementations§

Source§

impl ReplicatedEnvironment

Source

pub fn new(config: RepConfig) -> Result<Self>

Create a new replicated environment.

Creates a replicated environment handle and starts participating in the replication group. The node’s state is determined when it joins the group, and mastership is not preconfigured. If the group has no current master, creation will trigger an election to determine whether this node will participate as a Master or a Replica.

A brand new node will always join an existing group as a Replica, unless it is the very first electable node that is creating the group. In that case it joins as the Master of the newly formed singleton group.

Source

pub fn open(config: RepConfig) -> Result<Arc<Self>>

Open a replicated environment with the standard production lifecycle.

This is the entry point recommended by the mdBook chapters: it allocates the ReplicatedEnvironment, registers all services on the TCP dispatcher, and spawns the election driver background thread that runs Paxos rounds against known peers until the node has resolved into either Master or Replica state.

Closes finding F6 of docs/src/internal/api-audit-2026-05-rep.md.

Use ReplicatedEnvironment::new directly only when the caller plans to drive state transitions explicitly (test harnesses, scripted bootstrap, recovery tooling).

Source

pub fn init_self_weak(self: &Arc<Self>)

Populate the env’s self-referential Weak so background threads can obtain a back-reference for auto-orchestrated follow-up actions (e.g. replica auto-bootstrap on NeedsRestore). Idempotent: subsequent calls are silent no-ops because the inner OnceLock only accepts one set.

Callers that wrap the env in Arc and want auto-bootstrap behaviour should call this immediately after construction. Self::open already does so. Test harnesses that drive transitions manually (RepTestBase) also call this so the auto-bootstrap path is exercised in tests.

Source

pub fn register_admin_service(self: &Arc<Self>)

Register the ADMIN service handler on the TCP dispatcher.

Closes findings F7 / F8. Holds a Weak<Self> so the handler does not extend the env’s lifetime. Idempotent: re-registering is harmless because TcpServiceDispatcher::register overwrites the existing handler.

Source

pub fn start_vlsn_persistence_daemon(self: &Arc<Self>)

Spawn the VLSN-index persistence daemon (F11).

Periodically (every 2 seconds) snapshots the in-memory VlsnIndex to <env_home>/vlsn.idx so a clean restart can resume from where the replica left off without a full network restore. No-op when config.env_home is None.

Idempotent: only one daemon is ever spawned per env.

Source

pub fn start_election_driver(self: &Arc<Self>)

Spawn the election driver background thread.

While the env is in Detached or Unknown state and no master is known, the driver periodically attempts a Paxos election against peers in GroupService (whose ELECTION services were registered at open() time). On success the driver calls become_master (if this node is the winner) or become_replica (otherwise). On failure (no quorum), the driver waits config.election_timeout and tries again.

The driver respects io_shutdown; on env close the loop exits promptly.

Idempotent: a second call is a no-op (only one driver thread is ever spawned per env).

Source

pub fn bound_addr(&self) -> Option<SocketAddr>

Return the socket address the TCP service dispatcher is bound to.

This may differ from the configured node_port when port 0 is used (the OS assigns an ephemeral port). Returns None if the dispatcher could not be started (e.g. the address is not resolvable).

Source

pub fn with_environment(&self, env: Arc<EnvironmentImpl>)

Wire a live EnvironmentImpl into this replicated environment.

After this call, state transitions (become_master, become_replica) will spawn real feeder/receiver I/O threads backed by the live log.

If the RESTORE service was not registered at construction time (because config.env_home was None), it is registered here using the environment’s actual home path. This mirrorsRepNode.envSetup() which registers the restore handler during environment wiring.

Environment reference wiring. EnvironmentImpl via RepImpl.repNode.envImpl in HA.

Source

pub fn get_state(&self) -> NodeState

Get the current node state.

Returns the current state of the node associated with this replication environment. If the caller’s intent is to track the state of the node, StateChangeListener may be a more convenient and efficient approach.

Source

pub fn is_master(&self) -> bool

Check if this node is the master.

Returns true if the node’s current state is Master.

Source

pub fn is_replica(&self) -> bool

Check if this node is a replica.

Returns true if the node’s current state is Replica.

Source

pub fn is_active(&self) -> bool

Returns true if the node is currently participating in the group as a Replica or a Master.

Source

pub fn get_node_name(&self) -> &str

Get the node name.

Returns the unique name used to identify this replicated environment.

Source

pub fn get_group_name(&self) -> &str

Get the group name.

Returns the name of the replication group this node belongs to.

Source

pub fn get_master_name(&self) -> Option<String>

Get the current master (if known).

Returns the name of the node that is currently the master, or None if the master is not known (e.g. the node is in the Unknown or Detached state).

Source

pub fn get_group(&self) -> &GroupService

Get the replication group info.

Returns a description of the replication group as known by this node. The replicated group metadata is stored in a replicated database and updates are propagated by the current master node to all replicas. If this node is not the master, it is possible for its description of the group to be out of date.

Source

pub fn add_peer(&self, node: RepNode) -> Result<()>

Add a peer node to the replication group at runtime.

The node is registered in the GroupService so elections and quorum calculations immediately reflect the new membership.

Source

pub fn remove_peer(&self, name: &str) -> Result<()>

Remove a peer node from the replication group by name.

The node is deregistered from the GroupService. Elections initiated after this call will not include the removed node in quorum calculations.

Source

pub fn update_peer_metadata(&self, name: &str, node: RepNode) -> Result<()>

Update the capacity and latency metadata of an existing peer.

Only the following fields are updated from node:

  • read_capacity_pct
  • write_capacity_pct
  • latency_hint_ms

The node’s identity (name, address, port, node_type) is preserved. Safe to call while replication is active.

If the quorum policy is Flexible or Expression, the quorum system is rebuilt to reflect the new capacity/latency weights.

§Note

update_peer_metadata does not currently re-run QuorumPolicy::validate(electable_count) after the metadata change. An LP-optimal Expression quorum that was safe before the update may no longer satisfy the intersection property afterwards. Until automatic revalidation lands, deployments using QuorumPolicy::Expression should call quorum_policy().validate(get_rep_group().electable_count()) on the returned RepGroup after every metadata change and fail the operator-facing operation if validation reports unsafety.

Source

pub fn get_rep_group(&self) -> RepGroup

Returns a snapshot of the current replication group as a RepGroup.

The snapshot reflects the state at the time of the call; subsequent add_peer / remove_peer calls are not reflected in it.

Source

pub fn get_config(&self) -> &RepConfig

Get the replication configuration.

Returns the replication configuration that has been used to create this environment.

Source

pub fn get_vlsn_range(&self) -> VlsnRange

Get the current VLSN range on this node.

Returns the range of VLSNs currently available on this node.

Source

pub fn get_current_vlsn(&self) -> u64

Get the latest VLSN.

Returns the most recent VLSN registered on this node.

Source

pub fn feeder_replica_names(&self) -> Vec<String>

Return the list of replica names that currently have a Feeder tracker on this (master) node.

Used by tests and operator tooling. The returned list reflects the master’s view at the time of the call; subsequent add_peer/remove_peer calls may change it.

Source

pub fn bootstrap_via_dispatcher(&self, peer_name: &str) -> Result<()>

Bootstrap this node’s environment by network-restoring all .ndb files from peer_name via the dispatcher’s RESTORE service.

Closes findings F2 / F4 of docs/src/internal/api-audit-2026-05-rep.md.

The standalone NetworkRestore::execute() opens raw TCP and expects to drive the legacy NetworkRestoreServer::start listener. Production replicated environments host the RESTORE handler on the dispatcher, so this method routes through execute_via_dispatcher.

peer_name must be a known peer in GroupService; on success the peer’s .ndb files are written into config.env_home. Returns Err if env_home is None, the peer is unknown, or the restore fails for any reason.

Source

pub fn get_stats(&self) -> &RepStats

Get replication statistics.

Returns statistics associated with this environment.

Source

pub fn get_ack_tracker(&self) -> &AckTracker

Get the ack tracker.

Source

pub fn ensure_unknown_state(&self) -> Result<()>

Ensure the node state machine is in Unknown state, transitioning from Detached if necessary. This is needed because the state machine only allows Detached -> Unknown -> Master/Replica.

Source

pub fn become_master(&self, term: u64) -> Result<()>

Transition to master state.

Transitions this node to Master state for the given election term. As master, the node can accept write operations and feed log entries to replicas.

If a live EnvironmentImpl has been wired in via with_environment, a FeederRunner + EnvironmentLogScanner background thread is spawned for each currently-registered replica (feeder entries in feeders).

In HA.

Source

pub fn become_replica(&self, master_name: &str) -> Result<()>

Transition to replica state with the given master.

Transitions this node to Replica state. The node will receive log entries from the specified master.

If a live EnvironmentImpl has been wired in via with_environment, the method prepares an EnvironmentLogWriter so that replicated entries can be written to the local log. The actual network connection is established by the TcpServiceDispatcher; this method logs intent.

In HA.

Source

pub fn transfer_master(&self, config: MasterTransferConfig) -> Result<()>

Initiate a master transfer to the target node.

Transfers the current master state from this node to one of the electable replicas. The replica that is actually chosen to be the new master is the one with which the Master Transfer can be completed most rapidly. The transfer operation ensures that all changes at this node are available at the new master upon conclusion of the operation.

Source

pub fn register_vlsn(&self, vlsn: u64, file_number: u32, file_offset: u32)

Register a VLSN (as master, after writing a log entry).

Maps the given VLSN to the specified log file position. This is called by the master after it writes a replicated log entry.

Source

pub fn replicate_entry( &self, vlsn: u64, file_number: u32, file_offset: u32, entry_type: u8, data: Vec<u8>, )

Replicate a freshly committed log entry from the master.

Closes finding F9 of docs/src/internal/api-audit-2026-05-rep.md.

Combines register_vlsn with a push into the in-memory peer_scanner so that downstream replicas pulling from this node’s PEER_FEEDER service (via catch_up_from_peer) can stream the entry without round-tripping through the on-disk log. The local log is still the source of truth; the peer scanner is a fast-path cache that bounds itself via PeerLogScanner::with_capacity so old entries are evicted.

Should be called by the master after the local commit has fsynced. Calling on a non-master is harmless (the peer scanner cache is also used by replicas) but is logged at trace level for diagnostics.

Source

pub fn apply_entry( &self, vlsn: u64, entry_type: u8, data: Vec<u8>, ) -> Result<()>

Apply a replicated entry (as replica).

Applies a log entry received from the master. This is called by the replica stream handler after receiving an entry from the feeder.

data is the wire-encoded log-record payload. When the replicated environment has not been wired to a local noxu_db::Environment (i.e., before with_environment is called) the payload is forwarded into the in-memory peer scanner so that downstream replicas attached to the PEER_FEEDER service can re-stream it; the local log is not updated. This is documented behaviour rather than a stub — see api-audit-2026-05-rep.md finding #26 (medium) for the with_environment-required local-apply path. cleanup (rep info F35: _data placeholder) renames the leading underscore so reviewers don’t read it as a TODO.

Source

pub fn record_ack(&self, vlsn: u64, replica_name: &str)

Record an ack from a replica (as master).

Records that the specified replica has acknowledged processing up to the given VLSN. This is used by the master to track durability guarantees.

Source

pub fn set_state_change_listener(&self, listener: Arc<dyn StateChangeListener>)

Set the state change listener.

Sets the listener used to receive asynchronous replication node state change events. Note that there is one listener per replication node, not one per handle. Invoking this method adds to the set of listeners.

Invoking this method typically results in an immediate callback to the application via the on_state_change method, so that the application is made aware of the existing state of the node at the time the listener is first established.

Source

pub fn close(&self) -> Result<()>

Close the replicated environment.

Closes this handle and releases any resources. When closed, daemon threads are stopped, even if they are performing work. The node ceases participation in the replication group. If the node was currently the master, the rest of the group will hold an election.

The ReplicatedEnvironment should not be closed while any other type of handle that refers to it is not yet closed.

Source

pub fn shutdown_group(&self, replica_shutdown_timeout_ms: u64) -> Result<()>

Close this handle and shut down the Replication Group by forcing all active Replicas to exit.

This method must be invoked on the node that’s currently the Master after all other outstanding handles have been closed. The Master waits for all active Replicas to catch up so that they have a current set of logs, and then shuts them down.

Source

pub fn is_shutdown(&self) -> bool

Check if shutdown is in progress.

Trait Implementations§

Source§

impl ReplicaAckCoordinator for ReplicatedEnvironment

Source§

fn alloc_vlsn_for_recovered_commit(&self, lsn: Lsn) -> u64

X-3: allocate the next VLSN for a recovered XA commit and register lsn in the VLSN index so feeders can stream the commit.

Increments off the current latest VLSN so the new VLSN is strictly monotonically increasing. In a single-node or master-less environment (not master) returns 0 (NULL_VLSN — harmless, the default).

Source§

fn await_replica_acks( &self, policy: ReplicaAckPolicyKind, timeout: Duration, ) -> Result<u32, AckWaitError>

Block until at least policy.required_acks(electable_count) replicas have acknowledged the most-recent local commit, or until timeout elapses, whichever comes first. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,