- Events & Pub/Sub
- Best Practices
Best Practices
This guide covers strategies for managing subscriptions, handling failures, and optimizing event-driven logic.
Subscription Resource Management
Section titled “Subscription Resource Management”- Reuse Clients: Create one client per application lifecycle. Do not create a new client for every subscription.
- Specific Patterns: Use specific patterns (e.g.,
events:order.123) instead of wildcards (events:>) where possible to reduce network traffic. - Limit In-Flight: Use
maxInflightin consumer groups to prevent your worker from being overwhelmed.
Error Handling Patterns
Section titled “Error Handling Patterns”Real-time subscriptions can fail due to network drops or server restarts. The JS
SDKs reconnect automatically. In Go it depends on how you built the client:
client.CreateSubscriptionClient() sets AutoReconnect: true for you, while
NewSubscriptionClient(SubscriptionClientConfig{...}) defaults it to false —
only the delay (1s), backoff (1.5) and max-delay (30s) fields get defaults. A
subscription that carries StartAfterSequence reconnects either way. Whichever
path you take, your application should be aware of the state.
const sub = await ironflow.subscribe("events:>", { onEvent: (e) => handle(e), onError: (error) => { // 'retrying' means the SDK is already attempting to reconnect if (error.retrying) { console.warn("Connection lost, reconnecting..."); } else { console.error("Terminal subscription error:", error.code); } }});for { select { case event := <-sub.Events(): handle(event) case err := <-sub.Errors(): log.Printf("Sub error [%s]: %s (retrying: %v)", err.Code, err.Message, err.Retrying) }}from ironflow import IronflowRPCfrom ironflow.rpc import IronflowRPCErrorfrom ironflow.rpc.v1 import SubscribeOptions, SubscribeRequest
cursor = 0with IronflowRPC(server_url="http://localhost:9123") as rpc: try: # A positioned subscription (start_after_sequence) reconnects by itself. for event in rpc.pubsub.subscribe(SubscribeRequest( pattern="events:>", options=SubscribeOptions(start_after_sequence=cursor), )): handle(event) cursor = event.sequence except IronflowRPCError as err: # Raised when the reconnect budget is spent, or for a terminal error. print("Terminal subscription error:", err.code)Without start_after_sequence, a dropped connection ends the stream.
Idempotency
Section titled “Idempotency”Events may be delivered more than once (At-Least-Once delivery). Your logic should be idempotent:
const processedEvents = new Set();
onEvent: (event) => { if (processedEvents.has(event.eventId)) return; processedEvents.add(event.eventId);
// Perform logic...}processed_events: set[str] = set()
for event in events: if event.event_id in processed_events: continue processed_events.add(event.event_id)
# Perform logic...Performance Considerations
Section titled “Performance Considerations”| Scenario | Recommendation |
|---|---|
| High Volume | Use CEL Filtering to filter on the server. |
| Slow Consumers | Use Consumer Groups. Set maxInflight and backpressure on the group. |
Monitoring & Debugging
Section titled “Monitoring & Debugging”Use the Ironflow CLI to monitor events in real-time while developing your application.
# Watch all system and user eventsironflow subscribe "system.>" "events:>" --json
# Monitor a specific workflow runironflow subscribe "system.run.run_abc123.>" --replay 100
# Watch for failures onlyironflow subscribe "system.run.*.failed" --json | jqCommon Troubleshooting
Section titled “Common Troubleshooting”| Issue | Check |
|---|---|
| No events received | Verify the pattern matches (e.g., events: prefix) and the server is reachable. |
| Duplicate events | Expected in At-Least-Once delivery. Ensure your handlers are idempotent. |
| Connection drops | Check for proxy/load-balancer timeouts. The JS SDKs reconnect automatically; in Go it depends on AutoReconnect — see Error Handling Patterns. |