github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/pkg/checker/real_checker.go (about)

     1  // Copyright 2021 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package checker
    15  
    16  import (
    17  	"context"
    18  	"fmt"
    19  	"sync"
    20  )
    21  
    22  // RealChecker is interface that defines RealChecker to check configurations of system.
    23  // It is mainly used for configuration checking of data synchronization between database systems.
    24  type RealChecker interface {
    25  	Name() string
    26  	Check(ctx context.Context) *Result
    27  }
    28  
    29  // State is state of check.
    30  type State string
    31  
    32  const (
    33  	// StateSuccess indicates that the check was successful.
    34  	StateSuccess State = "success"
    35  	// StateFailure indicates that the check was failed.
    36  	StateFailure State = "fail"
    37  	// StateWarning indicates that the check had warnings.
    38  	StateWarning State = "warn"
    39  )
    40  
    41  type Error struct {
    42  	Severity    State  `json:"severity"`
    43  	ShortErr    string `json:"short_error"`
    44  	Self        string `json:"self,omitempty"`
    45  	Other       string `json:"other,omitempty"`
    46  	Instruction string `json:"instruction,omitempty"`
    47  }
    48  
    49  // NewError creates a pointer to Error, the parameters could be used as in Sprintf.
    50  func NewError(description string, args ...interface{}) *Error {
    51  	return &Error{Severity: StateFailure, ShortErr: fmt.Sprintf(description, args...)}
    52  }
    53  
    54  // NewWarn creates a pointer to Error, the parameters could be used as in Sprintf.
    55  func NewWarn(description string, args ...interface{}) *Error {
    56  	return &Error{Severity: StateWarning, ShortErr: fmt.Sprintf(description, args...)}
    57  }
    58  
    59  // Result is result of check.
    60  type Result struct {
    61  	ID          uint64   `json:"id"`
    62  	Name        string   `json:"name"`
    63  	Desc        string   `json:"desc"`
    64  	State       State    `json:"state"`
    65  	Errors      []*Error `json:"errors,omitempty"`
    66  	Instruction string   `json:"instruction,omitempty"`
    67  	Extra       string   `json:"extra,omitempty"`
    68  }
    69  
    70  // ResultSummary is summary of all check results.
    71  type ResultSummary struct {
    72  	Passed     bool  `json:"passed"`
    73  	Total      int64 `json:"total"`
    74  	Successful int64 `json:"successful"`
    75  	Failed     int64 `json:"failed"`
    76  	Warning    int64 `json:"warning"`
    77  }
    78  
    79  // Results contains all check results and summary.
    80  type Results struct {
    81  	Results []*Result      `json:"results"`
    82  	Summary *ResultSummary `json:"summary"`
    83  }
    84  
    85  // Do executes several RealCheckers.
    86  func Do(ctx context.Context, checkers []RealChecker) (*Results, error) {
    87  	results := &Results{
    88  		Results: make([]*Result, 0, len(checkers)),
    89  	}
    90  	if len(checkers) == 0 {
    91  		results.Summary = &ResultSummary{Passed: true}
    92  		return results, nil
    93  	}
    94  
    95  	var (
    96  		wg         sync.WaitGroup
    97  		finished   bool
    98  		total      int64
    99  		successful int64
   100  		failed     int64
   101  		warning    int64
   102  	)
   103  	total = int64(len(checkers))
   104  
   105  	resultCh := make(chan *Result)
   106  
   107  	wg.Add(1)
   108  	go func() {
   109  		defer wg.Done()
   110  		for {
   111  			result := <-resultCh
   112  			switch result.State {
   113  			case StateSuccess:
   114  				successful++
   115  			case StateFailure:
   116  				failed++
   117  			case StateWarning:
   118  				warning++
   119  			}
   120  
   121  			// if total == successful + warning + failed, it's finished
   122  			finished = total == successful+warning+failed
   123  			results.Results = append(results.Results, result)
   124  
   125  			if finished {
   126  				return
   127  			}
   128  		}
   129  	}()
   130  
   131  	for i, checker := range checkers {
   132  		wg.Add(1)
   133  		go func(i int, checker RealChecker) {
   134  			defer wg.Done()
   135  			result := checker.Check(ctx)
   136  			result.ID = uint64(i)
   137  			resultCh <- result
   138  		}(i, checker)
   139  	}
   140  	wg.Wait()
   141  
   142  	passed := finished && (failed == 0)
   143  	results.Summary = &ResultSummary{
   144  		Passed:     passed,
   145  		Total:      total,
   146  		Successful: successful,
   147  		Failed:     failed,
   148  		Warning:    warning,
   149  	}
   150  
   151  	return results, nil
   152  }