pg(3)

πŸ“¦
version 29.0.6 Β· 3 versions
, in ubuntu stonking

πŸ’– pg β€” the manual, but make it a group chat πŸ’–

okay bestie, so this is the pg module from the Erlang docs β€” kernel manual, section 3. Sit down, grab a strawberry πŸ“, and let's get into it, because this one is genuinely iconic.


✨ NAME ✨

pg β€” Distributed named process groups. Yeah. Groups. But make them distributed. πŸ’…


πŸ¦‹ DESCRIPTION πŸ¦‹

Okay so, this module implements process groups. And the whole vibe is: you can send a message to one, some, or all group members. That's the dream, that's the fantasy. πŸ”₯

Not me about to drop some lore though β€” up until OTP 17 there used to exist an experimental pg module in stdlib. This pg module is not the same module as that experimental pg module, and they only share the same module name. Like, same name, different girl. πŸ™ˆ

So here's how it works, fr fr: a group of processes can be accessed by a common name. For example, if there is a group named foobar, there can be a set of processes (which can be located on different nodes) that are all members of the group foobar. And β€” listen β€” there are no special functions for sending a message to the group. None. Instead, client functions are to be written with the functions get_members/1 and get_local_members/1 to determine which processes are members of the group. Then the message can be sent to one or more group members. You're the main character writing the client functions, periodt. ✨

If a member terminates, it is automatically removed from the group. The group serves cunt and also serves eviction. 🫢

A process may join multiple groups. It may join the same group multiple times. But β€” and this is the catch β€” it is only allowed to join processes running on local node. Local only, bestie. No crossing nodes on that one.

Process Groups implement strong eventual consistency. The Process Groups membership view may temporarily diverge. For example, when processes on node1 and node2 join concurrently, node3 and node4 may receive updates in a different order. It's giving "everybody finds out eventually, just not in the same order" πŸ’€

Membership view is not transitive. If node1 is not directly connected to node2, they will not see each other's groups. But if both are connected to node3, node3 will have the full view. Node3 is the group chat admin, ngl. πŸ‘‘

Groups are automatically created when any process joins, and are removed when all processes leave the group. The non-existing group is considered empty (containing no processes). Lowkey a beautiful lifecycle to witness. 🌸

Process groups can be organised into multiple scopes. Scopes are completely independent of each other. A process may join any number of groups in any number of scopes. Scopes are designed to decouple single mesh into a set of overlay networks, reducing amount of traffic required to propagate group membership information. Default scope pg is started automatically when Kernel is configured to do so. ✨

NOTE: Scope name is used to register process locally, and to name an ETS table. If there is another process registered under this name, or another ETS table exists, scope fails to start. Local membership is not preserved if scope process exits and restarts. A scope can be kept local-only by using a scope name that is unique cluster-wide, e.g. the node name: pg:start_link(node()).

Read that note twice, bestie, it will save your life. 😭

πŸŽ€ See Also

Kernel


🌈 DATA TYPES 🌈

global_view() :: {atom(), #{pid() => {reference(), [group()]}}}.

group() :: term().

  • The identifier of a process group. (the name, the label, the ✨identity✨)

local_data() :: #{group() => [pid()]}.

options() :: map().

scope() :: atom().

subscribe_result() :: #{group() => [pid()]} | [pid()].

subscription() :: scope | {group, group()}.

subscriptions() :: #{subscription() => #{reference() => pid()}}.

update() :: [{group(), Add :: [pid()], Remove :: [pid()]}].

version() :: 0 | 1.


πŸ’… FUNCTIONS πŸ’…

demonitor(Scope :: atom(), Ref :: reference()) -> ok | false.

Unsubscribes the caller from updates (scope or group). Flushes all outstanding updates that were already in the message queue of the calling process. Like muting the chat but also clearing the unread messages. Iconic behaviour. 🧚

get_local_members(Scope :: atom(), Group :: group()) -> [pid()].

Returns all processes running on the local node in the group Group. Processes are returned in no specific order. This function is optimised for speed. 🏎️

get_members(Scope :: atom(), Group :: group()) -> [pid()].

Returns all processes in the group Group. Processes are returned in no specific order. This function is optimised for speed. Also fast, also serving. πŸ”₯

join(Scope :: atom(), Group :: group(), PidOrPids :: pid() | [pid()]) -> ok.

Joins single process or multiple processes to the group Group. A process can join a group many times and must then leave the group the same number of times. (yes, it's a counter, no, you cannot skip leg day)

PidOrPids may contain the same process multiple times. No judgement. 🫢

leave(Scope :: atom(), Group :: group(), PidOrPids :: pid() | [pid()]) -> ok | not_joined.

Makes the process PidOrPids leave the group Group. If the process is not a member of the group, not_joined is returned. πŸšͺ

When list of processes is passed as PidOrPids, function returns not_joined only when all processes of the list are not joined.

monitor(Scope :: atom(), Group :: group()) -> {reference(), [pid()]}.

Subscribes the caller to updates for the specified group.

Returns list of processes currently in the group, and a reference to match the upcoming notifications.

See monitor_scope/0 for the update message structure. πŸ‘€

monitor_scope(Scope :: atom()) -> {reference(), #{group() => [pid()]}}.

Subscribes the caller to updates from the specified scope.

Returns content of the entire scope and a reference to match the upcoming notifications.

Whenever any group membership changes, an update message is sent to the subscriber:

{Ref, join, Group, [JoinPid1, JoinPid2]}

{Ref, leave, Group, [LeavePid1]}

That's the notification format, copy it exactly, don't freestyle it. πŸ’–

start(Scope :: atom()) -> {ok, pid()} | {error, term()}.

Starts additional scope. ✨

Starts the default pg scope within supervision tree.

Kernel may be configured to do it automatically by setting the Kernel configuration parameter start_pg.

Equivalent to start(Scope), except that it also creates a link/1 with the calling process. So it's the same but clingier. 🫢

which_groups(Scope :: atom()) -> [Group :: group()].

Returns a list of all known groups. The full roster. The guest list. The πŸ’– who's-who πŸ’–.


okay that's the whole page, no skips, no cuts, every flag and every type stayed in. you ate that, bestie. πŸ‘‘

also, credit where credit is due: this one came from Ericsson AB, and it's the kernel 11.0.3 version of the page. mwah. πŸ’‹ βœ¨πŸŒΈπŸ’…

πŸ“¦ where this page comes from