Production resilience patterns implemented in GoBridge transport adapters.
When multiple receivers share an MQTT session, each handler goroutine receives an independent deep-copy of the MQTT Properties (User properties, CorrelationData, etc.), preventing data races under concurrent dispatch.
MQTT error messages are matched case-insensitively. “Connection Refused”, “CONNECTION REFUSED”, and “connection refused” are all correctly classified as ErrConnectionLost, enabling proper retry behavior regardless of broker error message formatting.
MQTT senders enforce a 60-second fallback timeout when no explicit timeout is configured, preventing indefinite hangs on stalled broker connections. When the caller’s context already has a deadline, the fallback is not applied.
When the MQTT circuit breaker sender rejects a publish (circuit open), a mqtt.publish.failures metric with reason=circuit_open tag is emitted, providing visibility into fail-fast rejections.
SQS receiver startup (client creation, queue URL resolution) is bounded by a 30-second timeout, preventing indefinite hangs when AWS credentials or endpoints are unavailable.
Each SQS ReceiveMessage call has an explicit timeout of WaitTimeSeconds + 10 seconds, protecting against network-level stalls beyond the SQS long-poll window.
SQS receivers default to WaitTimeSeconds=20 (long-polling) when not explicitly configured, preventing accidental short-polling which causes excessive API costs.
SQS batch send failures now distinguish between server faults (transient, retriable) and sender faults (permanent, not retriable). Messages with malformed payloads are classified as ErrorRejected and routed to DLQ instead of being retried indefinitely.
When Extend() changes the SQS visibility timeout, the auto-extend ticker interval updates accordingly, preventing excessive or insufficient extend calls.
The processingCancel context function is set during delivery construction (before the auto-extend goroutine starts), eliminating a race window where extend failure could not cancel processing.
HTTP forwarder responses are classified by status code family:
ErrUnavailable (transient, retriable)ErrForwardFailed (permanent, not retriable)HTTP ingress validates Content-Type: application/json when the header is present, returning 415 Unsupported Media Type for non-JSON requests.
HTTP ingress generates a unique envelope ID when the request omits the id field, ensuring all messages have traceable identifiers.
bridge:
id: resilient-bridge
sessions:
- id: mqtt-session
transport: mqtt
options:
session:
broker_urls: ["tcp://broker:1883"]
client_id: resilient-bridge
keep_alive: 30
receivers:
- id: sqs-in
transport: sqs
options:
queue_url: https://sqs.us-west-1.amazonaws.com/123/input
# wait_time_seconds defaults to 20 (long-polling)
# auto_extend defaults to true with adaptive ticker
senders:
- id: mqtt-out
transport: mqtt
session_id: mqtt-session
options:
sender:
qos: 1
# timeout defaults to 30s; 0 uses 60s safety-net
bindings:
- id: to-mqtt
sender_id: mqtt-out
# Naming the session on the binding is what makes the bridge manage it:
# a session nobody manages never connects, and every publish fails.
session_id: mqtt-session
address: devices/commands
stores:
# Where a message the route gives up on is kept.
dlq:
type: sqlite
options:
path: /var/lib/gobridge/state/dlq.db
routes:
- id: resilient-route
receiver_id: sqs-in
delivery_mode: direct_hold
bindings: [to-mqtt]
processors: [transform, circuitbreaker]
processors lists names, not inline config. transform and circuitbreaker are
registered programmatically in Go (see processors-and-stores.md);
the runtime resolves each name against the registry. There is no YAML surface for
processor options.
| Transport | Pattern | Default | Effect |
|---|---|---|---|
| MQTT | Publish timeout fallback | 60s | Prevents indefinite hangs on stalled connections |
| MQTT | Properties deep-copy | Always | Prevents data races in multi-handler sessions |
| MQTT | Case-insensitive errors | Always | Correct error classification regardless of broker |
| MQTT | Circuit breaker metrics | Always | mqtt.publish.failures with reason=circuit_open |
| SQS | Receiver init timeout | 30s | Prevents startup hangs |
| SQS | Per-poll timeout | WaitTime+10s | Prevents network-level stalls |
| SQS | Long-poll default | 20s | Prevents accidental short-polling |
| SQS | Batch error classification | Always | SenderFault -> permanent, ServerFault -> transient |
| SQS | Adaptive auto-extend | Always | Ticker interval tracks visibility timeout changes |
| HTTP | Forward error classification | Always | 5xx transient, 4xx permanent |
| HTTP | Content-Type validation | Always | 415 for non-JSON when Content-Type present |
| HTTP | Auto envelope ID | Always | UUID generated when id field omitted |