🔒 Reliability: Timeouts, Reconnects, Backoff (GoMT4)¶
This chapter shows how reliability is implemented in this repo and how to tune it. Everything below references real code locations so you can cross‑check quickly.
⏳ 1) Backoff & Jitter (central knobs)¶
Defined in examples/mt4/MT4Account.go:
// Retry/backoff settings.
const (
backoffBase = 300 * time.Millisecond // initial backoff
backoffMax = 5 * time.Second // cap for backoff
jitterRange = 200 * time.Millisecond // ± jitter range
maxRetries = 10 // attempts before giving up
)
Helpers:
// waitWithCtx sleeps for d unless ctx is done.
func waitWithCtx(ctx context.Context, d time.Duration) error { /* ... */ }
// backoffDelay returns exponential backoff with jitter, capped.
func backoffDelay(attempt int) time.Duration { /* base<<attempt, cap, jitter */ }
💡 Why it matters: all retry loops (unary and streaming) use these values.
Increase backoffBase for slower retry cadence; raise backoffMax for noisy networks; widen jitterRange to desync reconnect storms.
⏱️ 2) Per‑call timeouts (unary RPC)¶
Pattern across calls: add a short per‑call timeout around read‑only RPCs.
Example in ConnectByServerName health‑check (examples/mt4/MT4Account.go):
hctx, cancel := context.WithTimeout(ctx, 3*time.Second)
defer cancel()
if _, err := a.AccountSummary(hctx); err != nil { /* disconnect & error */ }
📌 Guidelines:
- Read‑only calls: 2–5s.
- Trading actions: 3–8s, depending on broker latency.
- Always
defer cancel()to avoid goroutine leaks.
🔁 3) Retrying unary calls (transport errors)¶
Unary execution uses a retry loop that:
- calls the RPC,
- if error is
codes.Unavailable(transient transport), waitsbackoffDelay(attempt)withwaitWithCtx, then retries, - aborts on context cancel/deadline.
Pseudo‑excerpt (examples/mt4/MT4Account.go logic):
for attempt := 0; attempt < maxRetries; attempt++ {
res, err := grpcCall(headers)
if err == nil { return res, nil }
if s, ok := status.FromError(err); ok && s.Code() == codes.Unavailable {
if err := waitWithCtx(ctx, backoffDelay(attempt)); err != nil { return zero, err }
continue
}
return zero, err // non‑transient
}
return zero, fmt.Errorf("max retries reached: %w", lastErr)
⚙️ Tuning tips:
- For LAN/VPS,
backoffBase=150msoften feels snappier. - For unstable links, keep
base=300ms, maybemax=8–10s.
📡 4) Streaming with auto‑reconnect¶
Streaming helpers reopen the stream on recoverable errors (io.EOF / codes.Unavailable), using the same backoff+jitter.
Used by methods like OnSymbolTick and history/updates streams (examples/mt4/MT4Account.go).
Consumer pattern:
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Start stream
dataCh, errCh := account.OnSymbolTick(ctx, []string{"EURUSD"})
for {
select {
case <-ctx.Done():
return
case err := <-errCh:
if err != nil {
log.Printf("stream stopped: %v", err)
return
}
case tick := <-dataCh:
// process tick (DB, strategy, etc.)
}
}
🔎 Notes:
- Helper closes both channels when it gives up (
maxRetriesreached) orctxcanceled. - Don’t call
Recv()yourself; consume fromdataCh.
🧹 5) Clean cancellation & health checks¶
- Use one parent
ctxper workflow; cancel on shutdown. - Example (
examples/main.go): account closed viadefer account.Disconnect(). - After connect, code performs AccountSummary health‑check with 3s timeout.
✅ Shutdown checklist:
- Cancel stream contexts first.
- Wait for goroutines to finish.
- Then
Disconnect().
⚖️ 6) Choosing sensible defaults¶
| Scenario | Timeouts | Backoff (base → max) |
|---|---|---|
| Read‑only (quotes/info) | 2–3s per call | 300ms → 5s (default) |
| Trading actions | 3–8s per call | 300ms → 5–8s |
| Unstable/home Wi‑Fi | 4–6s per call | 500ms → 8–10s, jitter 300–400ms |
| VPS / local LAN | 1–2s per call | 150ms → 3–5s |
📌 All map to constants in MT4Account.go. Adjust once, apply everywhere.
⚠️ 7) Common pitfalls (and fixes)¶
- ❌ Leak: forgot
cancel()→ ✅ Alwaysdefer cancel(). - ❌ Hammering retries → ✅ Increase
backoffBaseorjitterRange. - ❌ Permanent errors treated as transient → ✅ Retry only on
codes.Unavailable/io.EOF. - ❌ Dead app on shutdown → ✅ Handle
<-ctx.Done()properly.
📂 8) Where to look in code¶
- Constants & helpers →
examples/mt4/MT4Account.go(retry/backoff, jitter,waitWithCtx). - Unary patterns & health‑check →
examples/mt4/MT4Account.go. - Streaming patterns →
examples/mt4/MT4Account.go(OnSymbolTick). - Entry point & cleanup →
examples/main.go(Disconnect()on exit).