Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initial backends dead #253

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions config/gobetween.toml
Expand Up @@ -199,6 +199,9 @@ protocol = "udp"
# fails = 5 # (optional) consecutive number of checks that should fail, to mark backend as inactive
# passes = 2 # (optional) consecutive number of checks that should pass, to mark backend as active
#
# # The liveness when a backend is first discovered (default live)
# initial_backend_status # (optional) "live" | "unhealthy"
#
# # -- ping -- #
# kind = "ping" # Unavailable if server.protocol is udp
#
Expand Down
4 changes: 4 additions & 0 deletions src/config/config.go
Expand Up @@ -307,6 +307,10 @@ type HealthcheckConfig struct {
Fails int `toml:"fails" json:"fails"`
Timeout string `toml:"timeout" json:"timeout"`

// The liveness when a backend is first discovered (default live)
// live | unhealthy
InitialBackendStatus *string `toml:"initial_backend_status" json:"initial_backend_status"`

/* Depends on Kind */

*PingHealthcheckConfig
Expand Down
6 changes: 3 additions & 3 deletions src/healthcheck/exec.go
Expand Up @@ -31,13 +31,13 @@ func exec(t core.Target, cfg config.HealthcheckConfig, result chan<- CheckResult
out, err := utils.ExecTimeout(execTimeout, cfg.ExecCommand, t.Host, t.Port)
if err != nil {
// TODO: Decide better what to do in this case
checkResult.Live = false
checkResult.Live = FailCheckResult
log.Warn(err)
} else {
if out == cfg.ExecExpectedPositiveOutput {
checkResult.Live = true
checkResult.Live = LiveCheckResult
} else if out == cfg.ExecExpectedNegativeOutput {
checkResult.Live = false
checkResult.Live = FailCheckResult
} else {
log.Warn("Unexpected output: ", out)
}
Expand Down
33 changes: 31 additions & 2 deletions src/healthcheck/healthcheck.go
Expand Up @@ -17,6 +17,14 @@ import (
*/
type CheckFunc func(core.Target, config.HealthcheckConfig, chan<- CheckResult)

type CheckResultLiveness int32

const (
InitialCheckResult CheckResultLiveness = iota
FailCheckResult
LiveCheckResult
)

/**
* Check result
* Handles target and it's live status
Expand All @@ -27,7 +35,7 @@ type CheckResult struct {
Target core.Target

/* Check live status */
Live bool
Live CheckResultLiveness
}

/**
Expand Down Expand Up @@ -147,7 +155,7 @@ func (this *Healthcheck) UpdateWorkers(targets []core.Target) {
cfg: this.cfg,
check: this.check,
LastResult: CheckResult{
Live: true,
Live: InitialCheckResult,
},
}
keep.Start()
Expand Down Expand Up @@ -175,6 +183,27 @@ func (this *Healthcheck) UpdateWorkers(targets []core.Target) {

}

func (this *Healthcheck) HasCheck() bool {
return this.cfg.Kind != "none"
}

func (this *Healthcheck) InitialBackendState() CheckResultLiveness {
if !this.HasCheck() {
return LiveCheckResult
}
if this.cfg.InitialBackendStatus != nil {
switch *this.cfg.InitialBackendStatus {
case "unhealthy":
return FailCheckResult
case "live":
return LiveCheckResult
default:
panic("Healthcheck invalid initial backend status, this should have been validated in manager, but has invalid value " + *this.cfg.InitialBackendStatus)
}
}
return LiveCheckResult
}

/**
* Stop healthcheck
*/
Expand Down
4 changes: 2 additions & 2 deletions src/healthcheck/ping.go
Expand Up @@ -30,9 +30,9 @@ func ping(t core.Target, cfg config.HealthcheckConfig, result chan<- CheckResult

conn, err := net.DialTimeout("tcp", t.Address(), pingTimeoutDuration)
if err != nil {
checkResult.Live = false
checkResult.Live = FailCheckResult
} else {
checkResult.Live = true
checkResult.Live = LiveCheckResult
conn.Close()
}

Expand Down
6 changes: 3 additions & 3 deletions src/healthcheck/probe.go
Expand Up @@ -26,7 +26,7 @@ func probe(t core.Target, cfg config.HealthcheckConfig, result chan<- CheckResul
timeout, _ := time.ParseDuration(cfg.Timeout)

checkResult := CheckResult{
Live: false,
Live: FailCheckResult,
Target: t,
}

Expand All @@ -50,7 +50,7 @@ func probe(t core.Target, cfg config.HealthcheckConfig, result chan<- CheckResul
conn, err = net.DialTimeout(cfg.ProbeProtocol, t.Address(), timeout)
}
if err != nil {
checkResult.Live = false
checkResult.Live = FailCheckResult
return
}

Expand Down Expand Up @@ -115,5 +115,5 @@ func probe(t core.Target, cfg config.HealthcheckConfig, result chan<- CheckResul
panic("probe_strategy should be checked in manager")
}

checkResult.Live = true
checkResult.Live = LiveCheckResult
}
12 changes: 7 additions & 5 deletions src/healthcheck/worker.go
Expand Up @@ -99,15 +99,17 @@ func (this *Worker) process(checkResult CheckResult) {

log := logging.For("healthcheck/worker")

if this.LastResult.Live && !checkResult.Live {
if checkResult.Live == this.LastResult.Live {
// check status not changed
return
}

if checkResult.Live == FailCheckResult {
this.passes = 0
this.fails++
} else if !this.LastResult.Live && checkResult.Live {
} else if checkResult.Live == LiveCheckResult {
this.fails = 0
this.passes++
} else {
// check status not changed
return
}

if this.passes == 0 && this.fails >= this.cfg.Fails ||
Expand Down
8 changes: 8 additions & 0 deletions src/manager/manager.go
Expand Up @@ -313,6 +313,14 @@ func prepareConfig(name string, server config.Server, defaults config.Connection
}
}

if server.Healthcheck.InitialBackendStatus != nil {
switch *server.Healthcheck.InitialBackendStatus {
case "live", "unhealthy":
default:
return config.Server{}, errors.New("Unsupported initial_backend_status")
}
}

if server.Healthcheck.Kind == "probe" {

switch server.Healthcheck.ProbeProtocol {
Expand Down
4 changes: 3 additions & 1 deletion src/server/scheduler/scheduler.go
Expand Up @@ -126,7 +126,7 @@ func (this *Scheduler) Start() {

// handle backend healthcheck result
case checkResult := <-this.Healthcheck.Out:
this.HandleBackendLiveChange(checkResult.Target, checkResult.Live)
this.HandleBackendLiveChange(checkResult.Target, checkResult.Live == healthcheck.LiveCheckResult)

/* ----- stats ----- */

Expand Down Expand Up @@ -248,6 +248,8 @@ func (this *Scheduler) HandleBackendsUpdate(backends []core.Backend) {
b := b // b has to be local variable in order to make unique pointers
b.Stats.Discovered = true
this.backends[b.Target] = &b

b.Stats.Live = this.Healthcheck.InitialBackendState() == healthcheck.LiveCheckResult
}

//remove not discovered backends without active connections
Expand Down