diff --git a/cmd/server/main.go b/cmd/server/main.go index 7a71c1a..1df8530 100644 --- a/cmd/server/main.go +++ b/cmd/server/main.go @@ -7,6 +7,7 @@ import ( "os" "path/filepath" "sync" + "time" "github.com/nats-io/nats.go" @@ -36,11 +37,60 @@ func main() { } log.Printf("✅ Verbunden mit Redis: %s", redisAddr) - // 2. NATS VERBINDUNG + // 2. NATS VERBINDUNG mit Reconnect-Logik natsURL := getEnv("NATS_URL", "nats://localhost:4222") - nc, err := nats.Connect(natsURL) + + nc, err := nats.Connect( + natsURL, + nats.MaxReconnects(-1), // Unbegrenzte Reconnects + nats.ReconnectWait(2*time.Second), + nats.ReconnectBufSize(5*1024*1024), // 5MB Buffer + nats.DisconnectErrHandler(func(nc *nats.Conn, err error) { + if err != nil { + log.Printf("⚠️ NATS getrennt: %v", err) + } + }), + nats.ReconnectHandler(func(nc *nats.Conn) { + log.Printf("✅ NATS wiederverbunden: %s", nc.ConnectedUrl()) + }), + nats.ClosedHandler(func(nc *nats.Conn) { + log.Println("❌ NATS Verbindung geschlossen") + }), + nats.Timeout(10*time.Second), + nats.PingInterval(20*time.Second), + nats.MaxPingsOutstanding(5), + ) + if err != nil { - log.Fatal("❌ Konnte nicht zu NATS verbinden: ", err) + // Retry-Logik für initiales Connect + maxRetries := 10 + retryDelay := 2 * time.Second + + for i := 0; i < maxRetries; i++ { + log.Printf("⚠️ NATS noch nicht erreichbar (Versuch %d/%d): %v", i+1, maxRetries, err) + log.Printf("⏳ Warte %v vor erneutem Versuch...", retryDelay) + time.Sleep(retryDelay) + + nc, err = nats.Connect( + natsURL, + nats.MaxReconnects(-1), + nats.ReconnectWait(2*time.Second), + nats.Timeout(10*time.Second), + ) + + if err == nil { + break + } + + retryDelay = retryDelay * 2 // Exponential backoff + if retryDelay > 30*time.Second { + retryDelay = 30 * time.Second + } + } + + if err != nil { + log.Fatalf("❌ Konnte nicht zu NATS verbinden nach %d Versuchen: %v", maxRetries, err) + } } defer nc.Close() @@ -48,7 +98,7 @@ func main() { if err != nil { log.Fatal("❌ JSON Encoder Fehler: ", err) } - log.Println("✅ Verbunden mit NATS unter", natsURL) + log.Printf("✅ Verbunden mit NATS: %s", natsURL) // 3. HANDLER: GAME JOIN sub, err := ec.Subscribe("game.join", func(req *game.JoinRequest) { diff --git a/pkg/server/leaderboard.go b/pkg/server/leaderboard.go index 84ac3ca..adb0b1c 100644 --- a/pkg/server/leaderboard.go +++ b/pkg/server/leaderboard.go @@ -24,13 +24,37 @@ const leaderboardKey = "leaderboard:top" func InitLeaderboard(redisAddr string) error { rdb := redis.NewClient(&redis.Options{ - Addr: redisAddr, - DB: 0, + Addr: redisAddr, + DB: 0, + MaxRetries: 5, + MinRetryBackoff: 1 * time.Second, + MaxRetryBackoff: 5 * time.Second, + DialTimeout: 10 * time.Second, + ReadTimeout: 3 * time.Second, + WriteTimeout: 3 * time.Second, }) ctx := context.Background() - if err := rdb.Ping(ctx).Err(); err != nil { - return err + + // Retry-Logik für initiales Connect + maxRetries := 10 + retryDelay := 2 * time.Second + + for i := 0; i < maxRetries; i++ { + if err := rdb.Ping(ctx).Err(); err != nil { + log.Printf("⚠️ Redis noch nicht erreichbar (Versuch %d/%d): %v", i+1, maxRetries, err) + if i < maxRetries-1 { + log.Printf("⏳ Warte %v vor erneutem Versuch...", retryDelay) + time.Sleep(retryDelay) + retryDelay = retryDelay * 2 // Exponential backoff + if retryDelay > 30*time.Second { + retryDelay = 30 * time.Second + } + continue + } + return fmt.Errorf("Redis nicht erreichbar nach %d Versuchen: %w", maxRetries, err) + } + break // Erfolgreich verbunden } GlobalLeaderboard = &Leaderboard{ @@ -38,7 +62,7 @@ func InitLeaderboard(redisAddr string) error { ctx: ctx, } - log.Println("📊 Redis-Leaderboard verbunden") + log.Println("✅ Redis-Leaderboard verbunden") return nil }