mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-01 10:01:21 +08:00
Every StartOrderSync spawned a ticker goroutine that ran forever — it survived trader stop AND deletion, so each quick-created trader left a permanent 30s Hyperliquid poll behind. Stacked leaks turned into an ~8s effective hammer that tripped Hyperliquid's 429 rate limit, which then broke the symbol board, trader creation, and order sync itself. - new trader/syncloop package: shared stoppable sync loop with exponential failure backoff (30s base, 5min cap) - all 9 exchanges' StartOrderSync now take the trader's stop channel and stop when the trader stops (close broadcast from AutoTrader.Stop) - provider/hyperliquid: GetPerpDexCoins now serves a 5min TTL cache and falls back to the stale board when the upstream returns 429, so the symbol panel keeps working through rate limiting
49 lines
1.4 KiB
Go
49 lines
1.4 KiB
Go
// Package syncloop runs background exchange order-sync loops with a shared
|
|
// lifecycle: loops stop when the owning trader stops, and consecutive
|
|
// failures back off exponentially so a rate-limiting exchange (HTTP 429)
|
|
// is not hammered at full frequency.
|
|
package syncloop
|
|
|
|
import (
|
|
"time"
|
|
|
|
"nofx/logger"
|
|
)
|
|
|
|
// maxBackoff caps the failure backoff so a recovered exchange is picked up
|
|
// within a few minutes at worst.
|
|
const maxBackoff = 5 * time.Minute
|
|
|
|
// Run starts a background loop calling syncFn at the given interval until
|
|
// stop is closed. After each consecutive failure the wait doubles (capped at
|
|
// maxBackoff); the first success resets it to the base interval.
|
|
func Run(stop <-chan struct{}, interval time.Duration, name string, syncFn func() error) {
|
|
if interval <= 0 {
|
|
interval = 30 * time.Second
|
|
}
|
|
go func() {
|
|
wait := interval
|
|
timer := time.NewTimer(wait)
|
|
defer timer.Stop()
|
|
for {
|
|
select {
|
|
case <-stop:
|
|
logger.Infof("⏹ %s order sync stopped", name)
|
|
return
|
|
case <-timer.C:
|
|
if err := syncFn(); err != nil {
|
|
wait *= 2
|
|
if wait > maxBackoff {
|
|
wait = maxBackoff
|
|
}
|
|
logger.Infof("⚠️ %s order sync failed: %v (backing off, next attempt in %v)", name, err, wait)
|
|
} else {
|
|
wait = interval
|
|
}
|
|
timer.Reset(wait)
|
|
}
|
|
}
|
|
}()
|
|
logger.Infof("🔄 %s order sync started (interval: %v)", name, interval)
|
|
}
|