mirror of
https://github.com/Sagit-chu/flvx.git
synced 2026-10-10 19:36:36 +08:00
Merge pull request #17 from Sagit-chu/opencode/curious-nebula
fix(gost): add fallback when FailFilter excludes all nodes
This commit is contained in:
+19
-4
@@ -201,11 +201,26 @@ func (p *chainHop) Select(ctx context.Context, opts ...hop.SelectOption) *chain.
|
|||||||
return nodes[0]
|
return nodes[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Always go through selector for proper FailFilter evaluation,
|
// For single-node case: bypass selector/FailFilter to ensure availability.
|
||||||
// even when there's only one node. This ensures failed nodes
|
// The marker system still works for metrics, but we don't block requests
|
||||||
// can be filtered out properly.
|
// based on recent failures - the connection will be attempted regardless.
|
||||||
|
// This matches upstream go-gost/x behavior.
|
||||||
|
if len(nodes) == 1 {
|
||||||
|
return nodes[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Multi-node case: use selector with FailFilter for proper failover.
|
||||||
|
// FailFilter will exclude recently-failed nodes, allowing traffic to
|
||||||
|
// be routed to healthy alternatives.
|
||||||
if s := p.options.selector; s != nil {
|
if s := p.options.selector; s != nil {
|
||||||
return s.Select(ctx, nodes...)
|
if node := s.Select(ctx, nodes...); node != nil {
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
// All nodes filtered out by FailFilter - all are marked as failed.
|
||||||
|
// Return nil to signal "no healthy nodes available" to the caller.
|
||||||
|
// The handler's retry loop will handle this appropriately.
|
||||||
|
log.Debugf("all %d nodes filtered out by FailFilter, no healthy nodes available", len(nodes))
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback: return first node if no selector configured
|
// Fallback: return first node if no selector configured
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
# GO-GOST REGISTRY KNOWLEDGE BASE
|
||||||
|
|
||||||
|
**Generated:** Wed Feb 04 2026
|
||||||
|
|
||||||
|
## OVERVIEW
|
||||||
|
Central registration point for all pluggable GOST components (handlers, listeners, dialers, etc.).
|
||||||
|
Allows the configuration system to resolve string types (e.g., "socks5") to actual Go implementations.
|
||||||
|
|
||||||
|
## STRUCTURE
|
||||||
|
One file per component type, exporting a standard Registry interface.
|
||||||
|
```
|
||||||
|
go-gost/x/registry/
|
||||||
|
├── handler.go # RegisterHandler(name, newFunc)
|
||||||
|
├── listener.go # RegisterListener(name, newFunc)
|
||||||
|
├── dialer.go # RegisterDialer(name, newFunc)
|
||||||
|
└── ... # Same pattern for auth, bypass, admission
|
||||||
|
```
|
||||||
|
|
||||||
|
## WHERE TO LOOK
|
||||||
|
| Task | Location | Notes |
|
||||||
|
|------|----------|-------|
|
||||||
|
| Register a new component | `go-gost/x/registry/{type}.go` | Use `Register{Type}(name, creator)` |
|
||||||
|
| Component lookup | `go-gost/x/registry/{type}.go` | `Get{Type}(name)` returns the creator function |
|
||||||
|
| Default registrations | `go-gost/x/` (init functions) | Most components register themselves in their package `init()` |
|
||||||
|
|
||||||
|
## CONVENTIONS
|
||||||
|
- Thread-safe maps used for storage.
|
||||||
|
- Names are case-sensitive (usually lowercase).
|
||||||
|
- Components must be registered *before* the configuration parser runs (usually done via `import _ "..."` in `main.go`).
|
||||||
@@ -24,11 +24,12 @@ func FailFilter[T any](maxFails int, timeout time.Duration) selector.Filter[T] {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Filter filters dead objects.
|
// Filter filters dead objects.
|
||||||
// Note: We intentionally do NOT skip filtering when len(vs) <= 1.
|
// For single-node case, skip filtering to ensure availability (matches upstream).
|
||||||
// This ensures that even a single dead node gets filtered out,
|
// For multi-node case, filter out failed nodes to enable failover.
|
||||||
// allowing the caller to know that no healthy nodes are available
|
|
||||||
// and potentially trigger failover behavior.
|
|
||||||
func (f *failFilter[T]) Filter(ctx context.Context, vs ...T) []T {
|
func (f *failFilter[T]) Filter(ctx context.Context, vs ...T) []T {
|
||||||
|
if len(vs) <= 1 {
|
||||||
|
return vs
|
||||||
|
}
|
||||||
var l []T
|
var l []T
|
||||||
for _, v := range vs {
|
for _, v := range vs {
|
||||||
maxFails := f.maxFails
|
maxFails := f.maxFails
|
||||||
|
|||||||
Reference in New Issue
Block a user