refactor: simplify HA backend peer handling
- parsePeer: use net.SplitHostPort (handles IPv6, stdlib convention) - tryConnect: take Go strings, own CGO allocation internally - RegisterToBackend: remove unreachable 'no HA candidates' error path - Sync Peers field comment between utils and backend config structs
This commit is contained in:
parent
99d13f5254
commit
da6d967a33
3 changed files with 26 additions and 36 deletions
|
|
@ -76,11 +76,15 @@ var idCtxOnce sync.Once
|
|||
// Must be set via StartNotificationListener before RegisterToBackend.
|
||||
var gHandler NotificationHandler
|
||||
|
||||
// tryConnect attempts to connect to a single controller candidate.
|
||||
// Returns nil on success. The C strings passed in are owned by the caller.
|
||||
func tryConnect(host string, port int, cFrom, cKey *C.char) error {
|
||||
func tryConnect(host string, port int, from, key string) error {
|
||||
cAddr := C.CString(host)
|
||||
defer C.free(unsafe.Pointer(cAddr))
|
||||
cFrom := C.CString(from)
|
||||
cKey := C.CString(key)
|
||||
defer func() {
|
||||
C.free(unsafe.Pointer(cAddr))
|
||||
C.free(unsafe.Pointer(cFrom))
|
||||
C.free(unsafe.Pointer(cKey))
|
||||
}()
|
||||
|
||||
var cConfig C.xce_client_config_t
|
||||
cConfig.nmasters = 1
|
||||
|
|
@ -99,10 +103,6 @@ func tryConnect(host string, port int, cFrom, cKey *C.char) error {
|
|||
// RegisterToBackend initialises the XCE client library and joins as an API
|
||||
// server. StartNotificationListener should be called first so that push
|
||||
// events have a handler ready before the first push can arrive.
|
||||
//
|
||||
// When config.Peers is non-empty the function iterates each "host:port" entry
|
||||
// in order and returns as soon as one succeeds (HA candidate fallback).
|
||||
// When Peers is empty it connects to config.Address:config.Port directly.
|
||||
func RegisterToBackend(config *BackendConfig) error {
|
||||
from := config.From
|
||||
if from == "" {
|
||||
|
|
@ -113,14 +113,6 @@ func RegisterToBackend(config *BackendConfig) error {
|
|||
key = "keepsecret$"
|
||||
}
|
||||
|
||||
cFrom := C.CString(from)
|
||||
cKey := C.CString(key)
|
||||
defer func() {
|
||||
C.free(unsafe.Pointer(cFrom))
|
||||
C.free(unsafe.Pointer(cKey))
|
||||
}()
|
||||
|
||||
// HA mode: iterate the peer list and connect to the first responsive primary.
|
||||
if len(config.Peers) > 0 {
|
||||
var lastErr error
|
||||
for _, peer := range config.Peers {
|
||||
|
|
@ -130,7 +122,7 @@ func RegisterToBackend(config *BackendConfig) error {
|
|||
lastErr = err
|
||||
continue
|
||||
}
|
||||
if err := tryConnect(host, port, cFrom, cKey); err != nil {
|
||||
if err := tryConnect(host, port, from, key); err != nil {
|
||||
logger.Warn("Candidate unreachable, trying next",
|
||||
zap.String("host", host), zap.Int("port", port), zap.Error(err))
|
||||
lastErr = err
|
||||
|
|
@ -140,14 +132,10 @@ func RegisterToBackend(config *BackendConfig) error {
|
|||
zap.String("host", host), zap.Int("port", port))
|
||||
return nil
|
||||
}
|
||||
if lastErr != nil {
|
||||
return fmt.Errorf("all HA candidates failed: %w", lastErr)
|
||||
}
|
||||
return fmt.Errorf("no HA candidates configured")
|
||||
return fmt.Errorf("all HA candidates failed: %w", lastErr)
|
||||
}
|
||||
|
||||
// Single-host mode (no peers configured).
|
||||
if err := tryConnect(config.Address, config.Port, cFrom, cKey); err != nil {
|
||||
if err := tryConnect(config.Address, config.Port, from, key); err != nil {
|
||||
return err
|
||||
}
|
||||
logger.Info("Backend connection established",
|
||||
|
|
|
|||
|
|
@ -2,21 +2,23 @@ package backend
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// parsePeer splits a "host:port" string into host and port components.
|
||||
// Returns an error if the format is invalid or the port is out of range.
|
||||
// parsePeer splits a "host:port" string. Uses net.SplitHostPort so IPv6
|
||||
// bracket notation (e.g. "[::1]:9990") is handled correctly.
|
||||
func parsePeer(peer string) (host string, port int, err error) {
|
||||
idx := strings.LastIndex(peer, ":")
|
||||
if idx <= 0 {
|
||||
return "", 0, fmt.Errorf("invalid peer %q: expected host:port", peer)
|
||||
h, portStr, err := net.SplitHostPort(peer)
|
||||
if err != nil {
|
||||
return "", 0, fmt.Errorf("invalid peer %q: %w", peer, err)
|
||||
}
|
||||
host = peer[:idx]
|
||||
port, err = strconv.Atoi(peer[idx+1:])
|
||||
if h == "" {
|
||||
return "", 0, fmt.Errorf("invalid peer %q: empty host", peer)
|
||||
}
|
||||
port, err = strconv.Atoi(portStr)
|
||||
if err != nil || port <= 0 || port > 65535 {
|
||||
return "", 0, fmt.Errorf("invalid port in peer %q", peer)
|
||||
}
|
||||
return host, port, nil
|
||||
return h, port, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,10 +18,10 @@ type BackendConfig struct {
|
|||
From string `yaml:"from"`
|
||||
Key string `yaml:"key"`
|
||||
// Peers lists additional controller candidates for HA mode in "host:port"
|
||||
// format. When non-empty, RegisterToBackend iterates all candidates and
|
||||
// connects to whichever responds as primary. Address+Port remain the
|
||||
// single-host fallback when Peers is empty.
|
||||
Peers []string `yaml:"peers"`
|
||||
// format. When non-empty, RegisterToBackend tries each candidate in order
|
||||
// and connects to whichever accepts the join as primary. Address+Port
|
||||
// serve as a single-host fallback when Peers is empty.
|
||||
Peers []string `yaml:"peers"`
|
||||
}
|
||||
|
||||
type AppServerConfig struct {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue