Module em_pop_node

em_pop_node — Population Protocol node (gen_server).

Behaviours: gen_server.

Authors: Steve Roques.

Description

em_pop_node — Population Protocol node (gen_server)

Implements the Angluin et al. (2004) Population Protocol model as a concrete Erlang process with HTTP-based pairwise interactions.

What this process holds

• A unique 16-byte random binary ID (generated at startup). • A semantic capability vector — an f32 flat binary produced by em_filter_vec:from_capabilities/1. • A peer map: #{PeerId => #peer{}} for every known node. • A kvex SIMD index for O(log N) cosine-similarity search.

Population Protocol transition (gossip_tick/1)

1. Pick a random peer from the known peer map. 2. Serialise our own state to a JSON-encodable map (payload). 3. HTTP POST /pop/gossip to the peer — this is the PP "interaction". 4. Receive the peer's payload in the HTTP response body. 5. Merge the peer's peer list into our own (transitive discovery).

After O(N log N) interactions, every node in a connected graph converges to knowing every other node — without any coordinator.

Trust model

Trust is a float in [0.0, 1.0] per peer. • First contact → TRUST_INIT (0.10) • Successful exchange → +TRUST_INCREMENT (0.10), capped at 1.0 • Failed exchange → -TRUST_DECAY (0.05), floored at 0.0

Background gossip

A timer fires every gossip_interval ms. The HTTP call is executed in a spawned process so the gen_server never blocks. Results arrive as {gossip_result, PeerId, Result} messages.

Stale peer eviction

Every background tick evicts peers whose last_seen timestamp is older than stale_timeout ms. The kvex index is rebuilt from scratch after any eviction or vector change.

Function Index

add_peer/3Contact a remote em_pop node and register it as a known peer.
get_id/1Return this node's unique binary identifier (16 bytes).
get_peers/1Return all currently known peers as a list of plain maps.
get_trust/2Return the trust score for PeerId (0.0 if unknown, 1.0 if fully trusted).
get_vector/1Return the capability vector this node was started with.
gossip_tick/1Trigger one synchronous gossip tick.
handle_call/3
handle_cast/2
handle_gossip/2Handle an incoming gossip payload from a remote node.
peers_for/3Return the top-K peers ordered by cosine similarity to QueryVec.
start_link/1Start a Population Protocol node linked to the calling process.

Function Details

add_peer/3

add_peer(Pid::pid(), Host::string(), Port::inet:port_number()) -> ok | {error, term()}

Contact a remote em_pop node and register it as a known peer.

Performs a full bidirectional gossip exchange: sends our state, receives theirs, and merges their peer list into ours.

Timeout is 15 s because the HTTP call may take up to GOSSIP_HTTP_TIMEOUT (5 s) plus gen_server scheduling overhead.

get_id/1

get_id(Pid::pid()) -> binary()

Return this node's unique binary identifier (16 bytes).

get_peers/1

get_peers(Pid::pid()) -> [map()]

Return all currently known peers as a list of plain maps.

get_trust/2

get_trust(Pid::pid(), PeerId::binary()) -> float()

Return the trust score for PeerId (0.0 if unknown, 1.0 if fully trusted).

get_vector/1

get_vector(Pid::pid()) -> binary()

Return the capability vector this node was started with.

gossip_tick/1

gossip_tick(Pid::pid()) -> ok

Trigger one synchronous gossip tick.

Picks a random peer, performs the HTTP exchange, and merges the result — all in the calling process's context. The gen_server is blocked for the duration of the HTTP call (up to GOSSIP_HTTP_TIMEOUT).

Intended for tests and manual invocation. The background timer uses an async pattern instead to keep the gen_server responsive.

handle_call/3

handle_call(Msg, From, State) -> any()

handle_cast/2

handle_cast(Msg, State) -> any()

handle_gossip/2

handle_gossip(Pid::pid(), Payload::map()) -> {ok, map()} | {error, term()}

Handle an incoming gossip payload from a remote node.

Called by em_pop_http when a POST /pop/gossip arrives. Merges the remote node into our peer table and returns our own current state as the reply payload.

peers_for/3

peers_for(Pid::pid(), Vec::binary(), K::pos_integer()) -> [{map(), float()}]

Return the top-K peers ordered by cosine similarity to QueryVec.

QueryVec must be an f32 little-endian binary of the same dimension as the vectors stored in this node's kvex index.

Returns a list of {PeerMap, Score} tuples sorted by descending similarity score. Returns [] when the peer list is empty.

start_link/1

start_link(Opts::map()) -> {ok, pid()} | {error, term()}

Start a Population Protocol node linked to the calling process.

Opts is a map with the following keys:

port => pos_integer() — required; TCP port for gossip HTTP vector => binary() — required; f32 capability vector stale_timeout => pos_integer() — optional; default 30 000 ms gossip_interval => non_neg_integer() — optional; default 5 000 ms max_peers => pos_integer() — optional; default 200


Generated by EDoc