# HG changeset patch # User Sascha L. Teichmann # Date 1558625294 -7200 # Node ID 6994602d2935e11075ac34d73fd35ce7857ca919 # Parent 7bbab09cdf71784a3b8845d10f7999cf48ab7711 fairway availibilty: Implemented for streches and sections. diff -r 7bbab09cdf71 -r 6994602d2935 pkg/controllers/bottlenecks.go --- a/pkg/controllers/bottlenecks.go Thu May 23 16:49:28 2019 +0200 +++ b/pkg/controllers/bottlenecks.go Thu May 23 17:28:14 2019 +0200 @@ -247,9 +247,9 @@ return b } -func durationsToPercentage(from, to time.Time, classes []time.Duration) []float64 { +func durationsToPercentage(duration time.Duration, classes []time.Duration) []float64 { percents := make([]float64, len(classes)) - total := 100 / to.Sub(from).Seconds() + total := 100 / duration.Seconds() for i, v := range classes { percents[i] = v.Seconds() * total } @@ -324,7 +324,6 @@ conn *sql.Conn, bottleneck string, ) ([]float64, error) { - var value float64 err := conn.QueryRowContext(ctx, selectGaugeLDCSQL, bottleneck).Scan(&value) switch { @@ -333,7 +332,6 @@ case err != nil: return nil, err } - log.Printf("info: LDC = %.2f\n", value) return []float64{value}, nil } @@ -367,7 +365,12 @@ req *http.Request, conn *sql.Conn, ) (jr JSONResult, err error) { + bn := mux.Vars(req)["objnam"] + var from, to time.Time + var los int + var breaks []float64 + var ldcRefs []float64 if bn == "" { err = JSONError{ @@ -377,8 +380,6 @@ return } - var from, to time.Time - if f := req.FormValue("from"); f != "" { if from, err = parseTime(f, "from"); err != nil { return @@ -399,9 +400,6 @@ to, from = from, to } - log.Printf("info: time interval: (%v - %v)\n", from, to) - - var los int if l := req.FormValue("los"); l != "" { if los, err = parseInt(l, "los"); err != nil { return @@ -410,7 +408,6 @@ los = 1 } - var breaks []float64 if b := req.FormValue("breaks"); b != "" { breaks = breaksToReferenceValue(b) } else { @@ -419,7 +416,6 @@ ctx := req.Context() - var ldcRefs []float64 if ldcRefs, err = loadLDCReferenceValue(ctx, conn, bn); err != nil { return } @@ -432,6 +428,8 @@ return } + log.Printf("info: time interval: (%v - %v)\n", from, to) + var ms availMeasurements if ms, err = loadDepthValues(ctx, conn, bn, los, from, to); err != nil { return @@ -457,8 +455,9 @@ (*availMeasurement).getDepth, ) - lnwlPercents := durationsToPercentage(from, to, lnwl) - afdPercents := durationsToPercentage(from, to, afd) + duration := to.Sub(from) + lnwlPercents := durationsToPercentage(duration, lnwl) + afdPercents := durationsToPercentage(duration, afd) type ldcOutput struct { Value float64 `json:"value"` diff -r 7bbab09cdf71 -r 6994602d2935 pkg/controllers/stretches.go --- a/pkg/controllers/stretches.go Thu May 23 16:49:28 2019 +0200 +++ b/pkg/controllers/stretches.go Thu May 23 17:28:14 2019 +0200 @@ -260,6 +260,7 @@ depthbreaks, widthbreaks, ) if err != nil { + log.Printf("error: %v\n", err) errors = append(errors, err) continue } @@ -445,6 +446,224 @@ req *http.Request, conn *sql.Conn, ) (jr JSONResult, err error) { - // TODO: Implement me! + + vars := mux.Vars(req) + stretch := vars["kind"] == "stretch" + name := vars["name"] + + var from, to time.Time + var los int + + depthbreaks, widthbreaks := afdRefs, afdRefs + + if f := req.FormValue("from"); f != "" { + if from, err = parseTime(f, "from"); err != nil { + return + } + } else { + from = time.Now().AddDate(-1, 0, 0).UTC() + } + + if t := req.FormValue("to"); t != "" { + if to, err = parseTime(t, "to"); err != nil { + return + } + } else { + to = from.AddDate(1, 0, 0).UTC() + } + + if to.Before(from) { + to, from = from, to + } + + if l := req.FormValue("los"); l != "" { + if los, err = parseInt(l, "los"); err != nil { + return + } + } else { + los = 1 + } + + if b := req.FormValue("depthbreaks"); b != "" { + depthbreaks = breaksToReferenceValue(b) + } + + if b := req.FormValue("widthbreaks"); b != "" { + widthbreaks = breaksToReferenceValue(b) + } + + ctx := req.Context() + + var bns stretchBottlenecks + if bns, err = loadStretchBottlenecks(ctx, conn, stretch, name); err != nil { + return + } + + if len(bns) == 0 { + err = JSONError{ + Code: http.StatusNotFound, + Message: "No bottlenecks found.", + } + return + } + + useDepth, useWidth := bns.contains("depth"), bns.contains("width") + + if useDepth && useWidth && len(widthbreaks) != len(depthbreaks) { + err = JSONError{ + Code: http.StatusBadRequest, + Message: fmt.Sprintf("class breaks lengths differ: %d != %d", + len(widthbreaks), len(depthbreaks)), + } + return + } + + log.Printf("info: time interval: (%v - %v)\n", from, to) + + var loaded []*fullStretchBottleneck + var errors []error + + for i := range bns { + l, err := loadFullStretchBottleneck( + ctx, + conn, + &bns[i], + los, + from, to, + depthbreaks, widthbreaks, + ) + if err != nil { + log.Printf("error: %v\n", err) + errors = append(errors, err) + continue + } + loaded = append(loaded, l) + } + + if len(loaded) == 0 { + err = JSONError{ + Code: http.StatusInternalServerError, + Message: fmt.Sprintf("No bottleneck loaded: %v", joinErrors(errors)), + } + return + } + + type calculation struct { + ldc, afd []time.Duration + } + + n := runtime.NumCPU() / 2 + if n == 0 { + n = 1 + } + + var breaks []float64 + if useDepth { + breaks = depthbreaks + } else { + breaks = widthbreaks + } + + jobCh := make(chan *fullStretchBottleneck) + calcCh := make(chan calculation, n) + done := make(chan struct{}) + + ldc := make([]time.Duration, 2) + afd := make([]time.Duration, len(breaks)+1) + + var wg sync.WaitGroup + + go func() { + defer close(done) + for calc := range calcCh { + for i, v := range calc.ldc { + ldc[i] += v + } + for i, v := range calc.afd { + afd[i] += v + } + } + }() + + for i := 0; i < n; i++ { + wg.Add(1) + go func() { + defer wg.Done() + for bn := range jobCh { + ldc := bn.measurements.classify( + from, to, + bn.ldc, + (*availMeasurement).getValue, + ) + + afd := bn.measurements.classify( + from, to, + bn.breaks, + bn.access, + ) + calcCh <- calculation{ldc: ldc, afd: afd} + } + }() + } + + for _, bn := range loaded { + jobCh <- bn + } + + close(jobCh) + wg.Wait() + close(calcCh) + <-done + + duration := to.Sub(from) * time.Duration(len(loaded)) + + lnwlPercents := durationsToPercentage(duration, ldc) + afdPercents := durationsToPercentage(duration, afd) + + type ldcOutput struct { + Value float64 `json:"value"` + Below float64 `json:"below"` + Above float64 `json:"above"` + } + + type intervalOutput struct { + From *float64 `json:"from,omitempty"` + To *float64 `json:"to,omitempty"` + Percent float64 `json:"percent"` + } + + type output struct { + LDC []intervalOutput `json:"ldc"` + AFD []intervalOutput `json:"afd"` + } + + out := output{ + LDC: []intervalOutput{ + {To: new(float64), Percent: lnwlPercents[0]}, + {From: new(float64), Percent: lnwlPercents[1]}, + }, + } + + // XXX: In mixed mode the breaks are not correct! + + for i, percent := range afdPercents { + var from, to *float64 + switch { + case i == 0: + to = &breaks[i] + case i == len(afdPercents)-1: + from = &breaks[len(breaks)-1] + default: + from = &breaks[i-1] + to = &breaks[i] + } + out.AFD = append(out.AFD, intervalOutput{ + From: from, + To: to, + Percent: percent, + }) + } + + jr = JSONResult{Result: &out} return }