diff --git a/go-gost/x/handler/forward/local/handler.go b/go-gost/x/handler/forward/local/handler.go index b1e47bf..20de1de 100644 --- a/go-gost/x/handler/forward/local/handler.go +++ b/go-gost/x/handler/forward/local/handler.go @@ -192,6 +192,8 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand var lastErr error var cc net.Conn + h.options.Logger.Debugf("[handler.retry] starting retry loop: maxRetries=%d", maxRetries) + for attempt := 0; attempt < maxRetries; attempt++ { // Select a target node, excluding previously tried nodes selectCtx := ctxvalue.ContextWithExcludeNodes(ctx, triedNodes) @@ -202,12 +204,15 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand ) } if target == nil { + h.options.Logger.Debugf("[handler.retry] attempt=%d target=nil, triedNodes=%v", attempt, triedNodes) if lastErr != nil { return lastErr } return errors.New("node not available") } + h.options.Logger.Debugf("[handler.retry] attempt=%d selected node=%s addr=%s", attempt, target.Name, target.Addr) + // Track this node as tried triedNodes = append(triedNodes, target.Addr) @@ -233,6 +238,8 @@ func (h *forwardHandler) Handle(ctx context.Context, conn net.Conn, opts ...hand // Mark node as failed for future selections if marker := target.Marker(); marker != nil { marker.Mark() + h.options.Logger.Debugf("[handler.retry] attempt=%d dial failed, marked node=%s count=%d err=%v", + attempt, target.Addr, marker.Count(), err) } lastErr = err // Try next node diff --git a/go-gost/x/hop/hop.go b/go-gost/x/hop/hop.go index 53cdcd3..f111409 100644 --- a/go-gost/x/hop/hop.go +++ b/go-gost/x/hop/hop.go @@ -149,6 +149,9 @@ func (p *chainHop) Select(ctx context.Context, opts ...hop.SelectOption) *chain. excludeSet[addr] = true } + // Debug logging for failover analysis + log.Debugf("[hop.Select] excludeNodes=%v, totalNodes=%d", excludeNodes, len(p.Nodes())) + var nodes []*chain.Node for _, node := range p.Nodes() { if node == nil { @@ -213,7 +216,9 @@ func (p *chainHop) Select(ctx context.Context, opts ...hop.SelectOption) *chain. // FailFilter will exclude recently-failed nodes, allowing traffic to // be routed to healthy alternatives. if s := p.options.selector; s != nil { + log.Debugf("[hop.Select] calling selector.Select with %d nodes", len(nodes)) if node := s.Select(ctx, nodes...); node != nil { + log.Debugf("[hop.Select] selected node=%s addr=%s", node.Name, node.Addr) return node } // All nodes filtered out by FailFilter - all are marked as failed. diff --git a/go-gost/x/selector/filter.go b/go-gost/x/selector/filter.go index c841ad6..8a7db3e 100644 --- a/go-gost/x/selector/filter.go +++ b/go-gost/x/selector/filter.go @@ -2,8 +2,10 @@ package selector import ( "context" + "fmt" "time" + "github.com/go-gost/core/chain" "github.com/go-gost/core/metadata" "github.com/go-gost/core/selector" mdutil "github.com/go-gost/x/metadata/util" @@ -53,8 +55,21 @@ func (f *failFilter[T]) Filter(ctx context.Context, vs ...T) []T { if mi, _ := any(v).(selector.Markable); mi != nil { if marker := mi.Marker(); marker != nil { - if marker.Count() < int64(maxFails) || - time.Since(marker.Time()) >= failTimeout { + count := marker.Count() + timeSince := time.Since(marker.Time()) + passed := count < int64(maxFails) || timeSince >= failTimeout + + // Debug logging for failover analysis + nodeName := "unknown" + nodeAddr := "unknown" + if node, ok := any(v).(*chain.Node); ok { + nodeName = node.Name + nodeAddr = node.Addr + } + fmt.Printf("[FailFilter] node=%s addr=%s count=%d maxFails=%d timeSince=%v failTimeout=%v passed=%v\n", + nodeName, nodeAddr, count, maxFails, timeSince, failTimeout, passed) + + if passed { l = append(l, v) } continue