Files
spore/docs/ClusterBroadcast.md
Patrick Balsiger b9b91d71b5 docs: add ClusterBroadcast guide for CLUSTER_EVENT and cluster/broadcast flow
Documents centralized cluster broadcasting: service -> ctx.fire("cluster/broadcast", eventJson) -> UDP CLUSTER_EVENT -> peer ctx.fire(event, data). Includes message format, service/core responsibilities, logging, networking notes, and troubleshooting.
2025-09-28 13:45:10 +02:00

3.2 KiB

Cluster Broadcast (CLUSTER_EVENT)

Overview

Spore supports cluster-wide event broadcasting via UDP. Services publish a local event, and the core broadcasts it to peers as a CLUSTER_EVENT. Peers receive the event and forward it to local subscribers through the internal event bus.

  • Local trigger: ctx.fire("cluster/broadcast", eventJson)
  • UDP message: CLUSTER_EVENT:{json}
  • Receiver action: Parses {json} and calls ctx.fire(event, data)

This centralizes network broadcast in core, so services never touch UDP directly.

Message format

  • UDP payload prefix: CLUSTER_EVENT:
  • JSON body:
{
  "event": "<event-name>",
  "data": "<json-string>" // or an inline JSON object/array
}

Notes:

  • The receiver accepts data as either a JSON string or a nested JSON object/array. Nested JSON is serialized back to a string before firing the local event.
  • Keep payloads small (UDP, default buffer 512 bytes).

Core responsibilities

  • ClusterManager registers a centralized handler:
    • Subscribes to cluster/broadcast to send the provided event JSON over UDP broadcast.
    • Listens for incoming UDP CLUSTER_EVENT messages and forwards them to local subscribers via ctx.fire(event, data).
  • Broadcast target uses subnet-directed broadcast (e.g., 192.168.1.255) for better reliability. Both nodes must share the same udp_port.

Service responsibilities

Services send and receive events using the local event bus.

  1. Subscribe to an event name and apply state from data:
ctx.on("api/neopattern", [this](void* dataPtr) {
    String* jsonStr = static_cast<String*>(dataPtr);
    if (!jsonStr) return;
    JsonDocument doc;
    if (deserializeJson(doc, *jsonStr)) return;
    JsonObject obj = doc.as<JsonObject>();
    // Parse and apply fields from obj
});
  1. Build a control payload and update locally via the same event:
JsonDocument payload;
payload["pattern"] = "rainbow_cycle"; // example
payload["brightness"] = 100;
String payloadStr; serializeJson(payload, payloadStr);
ctx.fire("api/neopattern", &payloadStr);
``;

3) Broadcast to peers by delegating to core:
```cpp
JsonDocument envelope;
envelope["event"] = "api/neopattern";
envelope["data"] = payloadStr; // JSON string
String eventJson; serializeJson(envelope, eventJson);
ctx.fire("cluster/broadcast", &eventJson);

With this flow, services have a single codepath for applying state (the event handler). Broadcasting simply reuses the same payload.

Logging

  • Core logs source IP, payload length, and event name for received CLUSTER_EVENTs.
  • Services can log when submitting cluster/broadcast and when applying control events.

Networking considerations

  • Ensure all nodes:
    • Listen on the same udp_port.
    • Are in the same subnet (for subnet-directed broadcast).
  • Some networks may block global broadcast (255.255.255.255). Subnet-directed broadcast is used by default.

Troubleshooting

  • If peers do not react:
    • Confirm logs show CLUSTER_EVENT raw from <ip> on the receiver.
    • Verify UDP port alignment and WiFi connection/subnet.
    • Check payload size (<512 bytes by default) and JSON validity.
    • Ensure the service subscribed to the correct event name and handles data.