# Process Groups _Path: en/lua/core/pg_ ## Table of Contents - Process Groups ## Content # Process Groups Join processes into named groups and broadcast to every member across the cluster. Modeled on Erlang/OTP `pg`: groups are dynamic, a process can belong to many groups, and membership is tracked cluster-wide and is eventually consistent. For the scope entry kind and its configuration, see [Process Groups](system/process-groups.md). For the broader clustering model, see the [Cluster Guide](guides/cluster.md). ## Loading ```lua local pg = require("pg") ``` ## Opening a Scope A process group lives inside a **scope** — a `pg.scope` registry entry. Open it to get an instance you operate on: ```lua local group, err = pg.open("app:pg") if err then return nil, err end ``` | Parameter | Type | Description | |-----------|------|-------------| | `id` | string | Scope entry ID (format: `"namespace:name"`) | **Returns:** `pg.Instance, error` **Permission:** `pg.open` on the scope `id` The instance is released automatically when the process exits; call `release()` to free it earlier. All other operations are methods on the instance, called with `:`. ## Joining and Leaving ```lua local ok, err = group:join("workers") -- single group local ok, err = group:join({"workers", "all"}) -- batch local ok, err = group:leave("workers") ``` | Parameter | Type | Description | |-----------|------|-------------| | `group` | string \| string[] | Group name, or a list of names for a batch operation | **Returns:** `boolean, error` A process may join the same group more than once; it must leave the same number of times to fully depart (multi-join semantics). `leave` is best-effort across a batch and returns an error only when the process was a member of none of the named groups. **Permissions:** `pg.join` / `pg.leave` on each group name ## Listing Members ```lua local members, err = group:get_members("workers") -- all nodes local local_members, err = group:get_local_members("workers") -- this node only ``` | Parameter | Type | Description | |-----------|------|-------------| | `group` | string | Group name | **Returns:** `string[], error` — an array of PID strings (empty for an unknown group) **Permissions:** `pg.get_members` / `pg.get_local_members` on the group name ## Listing Groups ```lua local groups, err = group:which_groups() -- all groups in the cluster local local_groups, err = group:which_local_groups() -- groups with a local member ``` **Returns:** `string[], error` — group names that currently have at least one member **Permissions:** `pg.which_groups` / `pg.which_local_groups` ## Broadcasting Send a message to every member of a group. Each member receives it under `topic` from the calling process — handle it with `process.listen(topic)`. ```lua local ok, err = group:broadcast("workers", "task", {id = 42}) -- all nodes local ok, err = group:broadcast_local("workers", "task", {id = 42}) -- this node only ``` | Parameter | Type | Description | |-----------|------|-------------| | `group` | string | Target group | | `topic` | string | Message topic | | `...` | any | Zero or more payload values | **Returns:** `boolean, error` **Permissions:** `pg.broadcast` / `pg.broadcast_local` on the group name ## Monitoring a Group `monitor` subscribes to join/leave events for one group and returns the current members atomically — no membership change can slip between the snapshot and the subscription. ```lua local sub, members, err = group:monitor("workers") if err then return nil, err end for _, pid in ipairs(members) do -- current members at subscription time end local ch = sub:channel() local event = ch:receive() -- {kind = "member.joined" | "member.left", path = "workers", data = {...}} sub:close() -- unsubscribe; sub:close({flush = true}) drains queued events first ``` | Parameter | Type | Description | |-----------|------|-------------| | `group` | string | Group to watch | **Returns:** `pg.Subscription, string[], error` — the subscription and a snapshot of current members **Permission:** `pg.monitor` on the group name ## Watching All Groups `events` subscribes to membership changes across every group in the scope and returns a snapshot of all groups to their members. ```lua local sub, snapshot, err = group:events() -- snapshot: { ["workers"] = {pid, ...}, ["all"] = {pid, ...} } local event = sub:channel():receive() sub:close() ``` **Returns:** `pg.Subscription, table, error` **Permission:** `pg.events` ### Event Fields Events delivered on a subscription channel carry: | Field | Type | Description | |-------|------|-------------| | `system` | string | Always `"pg"` | | `kind` | string | `"member.joined"` or `"member.left"` | | `path` | string | The group name | | `data` | table | `{Group = string, PIDs = string[]}` — the affected members | Subscription channels are buffered (capacity 64). If a slow consumer fills the buffer, further events are retained in the process mailbox in order and delivered once the consumer drains the channel (the subscription stalls rather than dropping events). ## Releasing ```lua group:release() ``` Frees the instance immediately. Idempotent; after release, every method returns an error. Cleanup also runs automatically when the process exits. **Returns:** `boolean` ## Permissions | Permission | Method | Resource | |------------|--------|----------| | `pg.open` | `pg.open()` | scope id | | `pg.join` | `join()` | group name | | `pg.leave` | `leave()` | group name | | `pg.get_members` | `get_members()` | group name | | `pg.get_local_members` | `get_local_members()` | group name | | `pg.which_groups` | `which_groups()` | (scope) | | `pg.which_local_groups` | `which_local_groups()` | (scope) | | `pg.broadcast` | `broadcast()` | group name | | `pg.broadcast_local` | `broadcast_local()` | group name | | `pg.monitor` | `monitor()` | group name | | `pg.events` | `events()` | (scope) | ## Errors | Condition | Kind | |-----------|------| | Permission denied | `errors.PERMISSION_DENIED` | | Missing or empty argument | `errors.INVALID` | | Scope not found | `errors.INTERNAL` | | Leave a group with no membership | `errors.INVALID` | | Instance released | `errors.INVALID` | See [Error Handling](lua/core/errors.md) for working with errors. ## See Also - [Process Groups](system/process-groups.md) - Scope entry kind and configuration - [Cluster](guides/cluster.md) - Membership, naming, and the clustering model - [Process Management](lua/core/process.md) - Spawning and messaging individual processes ## Navigation Previous: Process (lua/core/process) Next: Functions (lua/core/funcs)