Behaviours: gen_server.
Authors: Steve Roques.
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.
• 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.
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 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
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.
last_seen timestamp
is older than stale_timeout ms. The kvex index is rebuilt
from scratch after any eviction or vector change.
| add_peer/3 | Contact a remote em_pop node and register it as a known peer. |
| get_id/1 | Return this node's unique binary identifier (16 bytes). |
| get_peers/1 | Return all currently known peers as a list of plain maps. |
| get_trust/2 | Return the trust score for PeerId (0.0 if unknown, 1.0 if fully trusted). |
| get_vector/1 | Return the capability vector this node was started with. |
| gossip_tick/1 | Trigger one synchronous gossip tick. |
| handle_call/3 | |
| handle_cast/2 | |
| handle_gossip/2 | Handle an incoming gossip payload from a remote node. |
| peers_for/3 | Return the top-K peers ordered by cosine similarity to QueryVec. |
| start_link/1 | Start a Population Protocol node linked to the calling process. |
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(Pid::pid()) -> binary()
Return this node's unique binary identifier (16 bytes).
get_peers(Pid::pid()) -> [map()]
Return all currently known peers as a list of plain maps.
get_trust(Pid::pid(), PeerId::binary()) -> float()
Return the trust score for PeerId (0.0 if unknown, 1.0 if fully trusted).
get_vector(Pid::pid()) -> binary()
Return the capability vector this node was started with.
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(Msg, From, State) -> any()
handle_cast(Msg, State) -> any()
handle_gossip(Pid::pid(), Payload::map()) -> {ok, map()} | {error, term()}
Handle an incoming gossip payload from a remote node.
Called byem_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(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(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 200Generated by EDoc