94 lines
2.7 KiB
Go
94 lines
2.7 KiB
Go
package proxy
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"git.wntrmute.dev/kyle/crossbar/internal/choose"
|
|
"git.wntrmute.dev/kyle/crossbar/internal/config"
|
|
"git.wntrmute.dev/kyle/crossbar/internal/health"
|
|
"git.wntrmute.dev/kyle/crossbar/internal/lease"
|
|
"git.wntrmute.dev/kyle/crossbar/internal/limiter"
|
|
)
|
|
|
|
// Hosts adapts the health table and config for the lease table, and holds the operator's drain set.
|
|
// The lease table filters candidates by Healthy && !Draining: Healthy is the health table's view of
|
|
// a host, Draining is an operator flag that keeps new leases off a host being taken out of service.
|
|
type Hosts struct {
|
|
health *health.Table
|
|
drain map[string]bool
|
|
mu sync.Mutex
|
|
}
|
|
|
|
// HostView builds the lease-table view of the health table and config.
|
|
func HostView(h *health.Table, cfg *config.Config) *Hosts {
|
|
return &Hosts{health: h, drain: make(map[string]bool)}
|
|
}
|
|
|
|
// Healthy reports whether the health table says the host answered its last poll; an unknown host is
|
|
// not healthy.
|
|
func (h *Hosts) Healthy(name string) bool {
|
|
s, ok := h.health.Get(name)
|
|
if !ok {
|
|
return false
|
|
}
|
|
return s.Healthy
|
|
}
|
|
|
|
// Draining reports whether an operator is draining the host.
|
|
func (h *Hosts) Draining(name string) bool {
|
|
h.mu.Lock()
|
|
defer h.mu.Unlock()
|
|
return h.drain[name]
|
|
}
|
|
|
|
// SetDraining turns draining on or off for a host.
|
|
func (h *Hosts) SetDraining(name string, on bool) {
|
|
h.mu.Lock()
|
|
defer h.mu.Unlock()
|
|
if on {
|
|
h.drain[name] = true
|
|
return
|
|
}
|
|
delete(h.drain, name)
|
|
}
|
|
|
|
// hostChooser adapts config, health and limiter to lease.Chooser via choose.Best.
|
|
type hostChooser struct {
|
|
cfg *config.Config
|
|
health *health.Table
|
|
lim *limiter.Limiter
|
|
}
|
|
|
|
// Chooser adapts config, health and limiter to lease.Chooser using choose.Best: among the candidates
|
|
// it prefers the hosts that have the model loaded over those that can only serve it, then the one
|
|
// with the most free slots times weight, breaking ties by shortest queue. Draining is reported false
|
|
// because the lease table already filtered draining hosts out before calling Choose.
|
|
func Chooser(cfg *config.Config, h *health.Table, l *limiter.Limiter) lease.Chooser {
|
|
return &hostChooser{cfg: cfg, health: h, lim: l}
|
|
}
|
|
|
|
func (c *hostChooser) Choose(candidates []string, model string) (string, bool) {
|
|
return choose.Best(candidates, func(host string) (choose.Info, bool) {
|
|
s, ok := c.health.Get(host)
|
|
info := choose.Info{
|
|
Healthy: ok && s.Healthy,
|
|
Draining: false,
|
|
Loaded: contains(s.Loaded, model),
|
|
CanServe: c.cfg.Serves(host, model),
|
|
Free: c.lim.FreeSlots(host),
|
|
Queued: c.lim.Queued(host, model),
|
|
Weight: c.cfg.Hosts[host].Weight,
|
|
}
|
|
return info, ok
|
|
})
|
|
}
|
|
|
|
func contains(list []string, v string) bool {
|
|
for _, s := range list {
|
|
if s == v {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|