CHANGELOG
View Sourcev6.0.0
Breaking changes
- Minimum supported OTP version is now 27 (previously 24). The build now hard-fails on OTP < 27 (
rebar.config.script). - Removed
partisan_gen_fsm(previously deprecated and incomplete). Code based ongen_fsmis no longer supported — migrate topartisan_gen_statem. - Monitor inter-node protocol changed.
DOWNsignals are now delivered directly to the monitoring process on the monitor's bound channel (FIFO-ordered with user traffic), and a new inter-node cast{gc_proc_mon_out, _}is used for monitor GC.- Rolling upgrade: in a mixed v5/v6 cluster a v6 node may send a v5 monitor server a message it does not understand (leaving a stale bookkeeping entry); prefer a full cluster upgrade over long-lived mixed operation. See "Process & Peer Monitoring".
Changes
OTP compatibility
- Replaced the static OTP module forks in
priv/otp/24/with a compile-time AST transformation system that generates the partisan OTP modules from the installed OTP source.- New modules:
partisan_gen_transform,partisan_otp_rewrite,partisan_otp_patches— a pipeline that extracts abstract code from the installed OTP, applies mechanical AST rewrites (module renames, BIF replacements) and version-adaptive structural patches, and compiles the result. - Generated at compile time via
priv/generate_otp_sources.escript(a rebar3 pre-compile hook), with a runtime fallback inpartisan_app:start/2(ensure_otp_modules/0) for checkout dependencies; startup fails with{partisan_otp_modules_missing, _}if generation did not run. - 7 modules generated:
partisan_gen,partisan_proc_lib,partisan_sys,partisan_gen_server,partisan_gen_event,partisan_gen_statem,partisan_gen_supervisor. - Version-adaptive: patches adjust to the OTP version (e.g. OTP 28 supervisor replies include
hibernate_after_action/1, OTP 27 does not), eliminating the "OTP N+1 broke our forks" bug class.
- New modules:
- Supported and tested on OTP 27, 28 and 29 (CI matrix
27.3,28.3,29.0).- OTP 29 introduces supervisor hibernation, in which a leading
handle_call/3clause wakes a hibernating supervisor before the call is dispatched.partisan_gen_supervisorsupplieshandle_call/3in full and so emits that clause; without it a hibernating supervisor would not wake. The build stops on any OTP major whose supervisor internals have not been checked against these replacements (partisan_otp_patches:assert_reviewed_otp_version/1).
- OTP 29 introduces supervisor hibernation, in which a leading
- Added
partisan_otp_test_gen(generator for the OTP-compatibility test suites).- The OTP test suites it adapts are fetched for the OTP version in use (
test/fetch_otp_test_sources.sh) and cached underotp_src/, which version control ignores. An OTP release ships module sources but not its own test suites, so these are obtained separately; fetching them per version means a new OTP release needs no new copy in the repository. A build without network access can pre-seedotp_src/otp_<version>/test/, after which the fetch does nothing.
- The OTP test suites it adapts are fetched for the OTP version in use (
- Added installation / build documentation (
doc_extras/installation.md).
Process & Peer Monitoring
- Monitors are now bound to a channel for their lifetime; the eventual
DOWNis delivered on that channel, in order with other traffic on it (matches disterl's "messages before DOWN" guarantee). Added per-channel down detection: a single channel dropping while the node stays up firesDOWN/noconnectionfor monitors on that channel only.- Ordering caveat: the "messages sent by the dying process before its
DOWN" ordering holds only when the bound channel hasparallelism = 1. On a parallel channel Partisan dispatches across sockets by partition key, so neither user traffic nor theDOWNhas a per-channel total order — bind latency-sensitive monitors to aparallelism = 1channel if you rely on this.
- Ordering caveat: the "messages sent by the dying process before its
- Monitor failure reason on a transport-level timeout is now reported as
noconnection(nodedown-style) instead oftimeout.
disterl-hybrid routing
- When
connect_disterl = true,partisan:monitor/3and message forwarding now use nativeerlang:monitor/erlang:sendfor peers reachable over Erlang distribution (narrowed to atom/pid targets with noack/causal_labeloption, so interposition, ack and causal paths still use the partisan transport). Default is unchanged (connect_disterl = false).
Broadcast — per-group epidemic broadcast (ADR-000001)
The epidemic-broadcast substrate is no longer a single shared process. Each broadcast handler now runs in its own supervised broadcast group — its own process, mailbox, spanning tree and outstanding-lazy table — so independent gossip streams (e.g. an application's
plum_dbbroadcasts) no longer share a tree or a mailbox with Partisan's own membership heartbeat or with each other. Groups are declared bybroadcast_mods/ the newbroadcast_groupsconfig and can be created or retired at runtime.- New modules:
partisan_broadcast(public API —broadcast/2,start_group/1,stop_group/1,groups/0),partisan_broadcast_group_sup(the group supervisor), andpartisan_membership(the membership snapshot, below).partisan_plumtree_broadcastis now started once per group (start_link/2), one instance per handler module; group identity is the handler module.
- New modules:
Off-path handler apply (non-blocking). The
partisan_plumtree_broadcast_handlerbehaviour gains two optional callbacks —claim/2(a fast, atomic novelty check run on the tree process) andhandle_broadcast/2(the heavy apply, run off the tree process in the handler's own process). A handler that implements them no longer blocks the broadcast tree — or any other handler — while it merges/persists a payload. Handlers implementing onlymerge/2keep working unchanged (the synchronous path).partisan_plumtree_backendis migrated to the new contract as the reference implementation.Membership via a lock-free snapshot. The peer service manager now publishes membership to a public, lock-free ETS snapshot (
partisan_membership) that broadcast groups read directly, rather than each subscribing topartisan_peer_service_events(whose synchronousgen_eventfan-out would block the oracle and not scale to many groups).partisan_peer_service:broadcast_members/0is now answered from this snapshot.partisan_peer_service_eventsis unchanged and still available for external subscribers.- Rolling upgrade: the default (Partisan heartbeat) group keeps the legacy registered name
partisan_plumtree_broadcast, so the control plane keeps converging in a mixed old/new cluster; a compatibility shim forwards legacy-addressed messages for application handlers to their group. Application gossip old→new is delivered via the shim; new→old is best-effort during the upgrade window and heals via anti-entropy. Prefer a full cluster upgrade over long-lived mixed operation. - The tree engine is a per-group pluggable behaviour (
partisan_broadcast_engine): a group delegates tree construction/repair to an engine and owns everything else. Plumtree is engine #1 (partisan_plumtree_engine, the logic extracted from the group process into an I/O-free, action-returning engine); the group shell is unchanged in behaviour. A second engine, Thicket (partisan_thicket_engine), ships experimental and off by default: it embeds multiple interior-node-disjoint trees to spread forwarding load across nodes, is opt-in per group viaengine => partisan_thicket_engine, and stays gated on a measured interior-node-load imbalance — the default Plumtree path is byte-for-byte unchanged. Supporting a self-describing engine like Thicket added an optional raw-dispatch path to the behaviour (handle_message/2+repair_tick/1, selected by an engine'sdispatch_mode/0); the typed Plumtree callbacks are unchanged (ADR-000002, ADR-000004).
- Rolling upgrade: the default (Partisan heartbeat) group keeps the legacy registered name
A broadcast group may declare its own channel. Group specs accept
channel, which wins over the handler'sbroadcast_channel/0callback; omitting it keeps the callback's answer, so the change is additive. This is the only way to put a handler you do not own on a dedicated channel — the channel was previously a property of the module and of nothing else. Introspect withpartisan_plumtree_broadcast:group_channel/1.- The channel is deliberately a property of a group, not of an individual
broadcast/2call: repair traffic follows the tree, so a per-message channel would serve a grafted retransmission on the group's channel and put the full payload on the channel it was meant to stay off, exactly when the network is stressed enough to need repair. A handler that needs two channels should run in two groups. Documented under "Groups and channels" in the migration guide.
- The channel is deliberately a property of a group, not of an individual
Membership eventing (ADR-000003)
- Retired the
partisan_peer_service_eventsgen_eventbus. Every peer-service manager (pluggable, hyparview, static, client_server) now publishes membership changes to the lock-freepartisan_membershipsnapshot and delivers a non-blocking asynchronous{partisan_membership, Members}message to subscribers — replacing a synchronousgen_event:sync_notifythat blocked the manager on every subscriber's callback and ran all callbacks serially in one process.- Fixes a gap introduced with the membership snapshot: the snapshot was previously written only by the default (pluggable) manager, so broadcast groups under the hyparview/static/client_server managers observed empty membership. All managers now feed it (regression-guarded by an assertion in the hyparview
partisan_SUITEcases). - Membership API: observe changes via
partisan_membership:subscribe/0(handle{partisan_membership, Members}in your own process) or by pollingpartisan_membership:members/0/version/0. - Deprecated:
partisan_peer_service:add_sup_callback/1is now a compatibility shim over the push feed — the callback runs asynchronously in its own caller-linked process and no longer blocks the membership path. Prefer the API above.partisan_peer_service_events(with itsadd_handler/add_sup_handler/add_callbackfunctions) is removed.
- Fixes a gap introduced with the membership snapshot: the snapshot was previously written only by the default (pluggable) manager, so broadcast groups under the hyparview/static/client_server managers observed empty membership. All managers now feed it (regression-guarded by an assertion in the hyparview
RPC — partisan_erpc becomes the primary surface (ADR-000006)
callno longer runs on thepartisan_rpc_backendserver. Every inbound RPC used to be applied inline in that onegen_server'shandle_info/2, so a single slow, blocking or hungM:F(A)stalled every unrelated RPC behind it in the mailbox, a crashing RPC was a supervision event, and replies carried no request id — a late reply from a timed-out call could be consumed by an unrelated later call in the same process. RPC is now an explicit correlated request/response: each request carries its own reference and is executed by a worker process spawned per request, which replies directly to the caller.- The correlation reference is a process alias (
erlang:alias([explicit_unalias])), so abandoning a request deactivates its reply address and the runtime drops a late reply instead of leaving it in the caller's mailbox. - The failure-detection monitor deliberately carries only a reason, never a return value: in Partisan every remote
DOWNis relayed through onepartisan_monitorprocess per node, so using it as a value channel would funnel every RPC result on a node through it.
- The correlation reference is a process alias (
partisan_erpcworks over the Partisan transport. It was a vendored OTP 23/24-eraerpcsnapshot that still reached peers with the auto-importedspawn_request/5BIF and received results as a distributed monitor's exit reason — both distribution mechanisms — so it did not function as a Partisan surface at all, and no test had ever loaded it. It is now the primary RPC API; prefer it for new code.-compile({no_auto_import, [spawn_request/5, spawn_request_abandon/1]})makes any remaining distribution call a compile error rather than a silent disterl fallback.- Added the OTP 25+ request-identifier collection API the snapshot predated —
send_request/6,receive_response/3,wait_response/3,check_response/3,reqids_new/0,reqids_size/1,reqids_add/3,reqids_to_list/1— plus upstream'ssend_request/4fun+label+collection clause. Idiomatic modern fan-out code previously failed withundef. An export-parity test now asserts the module stays a superset oferpc.
- Added the OTP 25+ request-identifier collection API the snapshot predated —
partisan_rpcis re-based as a thin shim overpartisan_erpc, mirroring how OTP has implementedrpcovererpcsince OTP 23, including a verbatim copy of?RPCIFY/rpcify_exception/2. It is documented as the legacy surface but is not deprecated (OTP has not deprecatedrpc, and existing code depends on therpc => partisan_rpcrewrite).- Closes a live
undefbug:cast/4,multicall/3,4,5,async_call/4+yield/1,nb_yield/1,2andblock_call/4,5are reachable through therpcrewrite but several did not exist. They do now.
- Closes a live
partisan_rpc_backendis kept permanently as the counterpart of OTP'srex, scoped to the operations whose semantics require a server —block_call/4,5,sbcast/2,3,abcast/2,3,eval_everywhere/3,4— exactly the split OTP still uses.block_callcontinues to apply inline by design: executing on that server, serialised with otherblock_calls, is what distinguishes it fromcall.- Inbound RPC concurrency is bounded by the new
rpc_max_concurrency(default10000,infinitydisables). Each request runs in its own process, so without a bound a peer could spawn without limit; OTP tolerates the unbounded form only because the distribution buffer backpressures, which Partisan has no equivalent of. Over the cap a request is rejected rather than queued (a queue with no credit scheme is an unbounded mailbox with extra steps), surfacing aserror({partisan_erpc, overloaded})/{badrpc, {'EXIT', overloaded}}. New telemetry event[partisan, rpc, overload]. - Per-call transport options on every RPC surface, so RPC can be put on a channel of its own.
call/5andmulticall/5acceptforward_opts()in place of a bare timeout;partisan_erpc:send_request/5,7,cast/5,multicast/5andpartisan_rpc:async_call/5,cast/5are new arities.- Per-call options now win over the global
forward_options. The precedence was inverted (partisan_config:get(forward_options, CallerOpts)returns the configured value whenever one is set), so a per-callchannelorpartition_keywas silently discarded as soon as anything set the global.
- Per-call options now win over the global
- Rolling upgrade: a v6 node still serves the v5
{call, ...}framing for the whole 6.x series, so v5 caller → v6 node is unchanged; the reverse is not — a v5 node has no clause for the correlated request and discards it, so v6 caller → v5 node times out. Upgrade every node before relying on RPC between them. The v5 receiver is removed in 7.0.0, and the concurrency bound now covers it too.
Backpressure and the forwarding contract
partisan:forward_message/2,3,4and the threepartisan_peer_service_managerforward_messagecallbacks are no longer specced-> ok. They never were: forwarding returns{error, disconnected | not_yet_connected | notalive}— or{error, partitioned}under the hyparview manager — when it cannot hand a message to a connection. The contract is nowpartisan_peer_service_manager:forward_result/0andpartisan:send/3's spec is widened to match.- Runtime behaviour of
forward_messageis unchanged, so nothing breaks on upgrade — but code written against the old spec drops messages silently. Check the return value where delivery matters.
- Runtime behaviour of
partisan:send/2no longer crashes when the destination is unreachable. It wasok = send(Dest, Msg, []), which badmatched on{error, disconnected}— a crash in the caller for a function whose Erlang counterpart never fails that way (erlang:send/2to a dead process or unreachable node simply returns). It now followserlang:send/2: best-effort, returnsMsgregardless. Usesend/3when the outcome matters. The mismatch was invisible whileforward_message/3was mis-specced-> ok.- The RPC workers used to die on an undeliverable reply for the same reason (
ok = partisan:forward_message(...)looked total); a reply that cannot be delivered is now logged and dropped, since the caller detects the loss through its own monitor.
- The RPC workers used to die on an undeliverable reply for the same reason (
- New
connection_high_watermark(defaultinfinity, opt-in). Dispatch is agen_server:cast/2into an unbounded mailbox, so a sender faster than its socket grew that mailbox without limit. Past the mark a send is refused with{error, overloaded}and not queued.partisan_peer_connections:cast_encoded/3is now the single admission point for outbound data — all previous dispatch sites route through it. New telemetry event[partisan, connection, overload].monotonicchannels are exempt: their existing strategy is to drop a superseded message when the connection has backlog, which is correct for traffic where only the freshest value matters, and applying the mark would turn deliberate silent drops into errors those senders have never handled.- The default preserves v5 behaviour exactly; turning an unbounded queue into a refusing one changes what callers observe, so it is opt-in.
Development & tooling
- Removed eqWAlizer from CI and the build: the
EqwalizeGitHub workflow, themake eqwalizer/eqwalize-alltargets, and theeqwalizer_support/eqwalizer_rebar3injection inrebar.config.script. Dialyzer remains the static-analysis gate (make dialyzer/make check). All in-source-eqwalizer(...)attributes and%% eqwalizer:ignorecomments have been stripped (they were inert without the checker). - Split CI by resource footprint: the light suites (
compile,eunit,otp-compat-test,otp-test) run on GitHub runners (build_and_test.yml), while the heavy multi-node cluster suites (partisan_SUITE,partisan_alt_SUITE, PropEr) run on a large ephemeral Fly.io machine — they exceed a GitHub runner's memory. Newmake ci-light/make ci-heavyaggregate targets and atest/fly/runner. - Fixed the dialyzer PLT configuration: the old
{dialyzer_base_plt_apps, ...}key is not a valid rebar3 option and was silently ignored, socompiler,ssl,public_keyandinetswere absent from the PLT. Replaced with a proper{dialyzer, [{base_plt_apps, [...]}]}, clearing ~100 spurious "unknown function" warnings. - Adopted
erlfmtfor source formatting (rebar3 plugin + config). - Restored static analysis over the connection processes.
#state.ping_trefinpartisan_peer_service_client/_serverwas typed as an encoded remote reference, but both writers store a local timerreference()(erlang:start_timer/3, directly or viapartisan_retry:fire/1) and it is passed toerlang:cancel_timer/1;#state.ping_idle_timeoutwas typednon_neg_integer()despite both modules having an explicit#state{ping_idle_timeout = undefined}clause for when pings are disabled. Runtime behaviour was correct throughout — the annotations were not — but the two wrong types produced 13 cascading warnings including "no local return" onsend_ping/1andacceptor_continue/3, so dialyzer was effectively not analysing the ping or accept paths at all. Both modules are now warning-free (project total 42 → 27). - Exported
exchange/0,exchanges/0andselector/0frompartisan_plumtree_broadcast:partisan_peer_service:exchanges/0,1andcancel_exchanges/1— public API — carried specs referring to types that were not visible outside the defining module. - Removed two unreachable private clauses in
partisan_interval_sets(the bare-integer forms ofunsafe_element_intersection/2anddo_element_subtract/2): both are only reached after their callers have normalised the arguments to intervals. The integer forms of the element operations reachable from the public API are unaffected. - Removed three dead macros from the public
partisan.hrl(?PLUMTREE_OUTSTANDING,?GOSSIP_FANOUT,?GOSSIP_GC_MIN_SIZE) and corrected the annotation on?FANOUT, which was marked "not used?" but is the default for thefanoutconfiguration option. - Added a benchmark harness (
bench/,make bench): a driver with warmup, repetitions and per-operation latency percentiles, plus scenarios for the point-to-point, acknowledged, RPC and broadcast-fan-out paths, and recorded baselines inbench/BASELINE.md. It is not part ofmake testor CI — a machine-dependent number cannot be a pass/fail condition — but it does fail a scenario whose own repetitions disagree by more than 25%, since such a run cannot resolve a change smaller than the disagreement. - Test suite: disabled OTP 25+
globalprevent_overlapping_partitionson the disterl-based CT control plane (partisan_support). On OTP 27 it disconnected peer nodes mid-test as HyParView churned connections, making the HyParView cases flaky (global … requested disconnect … to prevent overlapping partitions). Partisan itself runsconnect_disterl = false, so production is unaffected.
Security
- Bounded inbound peer message frames: a new
max_message_sizeconfig option (default 64 MB) sets{packet_size, _}on the{packet, 4}framing of both the connect (partisan_peer_service_client) and accept (partisan_acceptor_socket) paths, so an oversized frame is rejected before it is assembled or decoded — closing a pre-authentication memory-exhaustion / decompression-bomb vector on the peer plane.- Upgrade / behavioural change: previously
{packet, 4}accepted frames up to ~4 GB; frames larger thanmax_message_sizeare now rejected (the receiving socket reportsemsgsizeand closes, which drops the peer from the active view until it reconnects). If your application legitimately sends peer messages larger than 64 MB (e.g. very largeplum_dbbroadcasts / AAE deltas), raisemax_message_sizeaccordingly.
- Upgrade / behavioural change: previously
- Bounded the server-side TLS handshake:
partisan_peer_socket:accept/1now passes a timeout (newtls_handshake_timeoutoption, default 5000 ms) tossl:handshake/3, so a peer that completes the TCP connection but stalls the TLS handshake can no longer pin an acceptor indefinitely. - Startup security-posture logging (
partisan_app:start/2): a?LOG_WARNINGwhen cluster TLS is enabled but peers are not verified (verify_peermissing → encrypted but MITM-able), and a?LOG_NOTICEwhen the peer plane is plaintext/unauthenticated (tls = false), so an insecure peer-plane configuration is surfaced at boot rather than silent. The diagnostic is best-effort and never affects application start. - Documentation: replaced the
verify_noneTLS examples (which modelled an unauthenticated, MITM-able configuration) withverify_peermTLS, documented the newmax_message_size/tls_handshake_timeoutoptions, and added a "Securing the cluster peer plane" deployment guide (doc_extras/cluster_security.md).
Fixes
HyParView could leave a permanent one-sided active-view link. Active-view links are symmetric by definition (Leitao et al., DSN'07, §4.1: "if node q is in the active view of node p then node p is also in the active view of node q"), but two handlers guarded the add with
partisan_peer_connections:is_connected/1and, when it was false, returned unchanged without answering the peer at all:- the
neighborhandler, which also receives the periodic symmetry re-assertions. A node absent from our active view has no connection kept open for it, so this was not a transient race but the steady state — every re-assertion hit the silent branch, and the asserting peer was never told to drop us. Observed as a stable asymmetry that survived 600 consecutive checks over 60 seconds. - the
neighbor_requesthandler, which sent neitherneighbor_acceptednorneighbor_rejected, leaving the initiator's promotion hanging. §4.3 requires the initiator be told so it can try another peer from its passive view.
Both now answer: a node that cannot hold a peer sends a DISCONNECT (or a rejection) so the peer drops the one-sided link. This fixed
partisan_SUITE:hyparview_manager_high_client_test, which had been failing on constrained hardware since well before this release.- the
Hardened
get_next_id/3inpartisan_hyparview_peer_service_manageragainst an epoch mismatch, which was acase_clausethat would have taken the manager down. Unreachable today —init/1startssent_message_mapempty and the epoch only advances across a restart — so this is defensive, not a fix.partisan_interval_sets:del_element/2raised{badarg, List}instead of removing the element, whenever the element partially overlapped a stored interval and the set held a further interval after it.element_subtract/2returns a list of the parts of the element the stored interval did not cover, and each still has to be removed from the rest of the set; the list was passed as a single element instead, andvalidate_element/1rejected it. Removing{5,25}from[{0,10},{20,30}]crashed where it should return[{0,4},{26,30}]. Now folded over the remainder. The existing test cases never combined a partial overlap with a later interval, so none of them reached it.Resolves a crash on OTP 28 caused by the supervisor returning new
{timeout, T, Msg}/hibernate_afteraction tuples the frozenpartisan_gen_serverdid not understand.Interposition: fixed a pterm key mismatch (
{partisan_peer_service_server, peer}written butpeer_noderead) that caused the originNodepassed to interposition functions on inbound-forwarded messages to always beundefined.Fixed
send_requestin the generatedpartisan_gencode to use{alias, demonitor}so[alias | Mref]replies route correctly.Fixed
partisan_interval_sets:from_list/1: it now validates every element (including single-element lists) and sorts with a correct total order (compare_lex) before compaction. The previous implementation validated only the elements itsusortcomparator happened to touch — so a single-element list was never validated — and could drop distinct intervals sharing a start bound.disterl-hybrid routing correctness (
connect_disterl = true). Three fixes to the opt-in native-transport fast path (defaultconnect_disterl = falsewas unaffected): (1) a remote pid ref is no longer converted to a native pid vialist_to_pid/1— Partisan stores pids in node-localized"<0.X.Y>"form, so that produced a local pid and misdelivered cross-node forwards/casts/replies;remote_ref_to_disterl/1now only reconstructs genuinely-local pids and otherwise falls back to the partisan transport; (2)forward_message/3's remote-ref path now applies the same guard as/4— it no longer short-circuits toerlang:sendwhenack/causal_labelare set (those need the partisan path) and only disterl-sends to a peer that is actually disterl-reachable (erlang:nodes()); (3)monitor/3selects nativeerlang:monitoronly when the peer is actually disterl-reachable, avoiding a spurious immediatenoconnectionDOWN for a process still reachable over the partisan overlay.TLS handshake failures no longer crash the acceptor.
partisan_peer_socket:accept/1matched{ok, _} = ssl:handshake(...)strictly, so a failed or timed-out server-side handshake raisedbadmatchand emitted a crash report per connection — a log-flood / acceptor-pool-exhaustion vector (slowloris, or a misconfigured peer). It now closes the socket and terminates the acceptor normally with a debug log; the pool replaces it.HyParView active-view symmetry repair. HyParView requires a symmetric active view — if node A holds peer B, then B must hold A. A control message lost during churn (a
NEIGHBORracing a not-yet-established reverse connection, or an undeliveredDISCONNECT) could strand a stable one-sided view that the still-open connection never repaired, occasionally failing the high-fanout convergence cases (hyparview_manager_high_client_test/high_active_test— a ~20% flake on OTP 28, worse under load). Added periodic active-view maintenance topartisan_hyparview_peer_service_manager: each node re-asserts its membership to its active peers using the ordinaryNEIGHBORmessage, so a peer that is missing us re-adds us and one that already has us ignores it. Uses no new wire message (safe for peers on older releases) and is a no-op once the view is symmetric. Cadence defaults torandom_promotion_interval, overridable via theactive_view_maintenance_intervalapplication env. Measured: 8/10 → 10/10 passes forhigh_client_testlocally.- Setting
active_view_maintenance_intervalnow takes effect. The key was not registered withpartisan_config, which reads only the application-environment keys it knows, so the cadence always followedrandom_promotion_interval. Leaving the key unset still selects that cadence.
- Setting
Additions
- New export
partisan:remote_ref_to_disterl/1— helper for disterl-hybrid deployments (connect_disterl = true).
v5.0.3
Fixes
- Fixed implementation of
partisan_peer_service_clientandpartisan_peer_service_serverping implementation that would close a connection when receiving and invalid ping message. Also added latency calculation and publich two telemetry events[partisan, connection, client, heartbeat]and[partisan, connection, server, hearbeat]
v5.0.0
Changes
- Drop
rctag and graduate to v5.0.0! - Added
connection_pingconfiguration option to prevent staleness during TCP half-open connections and other netorking issues. The same configuration works both for the client and server sides of the connection.
v5.0.0-rc.17
Changes
- Drop support for OTP24
- Added missing export
to_reference/1inpartisan_remote_ref
Fixes
- Fixes plumtree calling the local node
- Update to
partisan_interval_setutil module
v5.0.0-rc.16
Fixes
- Fixes a bug introduced in previous commit in the return of the
graftcallback.
v5.0.0-rc.15
Fixes
- Allow
okas result forpartisan_plumtree_broadcast:exchange/1callback.
v5.0.0-rc.14
Changes
- Add
okas valid return forexchangecallback inpartisan_plumtree_broadcast_handler.
v5.0.0-rc.14
Fixes
- Fixes the case where
partisan_plumbtree_broadcastbehaviour implementors' callbacks throw an exception which would crash the broadcast server. - Replace use of RPC in
partisan_plumbtree_broadcastand usepartisan_gen_server:call/3instead - Other minor fixes
v5.0.0-rc.13
Fixes
- set
distance_enabledoptions tofalseby default.
v5.0.0-rc.12
Fixes
- Fix a bug causing fast forward to be disabled in full-mesh topologies
- Merged PR #254 - Thanks Massimo Cesaro!
v5.0.0-rc.11
Fixes
- Fix a bug when dealing with deprecated configs
v5.0.0-rc.10
Changes
partisan_peer_discovery_dnsconfiguration changes. Added support for IPV6 viaaaaarecord_type and additionaloptions.{partisan, [ {peer_discovery, [ {type, partisan_peer_discovery_dns}, {config, #{ record_type => aaaa, query => "foo.local", node_basename => "foo", options => #{ nameservers => ["fdaa::3"] } }} ]} ]}
v5.0.0-rc.9
Changes
partisan_peer_discovery_dnsconfiguration changes. The configuration parametersnamewas renamed toqueryandnodenamewas renamed tonode_basename.nameandnodenameare still valid inputs but they are transformed during init.{partisan, [ {peer_discovery, [ {type, partisan_peer_discovery_dns}, {config, #{ record_type => fqdns, query => "foo.local", node_basename => "foo" }} ]} ]}- New implementation of plumtree heartbeats in
partisan_plumtree_backendto bound the timestamps stored by each peer. This is done using the new modulepartisan_invertal_sets. The module also offers new performance improvements by avoiding calling the server when possible (using ets directly instead).
v5.0.0-rc.8
Bug Fixes
- Fixes #250
peer_hostnot working. Thepeer_hostwas an experimental option that was never rally implemented and thus has been deprecated and the original feature has been now implemented using thelisten_addrsfeature and the new host resolution algorithm
Changes
listen_addrsis now the preferred way to configure the IP/Ports where Partisan will listen for connections. The new implementation allows for multiple different formats and coerces them to thepartisan:listen_addr()type i.e.#{ip => inet:ip_address(), port => 1..65535}. The following example shows the different formats accepted by the option.{listen_addrs, [ "127.0.0.1:12345", <<"127.0.0.1:12345">>, {"127.0.0.1", "12345"}, {{127, 0, 0, 1}, 12345}, #{ip => "127.0.0.1", port => "12345"}, #{ip => <<"127.0.0.1">>, port => <<"12345">>}, #{ip => {127, 0, 0, 1}, port => 12345} ]},- A new algorithm has been implemented to determine the listen address when
listen_addris not defined in the configuration. The algorithm usespeer_ipthe Erlang nodename ornameconfiguration option to extract the host from the name e.g.HOSTinmynode@HOSTand usesinet:getaddrto determine the IP Address.
v5.0.0-rc.7
Changes
- Performance improvements for
partisan:forward/2,3,4.
v5.0.0-rc.2
Bug Fixes
- Fixes a bug in
partisan:spawn/2
v5.0.0-rc.1
Bug Fixes
- Make sure a message forward to a local process never fails (restoring the original behaviour).
- Minor bug fixes
- Fixed type issues detected by Eqwalizer and Dialyzer
Changes
- Readme Docs improvements
v5.0.0-beta.24
- Removed eqwalizer from default profile
v5.0.0-beta.23
Bug Fixes
- Coerce
forward_optionsconfiguration option to map format. - Fix bug in merge of forward options on
partisan_pluggable_peer_servicemodule - Test suite fixes
- Export missing
partisan:monitor_node/3function. - Fix a bug in
partisan_hyparview_peer_service_messagewhen Options are passed as list.
Changes
- Remove unused module
partisan_promise_backend
v5.0.0-beta.22
Bug Fixes
- Continued adding support for OTP.
- The OTP modules
sys,proc_libwhere patched (partisan_sys,partisan_proc_lib) so that they support thepartisan_remote_ref:t()type and use thepartisanmodule functions for finding, monitoring and sending messages instead of the native Erlang counterparts. - OTP patched files are located in the priv directory and loaded dynamically by
rebar.config.scriptbased on the Erlang/OTP version being used. - Patched the CT suites (
gen_server_SUITE,gen_statem_SUITE,gen_event_SUITE) to test the partisan OTP modules. All tests passing except for some test cases that require not-yet implemented features like global and somerpcfunctions. - Notice
globalis not yet supported by Partisan.
- The OTP modules
- Added support for Eqwalizer, and passed both Eqwalizer and Dialyzer checks
Additions
- New improper list format for
partisan_remote_ref. This deprecates the config optionremote_ref_as_uriand addsremote_ref_formatinstead which acceptsimproper_list(the new default),tuple(the legacy format) anduri(also introduced in v5). - Adds
partisan_erpc. The patched version of the Erlang'serpcmodule.
v5.0.0-beta.19
Bug Fixes
- Fix implementation of
partisan_pluggable_peer_service_manager:sync_join/1.
v5.0.0-beta.18
Bug Fixes
- Remove optimisation from
partisan:self/0and addpartisan:self/1which accepts thecacheoption making the use of th optimization to be explicit. Check the docs for the explanation. - Fixed bug in
partisan:monitor/2introduced in previous version.
v5.0.0-beta.17
Bug Fixes
- Fix bugs in
partisan_gen_statemandpartisan_gen
v5.0.0-beta.16
Bug Fixes
- Fix a bug in
partisan:send/2,3
Changes
- Ensure the membership channel (
partisan_membership) exits and is properly configured.
v5.0.0-beta.15
Bug Fixes
- General bug fixes including:
- #121 updated_members should only accept a list of maps (an never a list of nodes)
- fix wrong calls to
self()andnode()as opposed to their partisan counterparts
- Fixed bugs in
partisan_monitor - Several bug fixes in the OTP implementation
- Several bug fixes in the CT suite
Changes
- Changed signature of partisan_membership_strategy and the implementing modules; added API e.g.
join(state(), partisan:node_spec(), state())is nowjoin(partisan:node_spec(), state(), state())which is more natural. - Added partisan_membership_strategy API functions, so that pluggable manager can call these functions
- Some other naming changes to disambiguate e.g. membership -> members
- moved some opt types from partisan_monitor to partisan module
- Fixed missing of gen_ and partisan_gen function calls.
- Made
channeloptions to be respected across the stack- Added channel configuration to
partisan_monitorcalls. - Added channel to OTP behaviours.
- The messages and the monitor signals will be sent using the configured channel.
- overloaded gen_server/statem functions to accept options including channel so that we do not add another function to the API
- store the Partisan opts in the process dict (again to avoid modifying our changed versions of the behaviours) *
- Added channel configuration to
- Configuration parameters renaming. Several configuration parameters were renamed. Check
partisan_configmodule description. The old parameters are still accepted but are renamed during startup. - Deprecated the
partisan_peer_service_manager:myselfcallback - Fix
partisan_utilterm encoding and renamed function; added compression option for encoding and for memberhip payload
Additions
- Added the following modules:
partisan_supervisorbehaviour
- Added the following functions:
- Peer Service manager now allows subscribing to events per channel
partisan_peer_service_manager:on_up/3accepting a channelpartisan_peer_service_manager:on_down/3accepting a channel
v5.0.0-beta.14
API
Changes
- Several functions previously found in
partisan_utilare now inpartisan_peer_service_manager.
- Types previously found in
partisan.hrlare now defined and exported by thepartisanmodule.
Peer Membership
Fixes
- Several bug fixes in the following backends:
partisan_hyparview_peer_service_managerpartisan_xbot_hyparview_peer_service_managerpartisan_client_server_peer_service_manager
- Fixes a bug in
partisan_plumbtree_broadcastwhere not all the handlers were used.- The configuration option
broadcast_start_exchange_limitis now considered to refer to each handler i.e. a limit of1means Partisan will only allow one instance of a broadcast AAE exchange per handler (and not a single one in total).
- The configuration option
Peer Connection Management
Changes
- Channel parallelism can now be defined per channel
channelsconfiguration option is overloaded to allow the new configuration options while keeping backwards compatibility. Check the documentation for the new formats in partisan_config.- The
partisan:node_spec()representation was changed:parallelismwas removedchannelswas changed from a list of atoms or tuples to a the return ofpartisan_config:get(channels)i.e. a map.
parallelismis now used as a default when the user doesn’t define a per channel parallelism.- The
partisanmodule now exports the new functionchannel_opts/1with returns the options for a given channel.
v5.0.0-beta.13
API
In general, the API was redesigned to concentrate all functions around two modules: partisan and partisan_peer_service.
Changes
partisanmodule was repurposed as a replacement for theerlangmodule for use cases related to distribution e.g.erlang:nodes/0->partisan:nodes/0.- Several functions previously found in
partisan_peer_service,partisan_monitorandpartisan_utilare now in this module:partisan:broadcast/2partisan:cast_message/2partisan:cast_message/3partisan:cast_message/4partisan:default_channel/0partisan:demonitor/1partisan:demonitor/2partisan:disconnect_node/1.partisan:forward_message/2partisan:forward_message/3partisan:forward_message/4partisan:is_alive/0partisan:is_connected/1partisan:is_connected/2partisan:is_fully_connected/1partisan:is_local/1partisan:is_pid/1partisan:is_process_alive/1partisan:is_reference/1partisan:make_ref/0partisan:monitor/1partisan:monitor/2partisan:monitor/3partisan:monitor_node/2partisan:monitor_nodes/1partisan:monitor_nodes/2partisan:node/0partisan:node/1partisan:node_spec/0partisan:node_spec/1partisan:node_spec/2partisan:nodes/0partisan:nodes/1partisan:nodestring/0partisan:self/0
- Several functions previously found in
Added the following functions:
partisan_peer_service:broadcast_members/0partisan_peer_service:broadcast_members/1partisan_peer_service:cancel_exchanges/1partisan_peer_service:exchanges/0partisan_peer_service:exchanges/1partisan_peer_service:get_local_state/0partisan_peer_service:inject_partition/2partisan_peer_service:leave/1partisan_peer_service:member/1partisan_peer_service:members_for_orchestration/0partisan_peer_service:on_down/2partisan_peer_service:on_up/2partisan_peer_service:partitions/0partisan_peer_service:reserve/1partisan_peer_service:resolve_partition/1partisan_peer_service:update_members/1
Use of
partisan_peer_service:mynode/0has been replaced bypartisan:node/0to follow Erlang conventionUse of
partisan_peer_service:myself/0has been replaced bypartisan:node_spec/0to disambiguate frompartisan:node/0.Use of
Nodevariable name fornode()type (as opposed toName) andNodeSpecfornode_spec()(as opposed toNode) to disambiguate.Adde new module
partisan_rpcthat will provide and API that mirrors ErlangsrpcanderpcmodulesAdded
partisan_remote_refto encapsulate the creation of reference and added an optional/alternative representation for encoded pids, references and registered names. The module offers all the functions to convert pids, references and names to/from Partisan encoded references.Alternative representation: In cases where lots of references are stored in process state, ets and specially where those are uses as keys, a binary format is preferable to the tuple format in order to save memory usage and avoid copying the term every time a message is send between processes.
partisan_remote_refrepresents an encoded reference as binary URI. This is controlled by the config optionremote_ref_as_uriandremote_ref_binary_paddingin case the resulting URIs are smaller than 65 bytes.1> partisan_remote_ref:from_term(self()). {partisan_remote_reference,nonode@nohost,{partisan_process_reference,"<0.1062.0>"}} 2> partisan_config:set(remote_ref_as_uri, true). ok 3> partisan_remote_ref:from_term(self()). <<"partisan:pid:nonode@nohost:0.1062.0">> 4> partisan_config:set(remote_ref_binary_padding, true). ok 5> partisan_remote_ref:from_term(self()). <<"partisan:pid:nonode@nohost:0.1062.0:"...>>
Peer Membership
Fixes
- Extracted the use of
state_orsetfrompartisan_full_membership_strategyinto its own modulepartisan_membership_setwhich will allow the possibility to explore alternative data structures to manage the membership set. - Introduced a membership prune operation to remove duplicate node specifications in the underlying
state_orsetdata structure. This isto avoid an issue where a node will crash and restart with a different IP address e.g. when deploying in cloud orchestration platforms. As the membership set containsnode_spec()objects which contain IP addresses we ended up with duplicate entries for the node. The prune operation tries to break ties between these duplicates at time of connection, trying to recognise when a node specification might be no longer valid forcing the removal of the spec from the set. - Fixes several bugs related to the
leaveoperation inpartisan_pluggable_peer_service_manager:- Added a missing call to update the membership set during leave
- Fixed a concurrency issue whereby on self leave the peer service server will restart before being able to sending the new state with the cluster peers and thus the node would remain as a member in all other nodes.
- Resolves an issue
partisan_plumtree_broadcastwhere theall_membersset was not updated when a member is removed. - Resolves the issue where the
partisan_plumtree_broadcastwas not removing the local node from the broadcast member set. - Gen Behaviours take new option
channelif defined. - Fixed implementation of
on_upandon_downcallback functions inpartisan_pluggable_peer_service_manager
Changes
- Added function
partisan_peer_service_manager:member/1 - Replaced the use of in-process sets in
plumtree_broadcast_backendwith anetstable for outstanding messages keeping the gen_server stack lean and avoiding garbage collection
Peer Connection management
Fixes
- Fixes a bug where connections where not properly killed during a leave
- Split TLS options for client and server roles
- Removed
tls_options - Added
tls_client_optionsandtls_server_options
- Removed
Changes
- New module
peer_service_connections:- Replaces the former
peer_service_connectionsprocess state data structure and thepartisan_connection_cachemodule. - As a result, the
partisan_connection_cachemodule has been was removed. - Checking connection status is now very fast and cheap. The implementation uses
etsto handle concurreny. It leverages leveragesets:update_counter/4,ets:lookup_element/3andets:select_count/2for fast access and to minimise copying data into the caller's process heap.
- Replaces the former
Process and Peer Monitoring
Fixes
- A more complete/safe implementation of process monitoring in
partisan_monitor. - More robust implementation of monitors using the new subscription capabilities provided by
peer_service:on_upandpeer_service:on_downcallback functions.- monitor a node or all nodes
- use node monitors to signal a process monitor when the remote node is disconnected
- local cache of process monitor to ensure the delivery of DOWN signal when the connection to the process node is down.
- avoid leaking monitors
- new supervisor to ensure that
partisan_monitoris restarted every time the configuredpartisan_peer_service_manageris restarted. - re-implementation based on ets tables
- If using OTP25 the monitor gen_server uses the parallel signal optimisation by placing the process inbox data off heap
NOTICE
At the moment this only works for partisan_pluggable_peer_service_manager backend.
Changes
- New api in
partisanmodule following the same name, signature and semantics of theirerlangandnet_kernelmodules counterparts:
OTP compatibility
Fixes
Changes
- Partisan now requires OTP24 or later.
- Upgraded
partisan_genandpartisan_gen_serverto match their OTP24 counterparts implementation - Added
partisan_gen_statem partisan_gen_fsmdeprecated as it was not complete and focus was given to the implementation ofpartisan_gen_stateminstead- Module
partisan_mochiglobalhas been removed and replaced bypersistent_term
Misc
Fixes
- Most existing
INFOlevel logs have been reclassified asDEBUG - Fixed types specifications in various modules
Changes
lagerdependency has been removed and all logging is done using the new Erlanglogger- Most uses of the
orddictmodule have been replaced by maps for extra performance and better usability - Most API options using
proplistsmodule have been replaced by maps for extra performance and better usability - In several functions the computation of options (merging user provided with defaults, validation, etc.) has been postponed until (and only if) it is needed for extra performance e.g.
partisan_pluggable_peer_servie_manager:forward_message - More utils in
partisan_util - Added
ex_doc(Elixir documentation) rebar plugin - Upgraded the following dependencies:
uuidtypes- rebar plugins