func PostDegradations()

in pkg/degradation-detector/postDegradation.go [59:140]


func PostDegradations(client *http.Client, backendURL string, degradations <-chan DegradationWithSettings) chan InsertionResults {
	url := backendURL + "/api/meta/accidents"
	insertionResults := make(chan InsertionResults, 100)
	go func() {
		defer close(insertionResults)
		var wg sync.WaitGroup
		accidentStates := sync.Map{} // map[string]*accidentState

		for degradation := range degradations {
			wg.Go(func() {
				d := degradation.Details
				if d.timestamp < time.Now().Add(-672*time.Hour).UnixMilli() { // Do not post degradations older than 28 days
					return
				}

				accidentKey := fmt.Sprintf("%s:%s", degradation.Settings.DBTestName(), d.Build)
				stateInterface, _ := accidentStates.LoadOrStore(accidentKey, &accidentState{})
				state, ok := stateInterface.(*accidentState)
				if !ok {
					insertionResults <- InsertionResults{Error: errors.New("unexpected type in accidentStates map")}
					return
				}

				state.mu.Lock()
				defer state.mu.Unlock()

				if state.posted {
					if state.successfullyInserted {
						insertionResults <- InsertionResults{DegradationWithSettings{d, degradation.Settings}, nil}
					}
					return
				}

				state.posted = true

				date := time.UnixMilli(d.timestamp).UTC().Format("2006-01-02")
				medianMessage := getMessageBasedOnMedianChange(d.medianValues)
				kind := "InferredRegression"
				if !d.IsDegradation {
					kind = "InferredImprovement"
				}
				insertParams := meta.AccidentInsertParams{Date: date, Test: degradation.Settings.DBTestName(), Kind: kind, Reason: medianMessage, BuildNumber: d.Build, UserName: "R2D2"}
				params, err := json.Marshal(insertParams)
				if err != nil {
					insertionResults <- InsertionResults{Error: fmt.Errorf("failed to marshal query: %w", err)}
					return
				}
				ctx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
				defer cancel()
				req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewBuffer(params))
				if err != nil {
					insertionResults <- InsertionResults{Error: fmt.Errorf("failed to create request: %w", err)}
					return
				}
				req.Header.Set("Content-Type", "application/json")

				resp, err := client.Do(req)
				if err != nil {
					insertionResults <- InsertionResults{Error: fmt.Errorf("failed to send POST request: %w", err)}
					return
				}
				defer resp.Body.Close()

				if resp.StatusCode == http.StatusOK {
					state.successfullyInserted = true
					insertionResults <- InsertionResults{DegradationWithSettings{d, degradation.Settings}, nil}
					return
				}

				// the accident already exists
				if resp.StatusCode == http.StatusConflict {
					return
				}

				insertionResults <- InsertionResults{Error: fmt.Errorf("failed to post Details: %v", resp.Status)}
			})
		}
		wg.Wait()
	}()

	return insertionResults
}