You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
`bacaro_new` creates a new instance with the given process name, binds its sockets, and starts peer discovery. Returns `NULL` on failure.
13
13
14
-
`bacaro_destroy` disconnects all peers, removes IPC files, and frees all resources. Sets `*self` to `NULL`.
14
+
`published_domains` is an optional NULL-terminated array of domain strings that this process will publish. When provided, a `.manifest` file is written alongside the `.pub` and `.rep` files so that peers can skip snapshot requests for non-overlapping domains. Pass `NULL` if you don't want to declare a manifest — the bus works identically without one.
15
+
16
+
```c
17
+
// Declare published domains (optional optimisation)
`bacaro_destroy` disconnects all peers, removes IPC files (including the `.manifest` if one was written), and frees all resources. Sets `*self` to `NULL`.
Copy file name to clipboardExpand all lines: docs/architecture.md
+13-10Lines changed: 13 additions & 10 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -29,17 +29,18 @@ Each process uses a **single shared SUB socket** that connects to every peer's P
29
29
1. Generate a UUID for this instance.
30
30
2. Resolve the runtime directory (`BACARO_RUNTIME_DIR`, default `/tmp/bacaro`).
31
31
3. Create the runtime directory if it does not exist.
32
-
4. Bind a `ZMQ_PUB` socket → IPC file `<name>.<uuid>.pub`
33
-
5. Bind a `ZMQ_ROUTER` socket → IPC file `<name>.<uuid>.rep`
34
-
6. Call `discovery_init` (see Discovery below).
32
+
4. If `published_domains` is non-NULL, write a `.manifest` file (`<name>.<uuid>.manifest`) listing one domain per line — written **before** discovery so peers see it when they find the `.pub` file.
33
+
5. Bind a `ZMQ_PUB` socket → IPC file `<name>.<uuid>.pub`
34
+
6. Bind a `ZMQ_ROUTER` socket → IPC file `<name>.<uuid>.rep`
35
+
7. Call `discovery_init` (see Discovery below).
35
36
36
37
The UUID in the filename prevents collisions when a process restarts quickly under the same name.
37
38
38
39
### Shutdown (`bacaro_destroy`)
39
40
40
41
1.`discovery_cleanup`: disconnect all peers, close epoll and inotify fds.
41
42
2. Set `ZMQ_LINGER = 200ms` on the PUB socket before closing, so any recently published messages have time to flush to connected peers (see [Slow-Joiner Note](#slow-joiner-note)).
42
-
3. Close and remove the `.pub`and `.rep` IPC files.
43
+
3. Close and remove the `.pub`, `.rep`, and `.manifest` IPC files.
43
44
4. Destroy the ZMQ context.
44
45
45
46
---
@@ -69,10 +70,9 @@ Triggered by a new `.pub` file appearing (either from the initial scan or from a
69
70
1. Extract the peer name from the filename (everything before the first `.`).
70
71
2. Derive the `.rep` filename by replacing the `.pub` suffix.
71
72
3. Call `zmq_connect` on the **shared SUB socket** to the peer's PUB endpoint.
72
-
4. Create a per-peer `ZMQ_DEALER` socket, connect to `ipc://<runtime_dir>/<rep_filename>`.
73
-
5. Register the DEALER socket's ZMQ fd with epoll.
74
-
6. Record the peer in `peers` and `dealer_fd_to_filename` maps. Store the PUB endpoint string for later `zmq_disconnect`.
75
-
7. Send a snapshot request for each currently subscribed domain prefix.
73
+
4. Read the peer's `.manifest` file if present; store declared domains and `has_manifest` flag in `PeerInfo`.
74
+
5. Record the peer in the `peers` map, storing both the PUB and REP endpoints.
75
+
6.**Lazy DEALER**: if the process has active subscriptions, iterate them. For each prefix that overlaps with the peer's manifest (or if the peer has no manifest), create a per-peer `ZMQ_DEALER` socket, connect to the REP endpoint, register its fd with epoll, and send a snapshot request. Peers with a manifest that doesn't overlap any active subscription get no DEALER socket at all.
76
76
77
77
### Peer disconnect (`discovery_peer_disconnect`)
78
78
@@ -257,7 +257,10 @@ The main handle. One per process. Contains:
257
257
### `PeerInfo`
258
258
259
259
Per-peer connection state:
260
-
-`dealer_sock` — per-peer ZMQ DEALER socket for snapshot protocol
260
+
-`dealer_sock` — per-peer ZMQ DEALER socket for snapshot protocol (created lazily, may be `nullptr`)
261
261
-`name` — peer process name (extracted from filename)
262
262
-`pub_endpoint` — stored for `zmq_disconnect` on the shared SUB socket
263
-
-`dealer_fd` — ZMQ fd registered with epoll
263
+
-`rep_endpoint` — stored for lazy DEALER connect
264
+
-`dealer_fd` — ZMQ fd registered with epoll (`-1` until DEALER is created)
265
+
-`manifest` — declared published domains read from the peer's `.manifest` file
266
+
-`has_manifest` — `true` if a `.manifest` file was found at discovery time
Copy file name to clipboardExpand all lines: docs/concepts.md
+24-7Lines changed: 24 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -22,8 +22,9 @@ There is no central broker. Each process:
22
22
23
23
1. Binds a `ZMQ_PUB` socket and a `ZMQ_ROUTER` socket, creating IPC files in the runtime directory.
24
24
2. Watches the runtime directory with **inotify** for new peers.
25
-
3. Connects a `ZMQ_SUB` socket and a `ZMQ_DEALER` socket to each peer on discovery.
26
-
4. Requests a **snapshot** of current state from each peer on connect, seeding its local cache.
25
+
3. Connects a single shared `ZMQ_SUB` socket to each peer's PUB endpoint on discovery.
26
+
4. Creates a per-peer `ZMQ_DEALER` socket lazily — only when a subscription overlaps with the peer's manifest (or when the peer has no manifest).
27
+
5. Requests a **snapshot** of current state from each relevant peer on connect, seeding its local cache.
27
28
28
29
After the initial snapshot, live `PUB/SUB` updates keep every subscriber's cache current.
29
30
@@ -33,7 +34,7 @@ Any process can publish on any path at any time. There is no ownership enforceme
33
34
34
35
## Publisher identity
35
36
36
-
No identity frame is added to the wire format. The publisher's name is inferred from which IPC socket the message arrived on — zero overhead.
37
+
The publisher's name is carried explicitly as a frame in the wire format. This is necessary because all messages from all peers arrive on a single shared SUB socket — there is no per-peer socket to infer identity from.
37
38
38
39
## Late join / snapshot protocol
39
40
@@ -43,14 +44,30 @@ Bacaro solves this automatically: every time a new peer is discovered or a new s
43
44
44
45
This means `bacaro_get` always returns the latest known value immediately, without any blocking network call.
45
46
47
+
## Manifest
48
+
49
+
An optional optimisation for deployments where the set of processes and their published domains is known at build time.
50
+
51
+
When calling `bacaro_new`, pass a NULL-terminated array of domain strings that the process will publish:
Bacaro writes this list to a `.manifest` file alongside the `.pub` file. When peers discover the process, they read the manifest and only open a DEALER socket (and request a snapshot) if their subscriptions overlap with the declared domains. Peers with no overlapping subscriptions skip the handshake entirely, reducing boot-time connection overhead.
59
+
60
+
Passing `NULL` disables the manifest — the bus works identically without one. The manifest is an optimisation hint, not access control: a process can still publish on any path regardless of what it declared.
61
+
46
62
## Wire format
47
63
48
-
Every published message is a three-frame ZMQ multipart message:
64
+
Every published message is a four-frame ZMQ multipart message:
49
65
50
66
```
51
-
Frame 0 — topic : "domain.sub.property" (ZMQ SUB prefix filter)
0 commit comments